Skip to content

Bound query scan to prevent full-DB page-fault thrash - #160

Merged
kwsantiago merged 5 commits into
mainfrom
fix/query-scan-cap
Jul 17, 2026
Merged

Bound query scan to prevent full-DB page-fault thrash#160
kwsantiago merged 5 commits into
mainfrom
fix/query-scan-cap

Conversation

@wksantiago

@wksantiago wksantiago commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added query_scan_multiplier ([limits], default 20) to cap per-query index scanning as limit × multiplier (set to 0 to disable).
    • Added WISP_QUERY_SCAN_MULTIPLIER support for controlling the multiplier via environment variables.
    • Introduced uncapped querying for exports and synchronization, improving completeness for trusted workflows.
  • Documentation
    • Updated configuration docs and wisp.toml.example with the new setting and behavior.
  • Bug Fixes
    • Improved follow-list loading and negentropy synchronization by enabling full scans where appropriate.
  • Tests
    • Added coverage to validate scan-cap behavior and uncapped modes.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kwsantiago, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 74e35a03-ac95-4dcf-b1db-126e350e728f

📥 Commits

Reviewing files that changed from the base of the PR and between ab34916 and 8f06d47.

📒 Files selected for processing (2)
  • src/handler.zig
  • src/store.zig

Walkthrough

The store now applies a configurable scan multiplier to bounded queries, supports uncapped queryFull calls and ID-based iteration, and loads the setting from configuration or environment variables. Export, follow-list, and negentropy paths use uncapped queries.

Changes

Query scan limits

Layer / File(s) Summary
Configuration and store wiring
src/config.zig, src/store.zig, src/main.zig
Adds query_scan_multiplier, defaults it to 20, parses file and environment values, and applies it to the store.
Query scan enforcement and ID fast path
src/store.zig
Tracks scanned index entries, enforces configured bounds, adds uncapped queryFull, and bypasses scan caps for explicit ID filters.
Uncapped internal query paths
src/main.zig, src/spider.zig, src/handler.zig
Uses queryFull for exports, contact-list loading, and negentropy synchronization while documenting capped request paths.
Configuration documentation and examples
docs/configuration.md, wisp.toml.example
Documents the setting, environment variable, default, and 0 uncapped behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ExportOrSpider
  participant Store
  participant QueryIterator
  ExportOrSpider->>Store: queryFull(filters, limit)
  Store->>QueryIterator: init(filters, limit, 0)
  QueryIterator->>QueryIterator: scan index entries without a cap
  QueryIterator-->>ExportOrSpider: matching events
Loading

Suggested reviewers: kwsantiago

Poem

A rabbit bounds through queries bright,
With scan caps set just right.
Full paths stretch and freely run,
Exporting events beneath the sun.
Twenty hops, or zero sky—
The store goes fast as carrots fly.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a query scan cap to avoid expensive full-database scans.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/query-scan-cap

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.

@wksantiago wksantiago self-assigned this Jul 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/store.zig (1)

398-401: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Apply the configurable multiplier to queryMultiKind scans.

queryMultiKind continues to use a hardcoded limit * 20 cap, completely ignoring self.query_scan_multiplier. This breaks the configuration contract—if an administrator configures the multiplier to 0 (uncapped) or 50, multi-kind queries will still stubbornly enforce a 20x bound.

Additionally, using saturating arithmetic (*|) prevents potential integer overflows if limit is exceptionally large.

🐛 Proposed fix to enforce the configured bound
-        var scanned: u32 = 0;
-        const max_scan: u32 = limit * 20;
-
-        while (entry != null and collected < limit and scanned < max_scan) : (entry = try cursor.get(.prev)) {
+        var scanned: u32 = 0;
+        const max_scan: u32 = if (self.query_scan_multiplier == 0) 0 else limit *| self.query_scan_multiplier;
+
+        while (entry != null and collected < limit and (max_scan == 0 or scanned < max_scan)) : (entry = try cursor.get(.prev)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/store.zig` around lines 398 - 401, Update the queryMultiKind scan limit
calculation around scanned and max_scan to use self.query_scan_multiplier
instead of the hardcoded 20 multiplier. Preserve the configured uncapped
behavior when the multiplier is 0, and use saturating multiplication (*|) so
exceptionally large limits cannot overflow; keep the existing scan loop behavior
otherwise.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/store.zig`:
- Around line 398-401: Update the queryMultiKind scan limit calculation around
scanned and max_scan to use self.query_scan_multiplier instead of the hardcoded
20 multiplier. Preserve the configured uncapped behavior when the multiplier is
0, and use saturating multiplication (*|) so exceptionally large limits cannot
overflow; keep the existing scan loop behavior otherwise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 436e241c-656f-4b98-ac85-9ba095245913

📥 Commits

Reviewing files that changed from the base of the PR and between b536bd4 and f616423.

📒 Files selected for processing (4)
  • src/config.zig
  • src/main.zig
  • src/spider.zig
  • src/store.zig

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/handler.zig`:
- Around line 714-717: Update the serving-side enumeration around
self.store.query so reconciliation never seals a capped or incomplete event set:
use the uncapped queryFull path, or detect a scan-cap condition and return
NEG-ERR instead. Preserve the existing network-safe behavior only if truncation
is explicitly surfaced and handled as an error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2353f2b0-2bb9-4f00-aa37-c5abe5ae3fa9

📥 Commits

Reviewing files that changed from the base of the PR and between f616423 and ab34916.

📒 Files selected for processing (5)
  • docs/configuration.md
  • src/handler.zig
  • src/main.zig
  • src/store.zig
  • wisp.toml.example
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main.zig
  • src/store.zig

Comment thread src/handler.zig
@wksantiago
wksantiago requested a review from kwsantiago July 17, 2026 16:54
@kwsantiago
kwsantiago merged commit 7c07362 into main Jul 17, 2026
4 checks passed
@kwsantiago
kwsantiago deleted the fix/query-scan-cap branch July 17, 2026 22:26
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.

2 participants