Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally delete commands in mysql storage backend #48

Merged
merged 3 commits into from
Jun 13, 2022

Conversation

jessepeterson
Copy link
Member

From the changes to the operations guide:

This option turns on the command and response deleter. When enabled (with delete=1) command responses, queued commands, and commands themeselves will be deleted from the database after enrollments have responded to a command.

This implements the feature discussed in #43 for the mysql storage backend.

storage/mysql/queue.go Outdated Show resolved Hide resolved
storage/mysql/queue.go Outdated Show resolved Hide resolved
Copy link

@kilpatds kilpatds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No non-nit feedback here. Look forward to pulling this in.

@sheshenia sheshenia mentioned this pull request Jun 7, 2022
Comment on lines +72 to +83
DELETE
c
FROM
commands AS c
LEFT JOIN enrollment_queue AS q
ON q.command_uuid = c.command_uuid
LEFT JOIN command_results AS r
ON r.command_uuid = c.command_uuid
WHERE
c.command_uuid = ? AND
q.command_uuid IS NULL AND
r.command_uuid IS NULL;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kilpatds @4e554c4c would you mind just confirming that my thinking is correct on this delete statement? I adjusted it slightly and add another JOIN. The intent is along the lines of "delete the command row if there are no command responses nor enqueue-ings associated with it". Thanks!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Delete from 'commands' where the command_uuid matches the provided one, and there is no matching record in the enrollment queue, and there is no matching record in the results table."

After you have a delete that translates to:
"Delete from the enrollment queue and the results table where the id in the queue matches the provided value and the command uuid matches the provided value. It's ok if there is nothing in the results table"

So, combined:
"For command_uuid=?, After I delete everything associated with id=? clean up the commands entry if it refers to nothing".

And we're relying on command_uuid being unique. Seems good?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried on the multi table delete used here again, due to the intricacies of its failure/ordering.
when you could always do some classic SQL command like

DELETE  c
FROM commands
WHERE NOT EXISTS (SELECT * from enrollment_queue WHERE command_uuid = c.command_uuid)
AND   NOT EXISTS (SELECT * from command_results  WHERE command_uuid = c.command_uuid)

but since we're only deleting one row here it probably won't matter :)

Comment on lines +72 to +83
DELETE
c
FROM
commands AS c
LEFT JOIN enrollment_queue AS q
ON q.command_uuid = c.command_uuid
LEFT JOIN command_results AS r
ON r.command_uuid = c.command_uuid
WHERE
c.command_uuid = ? AND
q.command_uuid IS NULL AND
r.command_uuid IS NULL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried on the multi table delete used here again, due to the intricacies of its failure/ordering.
when you could always do some classic SQL command like

DELETE  c
FROM commands
WHERE NOT EXISTS (SELECT * from enrollment_queue WHERE command_uuid = c.command_uuid)
AND   NOT EXISTS (SELECT * from command_results  WHERE command_uuid = c.command_uuid)

but since we're only deleting one row here it probably won't matter :)

}
return tx.Commit()
}

func (s *MySQLStorage) StoreCommandReport(r *mdm.Request, result *mdm.CommandResults) error {

This comment was marked as resolved.

This comment was marked as resolved.

@jessepeterson jessepeterson merged commit e7fe368 into micromdm:main Jun 13, 2022
@jessepeterson jessepeterson deleted the mysql-command-delete branch June 13, 2022 21:40
4e554c4c pushed a commit to 4e554c4c/nanomdm that referenced this pull request Dec 16, 2022
NanoMDM allows for a single command to be queued for multiple devices.
Since micromdm#48 landed, both queued commands and the command data itself is
deleted after a device in finished with a command.
However, I have observed deadlocks which occur when multiple devices
trigger deletion for a shared command at once.
This can be avoided by placing a record lock on the command row before
attempting a delete, so that only one device's enrollment queue can be
cleared at a time.

This should have minimal impacts on performance as it will only
serialize deletes to a single command. Furthermore, as the lock on the
command record must be taken anyway, this does not add any additional
locking to the transaction.

Testing:
I have tested this, and have confirmed that it prevents deadlocks in
NanoMDM. Furthermore, I have tested that commands are still delivered
and responded to.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants