Improved the performance of Windows MDM profile reconciliation - #44075
Conversation
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
|
/agentic_review |
Code Review by Qodo
1. NewActivity nil DB panic
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis pull request introduces cursor-based batching for Windows MDM profile reconciliation to improve scalability during large team transfers and bulk profile changes. The changes refactor Windows profile reconciliation from synchronous transactional behavior to deferred asynchronous processing via a global cron job. New datastore methods enable cursor-driven pagination through pending host UUIDs in configurable batch sizes, scoped install/remove profile listing by host, and concurrent-deletion safety through profile UUID validation. The reconciliation service now reads a persisted cursor, processes a bounded batch of hosts, and advances the cursor only on successful completion. Tests validate cursor state machine behavior, profile deletion handling, and end-to-end reconciliation flow using both eager (synchronous test-only) and deferred (production) paths. Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
server/datastore/mysql/microsoft_mdm.go (1)
3194-3230: LGTM —currentBatchcorrectly preserves iteration order for the pre-read SELECT.Tracking the batch in a slice (instead of relying on map iteration) gives deterministic SQL text for the
(host_uuid, profile_uuid) IN (...)pre-read, consistent with the new PK-aligned ordering elsewhere in this function. The SELECT tuple order andselectArgsappending (p.HostUUID, p.ProfileUUID) are aligned.Minor nit (optional):
profilesToInsert's map key is built asProfileUUID\nHostUUIDwhile the SELECT/UPDATE tuples are(host_uuid, profile_uuid). Functionally fine (the key only needs uniqueness), but flipping the key to match would make the code easier to follow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/datastore/mysql/microsoft_mdm.go` around lines 3194 - 3230, The map key for profilesToInsert is currently built as "ProfileUUID\nHostUUID", which mismatches the tuple ordering used elsewhere (host_uuid, profile_uuid); change the key construction to "HostUUID\nProfileUUID" wherever profilesToInsert is populated so the logical ordering matches currentBatch, the pre-read SELECT tuple order, and the upsert/update logic in executeUpsertBatch; update any comments or variable usage that assume the old ordering to keep the code easier to follow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@server/datastore/mysql/microsoft_mdm.go`:
- Around line 3194-3230: The map key for profilesToInsert is currently built as
"ProfileUUID\nHostUUID", which mismatches the tuple ordering used elsewhere
(host_uuid, profile_uuid); change the key construction to
"HostUUID\nProfileUUID" wherever profilesToInsert is populated so the logical
ordering matches currentBatch, the pre-read SELECT tuple order, and the
upsert/update logic in executeUpsertBatch; update any comments or variable usage
that assume the old ordering to keep the code easier to follow.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4404989b-e722-4913-a0e0-8d7010dd9e1d
📒 Files selected for processing (2)
changes/42545-windows-profile-reconciliation-batchingserver/datastore/mysql/microsoft_mdm.go
There was a problem hiding this comment.
Pull request overview
This PR improves scalability of the Windows MDM “desired state” reconciliation path by making batched updates more efficient and less deadlock-prone under high concurrency (e.g., large team transfers or large profile assignment changes).
Changes:
- Sorts
(host_uuid, profile_uuid)pairs to make SQL generation deterministic and to acquire row locks in a consistent order. - Reorders tuple predicates to match the
host_mdm_windows_profilesprimary key and increases the batch size used for the batched UPDATE/UPSERT work. - Adds a release note entry describing the performance improvements.
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| server/datastore/mysql/microsoft_mdm.go | Sorts host/profile pairs, matches PK tuple order in IN clauses, and increases batch sizes to reduce query overhead and deadlock retries. |
| changes/42545-windows-profile-reconciliation-batching | Adds a changelog entry for the Windows profile reconciliation performance improvements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #44075 +/- ##
==========================================
+ Coverage 66.73% 66.80% +0.06%
==========================================
Files 2627 2631 +4
Lines 211165 211319 +154
Branches 9423 9420 -3
==========================================
+ Hits 140924 141167 +243
+ Misses 57457 57334 -123
- Partials 12784 12818 +34
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…ions, reducing row lock durations and improving scalability for large team transfers.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
|
/agentic_review |
|
Persistent review updated to latest commit 8516b6a |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/datastore/mysql/microsoft_mdm.go`:
- Around line 3296-3315: The INSERT into host_mdm_windows_profiles in baseStmt
never updates profile_name on duplicate, leaving profile_name stale; modify the
ON DUPLICATE KEY UPDATE clause in the baseStmt built in microsoft_mdm.go so that
profile_name is set to VALUES(profile_name) (i.e., add profile_name =
VALUES(profile_name) alongside the other updated columns) to ensure profile_name
is refreshed for existing rows used by the Windows verification/retry helpers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 25c95363-201d-4cc2-a44d-25ec5260e181
📒 Files selected for processing (4)
changes/42545-windows-profile-reconciliation-batchingserver/datastore/mysql/mdm.goserver/datastore/mysql/mdm_test.goserver/datastore/mysql/microsoft_mdm.go
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
|
/agentic_review |
|
Persistent review updated to latest commit f4c6f6b |
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 17 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
server/datastore/mysql/mdm_test.go (1)
8699-8704: Make the darwin probe eligible except for platform.
sameTeamDarwinHostis never enrolled, so this negative case can still pass if the query excludes it for lack of MDM eligibility rather than becauseplatform != "windows". Enrolling it via Apple MDM would make this assertion actually prove the platform scoping.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/datastore/mysql/mdm_test.go` around lines 8699 - 8704, The darwin host created as sameTeamDarwinHost is never enrolled, so the negative test might pass for the wrong reason; update the test.NewHost call that creates sameTeamDarwinHost to mark it as enrolled in Apple MDM (so it is MDM-eligible) while keeping test.WithPlatform("darwin") and test.WithTeamID(team.ID); add the appropriate helper option your test helpers provide (e.g., test.WithEnrolledInMDM / test.WithMDMEnrollment or the equivalent) to sameTeamDarwinHost so the only exclusion is the platform check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/datastore/mysql/microsoft_mdm.go`:
- Around line 2353-2379: The outer WHERE host_uuid > ? must be moved into each
subquery so the cursor filters are applied before the UNION; update the usages
of windowsProfilesToInstallQuery and windowsProfilesToRemoveQuery (the
toInstall/toRemove fmt.Sprintf calls) to scope the cursor predicate in their
host filters (e.g., add h.uuid > ? / hmwp.host_uuid > ? into those subqueries)
and remove the outer WHERE from stmt while keeping the outer ORDER BY and LIMIT;
then adjust the sqlx.SelectContext argument list in the withTx block to pass the
new cursor placeholder(s) in the correct order for the install/remove subquery
placeholders (and drop the now-removed outer afterHostUUID placeholder if you
removed that WHERE).
---
Nitpick comments:
In `@server/datastore/mysql/mdm_test.go`:
- Around line 8699-8704: The darwin host created as sameTeamDarwinHost is never
enrolled, so the negative test might pass for the wrong reason; update the
test.NewHost call that creates sameTeamDarwinHost to mark it as enrolled in
Apple MDM (so it is MDM-eligible) while keeping test.WithPlatform("darwin") and
test.WithTeamID(team.ID); add the appropriate helper option your test helpers
provide (e.g., test.WithEnrolledInMDM / test.WithMDMEnrollment or the
equivalent) to sameTeamDarwinHost so the only exclusion is the platform check.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 73837715-5597-4af2-a175-f53c7f20748e
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (16)
changes/42545-windows-profile-reconciliation-batchinggo.modserver/datastore/mysql/mdm.goserver/datastore/mysql/mdm_test.goserver/datastore/mysql/microsoft_mdm.goserver/datastore/mysql/microsoft_mdm_eager_test.goserver/datastore/mysql/microsoft_mdm_property_test.goserver/datastore/mysql/microsoft_mdm_test.goserver/datastore/mysql/mysql.goserver/datastore/mysqlredis/windows_recon_cursor.goserver/fleet/datastore.goserver/mock/datastore_mock.goserver/service/microsoft_mdm.goserver/service/microsoft_mdm_integration_test.goserver/service/microsoft_mdm_test.goserver/service/reconcile_windows_profiles_property_test.go
|
@claude review once |
| // (install or remove). If afterHostUUID is empty, scanning starts from | ||
| // the beginning. The cron uses this to slice its per-tick work into a | ||
| // bounded host window; see ReconcileWindowsProfiles. | ||
| func (ds *Datastore) ListNextPendingMDMWindowsHostUUIDs(ctx context.Context, afterHostUUID string, batchSize int) ([]string, error) { |
There was a problem hiding this comment.
The cursor is lexicographic: host_uuid > cursor, ordered alphabetically. UUIDs are "random". This means if the cursor is at m... and a new host enrolls with UUID a... we have to wait until the cursor gets set to "" and wraps around?
There was a problem hiding this comment.
Gotcha. Not ideal, but we don't have a timestamp we can use for the cursor. Also putting a queue of ids into redis seems not ideal. I think this is the best we can do without other major modifications. 👍
|
One other comment I forgot to add. There isn't really any observability on batch progress. There's no metric or logs on how many hosts remain in the pending universe, how many passes have completed, or how long a full pass takes. Maybe adding a |
Related issue: Resolves #44052
Improve performance by reducing the time for the synchronous API call to update profiles or switch teams. And spreading out the application of profiles by processing 2000 hosts every 30 seconds.
Windows profile reconciliation is no longer synchronous to bulk-set.
Apple, Android, and Apple-declaration paths still write their pending state inside the bulk-set transaction. The Windows path commits the transactional inputs and lets the existing
mdm_windows_profile_managercron pick the work up on its next tick. The visible effect is thathost_mdm_windows_profilesis no longer guaranteed to be populated by the time bulk-set returns; it converges within one cron interval.The Windows reconciler now processes hosts in bounded batches, with a persisted cursor.
Previous behavior was "scan the universe of pending Windows hosts on every tick." New behavior is a host-window query bounded by batch size and a
host_uuidcursor, advanced after the batch commits successfully and persisted across ticks. A failed tick leaves the cursor untouched so the same window is retried.Two replication races are now explicitly handled.
updates.WindowsConfigProfilefromBulkSetPendingMDMHostProfilesis now always false in production.The only consumer ORs it with the transactional signal from
BatchSetMDMProfiles, which is the accurate source. The bulk-set call no longer attempts to compute or return that activity signal itself.Tests opt in to the old synchronous behavior via a named hook.
Default test behavior matches production (deferred). Legacy tests whose assertions require Windows rows immediately after bulk-set call an explicit enable-hook and rely on
t.Cleanupto restore.Checklist for submitter
If some of the following don't apply, delete the relevant line.
changes/,orbit/changes/oree/fleetd-chrome/changes.See Changes files for more information.
Testing
Summary by CodeRabbit