Skip to content

Improved the performance of the configuration profiles status summary#48873

Draft
getvictor wants to merge 10 commits into
mainfrom
48340-windows-profiles-status-rollup
Draft

Improved the performance of the configuration profiles status summary#48873
getvictor wants to merge 10 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

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.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.

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

  • If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes

Testing

For unreleased bug fixes in a release candidate, one of:

  • Confirmed that the fix is not expected to adversely impact load test results
  • Alerted the release DRI if additional load testing is needed

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).

New Fleet configuration settings

  • Setting(s) is/are explicitly excluded from GitOps

If you didn't check the box above, follow this checklist for GitOps-enabled settings:

  • Verified that the setting is exported via fleetctl generate-gitops
  • Verified the setting is documented in a separate PR to the GitOps documentation
  • Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional)
  • Verified that any relevant UI is disabled when GitOps mode is enabled

fleetd/orbit/Fleet Desktop

  • Verified compatibility with the latest released version of Fleet (see Must rule)
  • If the change applies to only one platform, confirmed that runtime.GOOS is used as needed to isolate changes
  • Verified that fleetd runs on macOS, Linux and Windows
  • Verified auto-update works from the released version of component to the new version (see tools/tuf/test)

Summary by CodeRabbit

  • New Features

    • Added improved background reconciliation to keep Windows configuration profile status summaries accurate over time.
  • Bug Fixes

    • Windows configuration profile status summaries are now faster and more reliable on large fleets (no more timeouts).
    • Fixed stale status rollups after host deletion, unenrollment, and related profile updates.
    • Improved resend and verification flows so per-host Windows profile status rolls up stay consistent.

@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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 94e287de-0d3a-41b2-9d50-0cf0bdbbf8d5

📥 Commits

Reviewing files that changed from the base of the PR and between 19aac45 and e899bbb.

📒 Files selected for processing (16)
  • changes/48340-windows-config-profiles-summary-scale
  • cmd/fleet/cron.go
  • server/datastore/mysql/apple_mdm.go
  • server/datastore/mysql/hosts.go
  • server/datastore/mysql/hosts_test.go
  • server/datastore/mysql/mdm.go
  • server/datastore/mysql/mdm_test.go
  • server/datastore/mysql/microsoft_mdm.go
  • server/datastore/mysql/microsoft_mdm_test.go
  • server/datastore/mysql/migrations/tables/20260707150000_AddHostMDMWindowsProfilesStatus.go
  • server/datastore/mysql/migrations/tables/20260707150000_AddHostMDMWindowsProfilesStatus_test.go
  • server/datastore/mysql/mysql.go
  • server/datastore/mysql/schema.sql
  • server/fleet/datastore.go
  • server/mock/datastore_mock.go
  • server/service/integration_mdm_test.go

Walkthrough

This PR adds host_mdm_windows_profiles_status as a per-host Windows profile status rollup, backfills it in a new migration, and updates the schema accordingly. Windows MDM write and cleanup paths now refresh or delete the rollup after profile changes, and a new reconciliation method plus cron job recomputes and removes orphan rows. GetMDMWindowsProfilesSummary now reads from the maintained rollup, and related datastore tests, mocks, and integration helpers were updated.

Possibly related PRs

  • fleetdm/fleet#47156: Touches the Windows MDM delete path in server/datastore/mysql/microsoft_mdm.go, which is one of the cleanup areas updated here to keep the rollup table consistent.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: improving configuration profiles status summary performance.
Description check ✅ Passed The description includes the related issue and the repository template sections, covering the required structure.
Linked Issues check ✅ Passed The PR implements a denormalized Windows rollup and reconciliation path that removes the expensive per-request aggregation.
Out of Scope Changes check ✅ Passed The changes are centered on Windows configuration-profile summary performance and its supporting tests, schema, and maintenance paths.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.1)
server/datastore/mysql/hosts_test.go

ast-grep timed out on this file

server/service/integration_mdm_test.go

ast-grep timed out on this file


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 68.51852% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.11%. Comparing base (19aac45) to head (1756f76).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/microsoft_mdm.go 71.61% 21 Missing and 23 partials ⚠️
server/datastore/mysql/mdm.go 40.90% 7 Missing and 6 partials ⚠️
.../20260707150000_AddHostMDMWindowsProfilesStatus.go 81.81% 4 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   68.11%   68.11%   -0.01%     
==========================================
  Files        3709     3698      -11     
  Lines      235142   235288     +146     
  Branches    12498    12344     -154     
==========================================
+ Hits       160168   160264      +96     
- Misses      60598    60620      +22     
- Partials    14376    14404      +28     
Flag Coverage Δ
backend 69.72% <68.51%> (-0.01%) ⬇️

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 on lines +1870 to +1874
// windowsHostProfileStatusCaseExpr returns the SQL CASE expression (and its as-yet-unexpanded args)
// that reduces a group of host_mdm_windows_profiles rows (aliased hmwp) to a single status bucket:
// `<statusPrefix>failed`, `<statusPrefix>pending`, `<statusPrefix>verifying`, `<statusPrefix>verified`,
// or ”. It is the single source of truth for the Windows profile status priority logic (failed >
// pending > verifying > verified, reserved profiles excluded, install-only for verifying/verified,
Comment on lines +1956 to +1959
// orphan a rollup row) safe without wrapping it in a transaction.
// - The orphan-delete removes the now-stale rollup row for a host whose last profile row was just
// deleted (the upsert's GROUP BY produces no row for it), which the summary treats as ”.
//
Comment on lines +12 to +14
// Up_20260707150000 creates host_mdm_windows_profiles_status, a per-host rollup of the aggregate Windows configuration-profile
// delivery status. It materializes exactly one bucket per host ('failed'|'pending'|'verifying'|'verified'|”)
func Up_20260707150000(tx *sql.Tx) error {
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

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

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.

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

2 participants