From b859f7b747e6d63ab1f725cbf62b56d56645e9da Mon Sep 17 00:00:00 2001 From: Zach Wasserman Date: Mon, 21 Jun 2021 16:10:24 -0700 Subject: [PATCH] Sort labels for membership update (#1156) This may help with deadlocks on the `label_membership` table. It is not clear from MySQL documentation whether the order of the records is significant for locking within a single query. If it is, this should help the problem. If it is not, this should have no negative impact. May fix #1146 --- server/datastore/mysql/labels.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go index a93f606832..2ae509ae9b 100644 --- a/server/datastore/mysql/labels.go +++ b/server/datastore/mysql/labels.go @@ -3,6 +3,7 @@ package mysql import ( "database/sql" "fmt" + "sort" "strings" "time" @@ -318,13 +319,21 @@ func (d *Datastore) LabelQueriesForHost(host *fleet.Host, cutoff time.Time) (map } func (d *Datastore) RecordLabelQueryExecutions(host *fleet.Host, results map[uint]bool, updated time.Time) error { + // Sort the results to have generated SQL queries ordered to minimize + // deadlocks. See https://github.com/fleetdm/fleet/issues/1146. + orderedIDs := make([]uint, 0, len(results)) + for labelID, _ := range results { + orderedIDs = append(orderedIDs, labelID) + } + sort.Slice(orderedIDs, func(i, j int) bool { return orderedIDs[i] < orderedIDs[j] }) + // Loop through results, collecting which labels we need to insert/update, // and which we need to delete vals := []interface{}{} bindvars := []string{} removes := []uint{} - - for labelID, matches := range results { + for _, labelID := range orderedIDs { + matches := results[labelID] if matches { // Add/update row bindvars = append(bindvars, "(?,?,?)")