<!-- Add the related story/sub-task/bug number, like Resolves#123, or
remove if NA -->
**Related issue:** Resolves#42836
This is another hot path optimization.
## Before
When a host submits policy results via `SubmitDistributedQueryResults`,
the system needed to determine which policies "flipped" (changed from
passing to failing or vice versa). Each consumer computed this
independently:
```
SubmitDistributedQueryResults(policyResults)
|
+-- processScriptsForNewlyFailingPolicies
| filter to failing policies with scripts
| BUILD SUBSET of results
| CALL FlippingPoliciesForHost(subset) <-- DB query #1
| convert result to set, filter, queue scripts
|
+-- processSoftwareForNewlyFailingPolicies
| filter to failing policies with installers
| BUILD SUBSET of results
| CALL FlippingPoliciesForHost(subset) <-- DB query #2
| convert result to set, filter, queue installs
|
+-- processVPPForNewlyFailingPolicies
| filter to failing policies with VPP apps
| BUILD SUBSET of results
| CALL FlippingPoliciesForHost(subset) <-- DB query #3
| convert result to set, filter, queue VPP
|
+-- webhook filtering
| filter to webhook-enabled policies
| CALL FlippingPoliciesForHost(subset) <-- DB query #4
| register flipped policies in Redis
|
+-- RecordPolicyQueryExecutions
CALL FlippingPoliciesForHost(all results) <-- DB query #5
reset attempt counters for newly passing
INSERT/UPDATE policy_membership
```
Each `FlippingPoliciesForHost` call runs `SELECT policy_id, passes FROM
policy_membership WHERE host_id = ? AND policy_id IN (?)`. All 5 queries
hit the same table for the same host before `policy_membership` is
updated, so they all see identical state.
Each consumer also built intermediate maps to narrow down to its subset
before calling `FlippingPoliciesForHost`, then converted the result into
yet another set for filtering. This meant 3-4 temporary maps per
consumer.
## After
```
SubmitDistributedQueryResults(policyResults)
|
CALL FlippingPoliciesForHost(all results) <-- single DB query
build newFailingSet, normalize newPassing
|
+-- processScriptsForNewlyFailingPolicies
| filter to failing policies with scripts
| CHECK newFailingSet (in-memory map lookup)
| queue scripts
|
+-- processSoftwareForNewlyFailingPolicies
| filter to failing policies with installers
| CHECK newFailingSet (in-memory map lookup)
| queue installs
|
+-- processVPPForNewlyFailingPolicies
| filter to failing policies with VPP apps
| CHECK newFailingSet (in-memory map lookup)
| queue VPP
|
+-- webhook filtering
| filter to webhook-enabled policies
| FILTER newFailing/newPassing by policy IDs (in-memory)
| register flipped policies in Redis
|
+-- RecordPolicyQueryExecutions
USE pre-computed newPassing (skip DB query)
reset attempt counters for newly passing
INSERT/UPDATE policy_membership
```
The intermediate subset maps and per-consumer set conversions are
removed. Each process function goes directly from "policies with
associated automation" to "is this policy in newFailingSet?" in a single
map lookup.
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Performance Improvements**
* Reduced redundant database queries during policy result submissions by
computing flipping policies once per host check-in instead of multiple
times.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->