fix(user-access-policy): route non-block strict-match lookups through split-query path#2822
Merged
Merged
Conversation
… split-query path Live 2026-07-10 Twickets regression: a Restrict/image rule for the IP `82.17.209.70` was correctly stored in Redis and indexed on all 10 pronodes, yet the frictionless DM fell through to `default_pow` and served POW instead of image. Root cause: `findRulesRanked` (the non-`blockOnly` matchingFieldsOnly path) capped candidates at `SERVER_SIDE_RANK_TOP_N=20` after a server `SORTBY @_rank`. Under greedy `matchingFieldsOnly=true` the query is a wide OR over `ismissing(@headHash)` / `ismissing(@coords)` / `ismissing(@headersHash)`, so for a Twickets-shaped tenant (~860 candidates on the same clientId) the top 20 slots were filled by higher-specificity SIMD_REPLAY / SUDDEN_VOLUME_INCREASE Block rules that didn't apply to the request. The specific-IP rule (spec=2, sev=0, rank=4) was pushed out. Node saw only irrelevant candidates, `rankCandidateRules` filtered them all via `ruleApplies`, and the lookup returned `[]`. `handleAccessPolicy` returned `handled: false` and the DM ran with default POW. Fix: extend the existing split-query path (previously `blockOnly=true` hot path only) to cover every matchingFieldsOnly caller. Each probe hits a discriminating posting list; the ip:exact probe returns exactly the rules that literally match this IP. `blockOnly` is now a flag on the sub-query builder that gates the `@type:{block}` filter. Reader sorts by (specificity desc, block-first) so direct consumers see the same ordering the old FT.AGGREGATE ranker imposed. Regression coverage: - unit: `buildScopedRulesSubQueries` emits/omits `@type:{block}` per `blockOnly` flag; probe shape unchanged. - integration: specific-IP Restrict rule survives when 40 higher-specificity irrelevant Block rules co-exist on the same tenant (mirrors the live Twickets shape). Benchmarks unchanged: split hot path p50=1.4ms / p99=2.2ms against 19,300 seeded rules; 100×10 concurrent storm holds ~990 req/s. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Root cause and fix for the 2026-07-10 regression where a Restrict/image rule was silently dropped from the frictionless access-policy lookup even though the rule was correctly stored in Redis and indexed on all 10 pronodes. The DM fell through to
default_powand served POW instead of the configured image challenge.Test plan
test:unit— 75 passedtest:integration— 45 passed (including new Twickets regression test)redisRulesReaderRank.benchmark.integration— 4 passedredisRulesReaderLoad.benchmark.integration— 6 passed@prosopo/provider test:unit— 944 passed🤖 Generated with Claude Code