Skip to content

fix(api): resolve UUID selectors by identity and keep single-pass algorithms off the iteration budget - #1926

Merged
DecisionNerd merged 2 commits into
mainfrom
fix/1922-analyst-verb-limits
Oct 8, 2026
Merged

DecisionNerd merged 2 commits into
mainfrom
fix/1922-analyst-verb-limits

Conversation

@DecisionNerd

@DecisionNerd DecisionNerd commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1922

What

  • UUID selector. NodeSelector::Uuid / Handle now resolve by probing the authenticated UUID membership index (ADR 0057) instead of scanning all node topology under the 1,000,000-row cap. A graph with no index at its topology generation (some projection graphs are written without one) still falls back to the capped scan, with the same node selector matched no nodes validation error for a missing UUID. Label/property selectors still scan and keep the cap.
  • Single-pass algorithms leave the iteration budget alone. AlgorithmControl::checkpoint() charges the 10,000-unit iteration budget, which counts rounds of iterative algorithms. LCC and a set of other single-pass algorithms called it per node, per source, per 1,024 edges or per 1,024 pairs. They now call the existing check_cancelled(). Cancellation still applies in every loop; the node, edge and output-row limits still bound the input. No limit was raised and no public signature changed.
  • The deferred-replay accounting that kept parallel charges deterministic in closeness, harmonic closeness, betweenness and triangle count is removed, since nothing is charged.

Fixed (same per-node/per-source/per-chunk charge): rank clustering_coefficient, degree, triangles, k_core (and the shared simple-neighbour preparation), closeness, harmonic_closeness, betweenness, common_neighbors, total_neighbors, resource_allocation, adamic_adar, preferential_attachment; analyze triangle_count, transitivity; paths dfs.

Not changed (still charge the budget per node/edge/pair, listed for a follow-up decision): CELF, jaccard/KNN similarity, dyad/triad census, is_planar, graph colourings, spanning trees, conductance, modularity, matching, flows and cuts, Dijkstra family, label-propagation and other clustering helpers' per-sweep work charges, and the exponential searches (automorphisms, chromatic number, cycles, Steiner, Yen). Several of these need a design call because their iteration budget is also their work bound.

Evidence

  • crates/graphforge-api/tests/analyst_verb_limits.rs (public facade, bulk construction):
    • BFS from a UUID source on a 1,000,100-node graph. On main: Validation("node selector topology scan exceeds row limit").
    • LCC on a 10,500-node graph with a 3,000-neighbour hub, three modes, checked exactly against an independent computation. On main: IterationLimit { observed: 10001, limit: 10000 }.
  • Exec tests: single-pass ignores iterations: 0; cancellation mid-run inside a 12,000-leaf hub pair loop (serial and pool); per-node and prepare-phase cancellation; node/edge/output limits on a 20,000-node graph.
  • Mutation proofs: no-op'ing the pair-loop poll, the per-node poll, or the prepare poll each fails its test; restoring checkpoint() in the 14 changed non-test sources fails 24 modified tests; restoring it in degree, dfs, transitivity and triangle_count fails theirs; making the selector probe always succeed fails the missing-UUID tests.
  • Synthetic Graphalytics repro (wiki-Talk mapping, gf import-session, GDC driver query with the bfs and lcc variants), release binaries from this branch: N=10,100 pass, N=1,000,000 pass, N=1,000,100 pass (bfs and lcc). Frozen c3551e1 binaries: N=10,100 lcc fails with observed 10001; N=1,000,100 bfs fails with the row limit and lcc with observed 10001.
  • make test-rust ARGS="-p graphforge-exec -p graphforge-api": 2687 passed, 0 failed, 27 skipped. cargo fmt --check, cargo clippy --workspace -- -D warnings, cargo check --workspace --benches --locked, ruff, scripts/ci/repo-checks.sh, scripts/check-workflows.sh pass.

Notes

  • AlgorithmLimits has no time or memory limit, so there is nothing to keep for those; cancellation is the only dynamic control.
  • clear() now drops the cached membership index: the topology generation counter restarts there and repeats an earlier value, so a stale cache validated an old UUID and rejected a current one (regression test clear_and_repopulate_to_the_same_generation_never_reuses_the_cached_index, failed before the fix).
  • Known limitation: closeness, harmonic closeness and betweenness have no aggregate work bound beyond the input limits and cancellation, and the facades pass uncancelled tokens. Betweenness's O(V²) reduction memory predates this PR.
  • The selector's only contact with the identity authority is indexed_node_membership, so perf(storage): stop producing and reading the UUID membership index #1925 (which removes the membership index) swaps in its Parquet identity probe there.
  • LCC's pair loop is O(sum of degree squared) with a binary search per pair. It no longer refuses, but a 100k-degree hub (real wiki-Talk) will be slow. That is a performance question, not changed here.

🤖 Generated with Claude Code

…orithms off the iteration budget (#1922)

A UUID node selector named one node but resolved it by scanning every node's
topology row under a 1,000,000-row cap, so paths(by=bfs) from a UUID source
failed on any graph over 1M nodes. It now probes the authenticated UUID
membership index (ADR 0057). Only a graph with no index at its topology
generation still scans, under the same cap and typed error.

AlgorithmControl::checkpoint() consumes the 10,000-unit iteration budget that
counts rounds of iterative algorithms, but single-pass algorithms called it per
node, per source, per 1,024 edges or per 1,024 pairs. LCC failed above ~10,000
nodes, and a hub's pair loop alone exceeded the budget. Single-pass algorithms
now poll check_cancelled(): cancellation still applies, the node, edge and
output-row limits still bound the input, and the iteration budget is left to
iterative algorithms. The deferred-replay accounting that kept parallel charges
deterministic is removed from closeness, harmonic closeness, betweenness and
triangle count, since nothing is charged.

Fixed: clustering_coefficient, degree, triangles, k_core (and the shared simple
neighbour preparation), closeness, harmonic_closeness, betweenness,
common_neighbors, total_neighbors, resource_allocation, adamic_adar,
preferential_attachment, analyze triangle_count and transitivity, paths dfs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: CurateLabs/graphforge/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 29e6a096-ef3b-4e94-a73b-9317c93b815d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


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.

@github-actions github-actions Bot added executor Changes to query executor core Core source code changes documentation Improvements or additions to documentation labels Oct 8, 2026
clear() restarts the topology generation counter, so a cached index from before
it could match a later generation and validate a removed node while rejecting a
current one. The selector's identity probe is also isolated in one function so
the index can be replaced later.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@DecisionNerd
DecisionNerd added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit af94096 Oct 8, 2026
17 checks passed
@DecisionNerd
DecisionNerd deleted the fix/1922-analyst-verb-limits branch October 8, 2026 22:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core source code changes documentation Improvements or additions to documentation executor Changes to query executor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(api): analyst verbs refuse ordinary graphs at fixed limits (UUID selector over 1M nodes, LCC over 10k nodes)

1 participant