Skip to content

Improved the performance of the configuration profiles status summary - #48873

Merged
getvictor merged 19 commits into
mainfrom
48340-windows-profiles-status-rollup
Jul 23, 2026
Merged

Improved the performance of the configuration profiles status summary#48873
getvictor merged 19 commits into
mainfrom
48340-windows-profiles-status-rollup

Conversation

@getvictor

@getvictor getvictor commented Jul 7, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #48340

Windows only. The fix is to use a rollup status table instead of recalculating the host profile summary on demand.

Verified the fix in load test with 100k Windows MDM hosts. Note that this does not improve the host details page filtered by OS settings, which will be handled by the follow up #48996

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

  • Timeouts are implemented and retries are limited to avoid infinite loops

Testing

Database migrations

  • Checked schema for all modified table for columns that will auto-update timestamps during migration.
  • Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects.
  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

Summary by CodeRabbit

  • Bug Fixes
    • Improved Windows fleet configuration profile status summaries to avoid timeouts on large fleets.
    • Kept per-host Windows profile statuses accurate after profile updates, resends, certificate changes, cleanup, unenrollment, and host deletion.
    • Added automatic reconciliation to correct stale or orphaned status data.
  • Data Integrity
    • Improved Windows profile status reporting, including profile and BitLocker summaries, for more reliable results.

@getvictor
getvictor requested a review from Copilot July 7, 2026 17:03
@getvictor

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@getvictor

Copy link
Copy Markdown
Member Author

/agentic_review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

This PR addresses timeouts in GET /configuration_profiles/summary for Windows at large scale by introducing a per-host rollup table (host_mdm_windows_profiles_status) that materializes each host’s aggregate profiles delivery status, and updating the summary query to read that rollup instead of performing an O(hosts × profiles) correlated aggregation.

Changes:

  • Add host_mdm_windows_profiles_status table + migration backfill to store one status bucket per Windows host.
  • Maintain the rollup incrementally across Windows profile write paths, and add an hourly reconcile job as a self-healing sweep.
  • Update/extend tests to validate rollup correctness and to reconcile before reading summaries in tests that seed profile rows directly.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
changes/48340-windows-config-profiles-summary-scale Release note / changes entry (content excluded by policy).
server/service/integration_mdm_test.go Reconcile rollup before asserting Windows profile summaries in integration tests.
server/mock/datastore_mock.go Add mock hook for ReconcileWindowsProfilesStatus.
server/fleet/datastore.go Extend datastore interface with ReconcileWindowsProfilesStatus.
server/datastore/mysql/schema.sql Add host_mdm_windows_profiles_status table to schema.
server/datastore/mysql/migrations/tables/20260707150000_AddHostMDMWindowsProfilesStatus.go Migration to create/backfill the rollup table.
server/datastore/mysql/migrations/tables/20260707150000_AddHostMDMWindowsProfilesStatus_test.go Migration test for rollup backfill logic.
server/datastore/mysql/microsoft_mdm.go Rollup maintenance helper, reconcile implementation, and summary query changes to read rollup.
server/datastore/mysql/microsoft_mdm_test.go Add/adjust tests to validate rollup maintenance and reconcile behavior.
server/datastore/mysql/mdm.go Ensure Windows MDM cleanup also clears the new rollup table.
server/datastore/mysql/mdm_test.go Reconcile rollup before Windows summary assertions in tests that seed rows directly.
server/datastore/mysql/hosts.go Ensure host deletion cleanup includes host_mdm_windows_profiles_status.
cmd/fleet/cron.go Add hourly cleanup job to reconcile Windows profiles status rollup.
Files excluded by content exclusion policy (1)
  • changes/48340-windows-config-profiles-summary-scale

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server/datastore/mysql/microsoft_mdm.go Outdated
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds a per-host Windows MDM profile status rollup table, updates Windows profile write and cleanup paths to maintain it, and introduces paged reconciliation for drift and orphan rows. Windows profile summary queries now read the rollup instead of recomputing per-host aggregates. Reconciliation is exposed through datastore interfaces and mocks, scheduled hourly, and covered by datastore, integration, and host-deletion tests.

Possibly related PRs

  • fleetdm/fleet#42206: Overlaps with Windows profile status rollup maintenance during profile removal workflows.
  • fleetdm/fleet#43719: Relates to cleanup of Windows profile status rollup rows during MDM profile cleanup.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The code adds incremental Windows profile status rollups and reconciliation, matching the issue's goal to avoid full re-aggregation on each request.
Out of Scope Changes check ✅ Passed The changes stay focused on Windows summary performance, rollup maintenance, schema, tests, and release notes without obvious unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the main change: speeding up the configuration profiles status summary.
Description check ✅ Passed The description matches the template with related issue, summary, testing, and migration details, with only optional items omitted.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 48340-windows-profiles-status-rollup

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 7, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Non-atomic rollup refresh ✓ Resolved 🐞 Bug ☼ Reliability
Description
BulkUpsertMDMWindowsHostProfiles updates host_mdm_windows_profiles_status via
updateWindowsProfilesStatusRollupDB using ds.writer(ctx) (autocommit), but the helper performs a
DELETE followed by an INSERT. If the process crashes or the INSERT fails after the DELETE, the
rollup row can be missing and GetMDMWindowsProfilesSummary will undercount affected hosts until
ReconcileWindowsProfilesStatus repairs it.
Code

