Problem
RecordPolicyQueryExecutions in server/datastore/mysql/policies.go runs on every osquery distributed-write that includes policy results. It UPSERTs every incoming (policy_id, host_id, passes) triple via INSERT INTO policy_membership ... ON DUPLICATE KEY UPDATE updated_at=VALUES(updated_at), passes=VALUES(passes), regardless of whether passes actually changed since the previous run. With N hosts and M policies-per-host, every check-in writes N×M rows even when steady-state passing/failing rates are stable.
The same function already does the read needed to skip the write. FlippingPoliciesForHost selects the previous (policy_id, passes) rows from policy_membership and computes both newFailing and newPassing — the exact set of rows that actually need a passes change. The current code only consumes newPassing (for retry-reset bookkeeping) and discards newFailing, so it falls back to UPSERTing everything.
Impact
This path's cost depends on the stability of each host's policy results. First-time submissions (right after enrollment, or after a policy is added or its query changes) genuinely need to write — there is no prior row to compare against. Steady-state submissions on a stable host population are mostly no-op UPSERTs: passes matches what's already in the row.
The 2026-04-25 loadtest on victor-484 captured both regimes. During the enrollment wave that scaled the fleet from 10,500 to 20,000 hosts (~19:55–20:10Z), INSERT INTO policy_membership ... ON DUPLICATE KEY UPDATE peaked at ~1,277 s of writer wall in a single 5-min bucket — about 4.3 s of writer wall per second of clock, dominating every other writer that interval. Once the wave settled, the same statement fell to ~17 s over a 15-min window and dropped to ~#13 in the writer top-15, well below ambient writes like scheduled_query_stats and host_seen_times.
The fix does not change the enrollment-burst cost — those inserts have to land. What it removes is the UPSERT cost for unchanged-passes rows, which is the dominant case in long-running operation: a Fleet that's been up long enough for hosts to have submitted at least one round of policy results, with stable pass/fail rates, sees most RecordPolicyQueryExecutions writes become no-ops without this change. The savings compound over time and reduce writer-side amplification when an enrollment wave or policy edit lands on top of ambient load.
Proposed fix
Use the result of FlippingPoliciesForHost to build the UPSERT batch only from newFailing ∪ newPassing instead of all incoming results. Rows whose previous passes value matches the incoming value are skipped — they're true no-ops.
Two specifics:
The current code calls FlippingPoliciesForHost for the retry-reset path but throws away newFailing. Capture both and use their union. In the SubmitDistributedQueryResults path that pre-computes newlyPassingPolicyIDs and passes it in (so the inner call is skipped), pass both newFailing and newPassing through to RecordPolicyQueryExecutions, or fall back to a fresh FlippingPoliciesForHost read locally — the read is small (single host, small policy count) and the write savings dominate.
updated_at semantics shift slightly: today it refreshes every check-in regardless of state change; after the fix it refreshes only when passes actually flips. This is arguably more useful (it now means "last time membership state changed"), but verify no consumer reads updated_at as a "last seen" timestamp. The existing policy_updated_at on the hosts table already serves the per-host last-policy-run signal and is unchanged by this fix.
Evidence
Loadtest writer top-ops (2026-04-25, victor-484): INSERT INTO policy_membership ... ON DUPLICATE KEY UPDATE peaked at ~1,277 s in a single 5-min bucket and ~931 s over a 15-min window during the 19:55–20:10Z enrollment wave (10,500 → 20,000 hosts). After the wave settled, the same statement fell to ~17 s over a 15-min window and dropped out of the writer top-15 entirely.
Existing flipping-detection that already does the work to identify writeable rows: FlippingPoliciesForHost in server/datastore/mysql/policies.go.
Current write site: RecordPolicyQueryExecutions in server/datastore/mysql/policies.go.
Related top-2 reader, also touching policy_membership: SELECT SUM(1 - pm.passes) AS n_failed FROM policy_membership pm WHERE pm.host_id = ? at ~107 s wall over the same window.
Problem
RecordPolicyQueryExecutionsinserver/datastore/mysql/policies.goruns on every osquery distributed-write that includes policy results. It UPSERTs every incoming(policy_id, host_id, passes)triple viaINSERT INTO policy_membership ... ON DUPLICATE KEY UPDATE updated_at=VALUES(updated_at), passes=VALUES(passes), regardless of whetherpassesactually changed since the previous run. With N hosts and M policies-per-host, every check-in writes N×M rows even when steady-state passing/failing rates are stable.The same function already does the read needed to skip the write.
FlippingPoliciesForHostselects the previous(policy_id, passes)rows frompolicy_membershipand computes bothnewFailingandnewPassing— the exact set of rows that actually need apasseschange. The current code only consumesnewPassing(for retry-reset bookkeeping) and discardsnewFailing, so it falls back to UPSERTing everything.Impact
This path's cost depends on the stability of each host's policy results. First-time submissions (right after enrollment, or after a policy is added or its query changes) genuinely need to write — there is no prior row to compare against. Steady-state submissions on a stable host population are mostly no-op UPSERTs:
passesmatches what's already in the row.The 2026-04-25 loadtest on victor-484 captured both regimes. During the enrollment wave that scaled the fleet from 10,500 to 20,000 hosts (~19:55–20:10Z),
INSERT INTO policy_membership ... ON DUPLICATE KEY UPDATEpeaked at ~1,277 s of writer wall in a single 5-min bucket — about 4.3 s of writer wall per second of clock, dominating every other writer that interval. Once the wave settled, the same statement fell to ~17 s over a 15-min window and dropped to ~#13 in the writer top-15, well below ambient writes likescheduled_query_statsandhost_seen_times.The fix does not change the enrollment-burst cost — those inserts have to land. What it removes is the UPSERT cost for unchanged-
passesrows, which is the dominant case in long-running operation: a Fleet that's been up long enough for hosts to have submitted at least one round of policy results, with stable pass/fail rates, sees mostRecordPolicyQueryExecutionswrites become no-ops without this change. The savings compound over time and reduce writer-side amplification when an enrollment wave or policy edit lands on top of ambient load.Proposed fix
Use the result of
FlippingPoliciesForHostto build the UPSERT batch only fromnewFailing ∪ newPassinginstead of all incoming results. Rows whose previouspassesvalue matches the incoming value are skipped — they're true no-ops.Two specifics:
The current code calls
FlippingPoliciesForHostfor the retry-reset path but throws awaynewFailing. Capture both and use their union. In theSubmitDistributedQueryResultspath that pre-computesnewlyPassingPolicyIDsand passes it in (so the inner call is skipped), pass bothnewFailingandnewPassingthrough toRecordPolicyQueryExecutions, or fall back to a freshFlippingPoliciesForHostread locally — the read is small (single host, small policy count) and the write savings dominate.updated_atsemantics shift slightly: today it refreshes every check-in regardless of state change; after the fix it refreshes only whenpassesactually flips. This is arguably more useful (it now means "last time membership state changed"), but verify no consumer readsupdated_atas a "last seen" timestamp. The existingpolicy_updated_aton thehoststable already serves the per-host last-policy-run signal and is unchanged by this fix.Evidence
Loadtest writer top-ops (2026-04-25, victor-484):
INSERT INTO policy_membership ... ON DUPLICATE KEY UPDATEpeaked at ~1,277 s in a single 5-min bucket and ~931 s over a 15-min window during the 19:55–20:10Z enrollment wave (10,500 → 20,000 hosts). After the wave settled, the same statement fell to ~17 s over a 15-min window and dropped out of the writer top-15 entirely.Existing flipping-detection that already does the work to identify writeable rows:
FlippingPoliciesForHostinserver/datastore/mysql/policies.go.Current write site:
RecordPolicyQueryExecutionsinserver/datastore/mysql/policies.go.Related top-2 reader, also touching policy_membership:
SELECT SUM(1 - pm.passes) AS n_failed FROM policy_membership pm WHERE pm.host_id = ?at ~107 s wall over the same window.