server/datastore/mysql/microsoft_mdm.go[R2443-2452]

+	// Keep the per-host profile status rollup current for the affected hosts (issue #48340). This path
+	// writes on the writer outside a transaction, so maintain the rollup on the same connection once all
+	// profile rows are written; the helper dedupes the host UUIDs and batches its work.
+	hostUUIDs := make([]string, 0, len(payload))
+	for _, p := range payload {
+		hostUUIDs = append(hostUUIDs, p.HostUUID)
+	}
+	if err := updateWindowsProfilesStatusRollupDB(ctx, ds.writer(ctx), hostUUIDs); err != nil {
+		return ctxerr.Wrap(ctx, err, "updating windows profiles status rollup after bulk upsert")
+	}
Evidence
The rollup update helper is a delete-then-insert sequence; BulkUpsert calls it with an autocommit
writer connection (not a tx), so failures between statements can drop rollup rows. The summary logic
treats missing/empty status as "" (not counted in Failed/Pending/Verifying/Verified), causing
undercounting until reconcile.

server/datastore/mysql/microsoft_mdm.go[2053-2114]
server/datastore/mysql/microsoft_mdm.go[2374-2453]
server/datastore/mysql/microsoft_mdm.go[2117-2154]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`updateWindowsProfilesStatusRollupDB` is implemented as `DELETE ...; INSERT ... SELECT ...` per batch. In `BulkUpsertMDMWindowsHostProfiles`, it’s invoked with `ds.writer(ctx)` outside a transaction, so the delete/insert pair is not atomic.
If the process crashes (or the insert statement errors) after the delete, rollup rows can be dropped, and the Windows profiles summary will temporarily (or until the next periodic reconcile) report incorrect counts.
### Issue Context
- The rollup table is now the source of truth for `GetMDMWindowsProfilesSummary` reads.
- Other write paths call the rollup helper in an existing transaction, but `BulkUpsertMDMWindowsHostProfiles` explicitly runs outside a transaction.
### Fix Focus Areas
- server/datastore/mysql/microsoft_mdm.go[2374-2453]
- server/datastore/mysql/microsoft_mdm.go[2053-2114]
### Implementation notes
Wrap the rollup refresh call in `BulkUpsertMDMWindowsHostProfiles` in a short `ds.withTx` (or equivalent) so the delete+insert is atomic and can be rolled back on error. For example:
- compute `hostUUIDs` as today
- `return ds.withTx(ctx, func(tx sqlx.ExtContext) error { return updateWindowsProfilesStatusRollupDB(ctx, tx, hostUUIDs) })`
This preserves the current “no rollup row when host has no profiles rows” semantics while preventing partial rollup deletion.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread server/datastore/mysql/microsoft_mdm.go Outdated
getvictor added 2 commits July 7, 2026 18:11
…s-status-rollup

# Conflicts:
#	server/datastore/mysql/schema.sql
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.55184% with 103 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.91%. Comparing base (d5d1b11) to head (9c82c6d).
⚠️ Report is 72 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/microsoft_mdm.go 65.91% 45 Missing and 31 partials ⚠️
server/datastore/mysql/mdm.go 53.84% 6 Missing and 6 partials ⚠️
.../20260721160351_AddHostMDMWindowsProfilesStatus.go 81.81% 4 Missing and 2 partials ⚠️
server/datastore/mysql/teams.go 63.63% 2 Missing and 2 partials ⚠️
cmd/fleet/cron.go 0.00% 3 Missing ⚠️
server/datastore/mysql/apple_mdm.go 33.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #48873      +/-   ##
==========================================
+ Coverage   67.89%   67.91%   +0.02%     
==========================================
  Files        3890     3891       +1     
  Lines      248429   249026     +597     
  Branches    13179    13179              
==========================================
+ Hits       168676   169136     +460     
- Misses      64540    64623      +83     
- Partials    15213    15267      +54     
Flag Coverage Δ
backend 69.30% <65.55%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

getvictor added 2 commits July 8, 2026 14:44
…s-status-rollup

# Conflicts:
#	server/datastore/mysql/schema.sql
@getvictor
getvictor requested a review from Copilot July 8, 2026 16:20
@getvictor

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@getvictor

Copy link
Copy Markdown
Member Author

/agentic_review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.

Files excluded by content exclusion policy (1)
  • changes/48340-windows-config-profiles-summary-scale

Comment thread server/service/integration_mdm_test.go Outdated
Comment thread server/datastore/mysql/microsoft_mdm.go Outdated
Comment thread server/datastore/mysql/microsoft_mdm.go Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e899bbb

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.

Files excluded by content exclusion policy (1)
  • changes/48340-windows-config-profiles-summary-scale
Comments suppressed due to low confidence (1)

server/datastore/mysql/mdm.go:2422

  • The rollup refresh selects all Windows hosts where this profile now has status IS NULL, which can include hosts that were already pending before this resend. At scale, that can cause an unnecessarily large rollup recompute. Capture the affected host UUIDs using the same status filter as the UPDATE (ideally FOR UPDATE) and refresh only those hosts.
		// Refresh the per-host Windows profile status rollup for the affected hosts in the same transaction.
		if table == "host_mdm_windows_profiles" {
			var windowsHostUUIDs []string
			if err := sqlx.SelectContext(ctx, tx, &windowsHostUUIDs,
				`SELECT host_uuid FROM host_mdm_windows_profiles WHERE profile_uuid = ? AND status IS NULL`,
				profileUUID); err != nil {
				return ctxerr.Wrap(ctx, err, "selecting affected hosts for batch resend")
			}

Comment thread server/datastore/mysql/microsoft_mdm_test.go
Comment thread server/datastore/mysql/microsoft_mdm.go
…s-status-rollup

# Conflicts:
#	server/datastore/mysql/schema.sql
Copilot AI review requested due to automatic review settings July 21, 2026 16:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • changes/48340-windows-config-profiles-summary-scale
Comments suppressed due to low confidence (1)

cmd/fleet/cron.go:1463

  • The cleanups/aggregation cron runs on a 1h interval (defaultInterval in newCleanupsAndAggregationSchedule). Running ReconcileWindowsProfilesStatus on every tick will page through all hosts/profiles and can reintroduce significant read load at scale (essentially scanning host_mdm_windows_profiles regularly), which undermines the performance goal of moving /configuration_profiles/summary to an O(hosts) read.

Consider moving this reconciler to its own schedule with a much longer interval (e.g. daily) and/or gating it behind a config flag so large deployments aren't forced into an hourly full-table reconcile.

		schedule.WithJob(
			// Self-healing safety net for the per-host Windows profile status rollup. This reconciles any drift and removes orphan rows.
			"windows_profiles_status_reconcile",
			func(ctx context.Context) error {
				return ds.ReconcileWindowsProfilesStatus(ctx)
			},

Copilot AI review requested due to automatic review settings July 22, 2026 19:19
@getvictor

Copy link
Copy Markdown
Member Author

@coderabbitai review

@getvictor

Copy link
Copy Markdown
Member Author

/agentic_review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • changes/48340-windows-config-profiles-summary-scale
Comments suppressed due to low confidence (1)

server/datastore/mysql/mdm.go:2431

  • BatchResendMDMProfileToHosts now runs an unbounded SELECT host_uuid ... inside the transaction to collect every affected Windows host for the async rollup refresh. On large fleets this can (a) double-scan the same large set of rows right after the UPDATE, (b) hold the transaction open while streaming a potentially huge result set, and (c) allocate a very large slice in memory.

Consider paging host UUIDs in batches (e.g., WHERE profile_uuid = ? AND status IS NULL AND host_uuid > ? ORDER BY host_uuid LIMIT ?) and dispatching rollup refresh per page (or performing the rollup recompute directly in a bounded background loop) to keep memory and transaction time bounded.

		// Collect the affected hosts for the rollup refresh. Selecting status IS NULL rows AFTER the update sees this transaction's own
		// writes, so it cannot miss a row the update touched; rows already NULL are harmless extras (the recompute is idempotent). The
		// refresh itself is dispatched asynchronously after commit: the affected set scales with the fleet (a fleet-wide failed-profile
		// resend touches every host), and a crash before it completes is healed by the hourly reconcile.
		if table == "host_mdm_windows_profiles" {
			if err := sqlx.SelectContext(ctx, tx, &windowsHostUUIDs,
				`SELECT host_uuid FROM host_mdm_windows_profiles WHERE profile_uuid = ? AND status IS NULL`,
				profileUUID); err != nil {
				return ctxerr.Wrap(ctx, err, "selecting affected hosts for batch resend")
			}

@getvictor
getvictor marked this pull request as ready for review July 23, 2026 14:30
@getvictor
getvictor requested a review from a team as a code owner July 23, 2026 14:30
@getvictor
getvictor requested a review from juan-fdz-hawa July 23, 2026 14:31
status VARCHAR(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (host_uuid),
KEY idx_host_mdm_windows_profiles_status_status (status)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't see this index being used anywhere?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@juan-fdz-hawa You're right. It is for the follow-up work to add this to the Hosts page os_settings filter (os_settings=pending). Let's keep it here so we don't need another migration in the follow-up PR.

@getvictor
getvictor requested a review from juan-fdz-hawa July 23, 2026 16:35
@getvictor
getvictor merged commit e42dc7a into main Jul 23, 2026
48 of 50 checks passed
@getvictor
getvictor deleted the 48340-windows-profiles-status-rollup branch July 23, 2026 16:59
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.

[Windows only] GET /configuration_profiles/summary times out at scale (O(hosts × profiles) status aggregation)

3 participants