Skip to content

feat(AF-624): pre-flight query cost & blast-radius estimation - #644

Merged
babltiga merged 2 commits into
mainfrom
feature/AF-624-preflight-cost-estimation
Jul 24, 2026
Merged

feat(AF-624): pre-flight query cost & blast-radius estimation#644
babltiga merged 2 commits into
mainfrom
feature/AF-624-preflight-cost-estimation

Conversation

@babltiga

Copy link
Copy Markdown
Contributor

Closes #624

Summary

Automatically runs the engine's native cost estimation (EXPLAIN / explain()) right after every query submission and — for UPDATE/DELETE — an actual affected-row count, then surfaces the result to reviewers, routing policies, and the AI analyzer's prompt.

Builds on the AF-445 dry-run capability (QueryEngine.dryRun / QueryExecutor.dryRun / per-dialect DryRunPlanners) rather than adding a new ExplainCapability SPI — the default-method pattern already covers "engines opt in individually; absent capability → estimate unavailable, never blocks submission".

Backend

  • query_estimates table (V127/V128, mirrors ai_analyses + back-pointer): engine id, supported flag, estimated rows, exact affected-row count, scan type (root plan operation), cost, raw plan, failure sentinel columns.
  • proxy.api.QueryCostEstimateService (DefaultQueryCostEstimateService) — idempotent, never-throws estimate pipeline: row-security-governed dryRun, plus for UPDATE/DELETE an exact count — relational via a JSqlParser SELECT COUNT(*) rewrite (AffectedRowCounter, RLS applied by the existing RowSecurityRewriter), engine-managed via the new default SPI method QueryEngine.countAffectedRows(...) returning QueryAffectedRowsResult (sentinel-unsupported default).
  • Triggered twice, race-safe by idempotency: QueryCostEstimateListener on QuerySubmittedEvent, and eagerly from DefaultAiAnalyzerService before the LLM call.
  • New tight statement-timeout knob ACCESSFLOW_PROXY_ESTIMATE_TIMEOUT (default PT5S).
  • Routing policies (AF-379): two new condition leaves estimated_rows (comparison) and scan_type (glob) with a live, fail-closed lookup in QueryReviewStateMachine.buildContext — absent/unsupported/failed estimate ⇒ leaf is false.
  • AI prompt: new {{cost_estimate}} placeholder in SystemPromptRenderer.DEFAULT_TEMPLATE; costEstimateContext parameter threaded through AiAnalyzerStrategy and all adapters/decorators.
  • GET /queries/{id} gains a nullable cost_estimate object; new query.estimate_complete WebSocket event.

Engine plugins (re-pinned in this PR)

countAffectedRows overrides for the four dry-run-capable engines, each with a version bump + connector.json SHA-256 re-pin:

  • mongodb 1.0.7 — real countDocuments with the $match-injected RLS filter
  • couchbase 1.1.4SELECT COUNT(*) rewrite of the WHERE-spliced statement (CouchbaseCountRewriter)
  • neo4j 1.0.4count(...) RETURN-projection rewrite of MATCH…DELETE/SET (CypherAffectedCountDeriver)
  • elasticsearch 1.1.3_count with the bool.filter-wrapped RLS query (update_by_query/delete_by_query)

Redis, Cassandra/ScyllaDB, DynamoDB, and the warehouse engines inherit the default and degrade gracefully (no bytecode change, no re-pin).

Frontend

  • CostEstimatePanel (components/review/) on QueryDetailPage — reuses PlanTree; states: pending (while PENDING_AI), available (rows/count/scan type/cost + plan tree + raw plan), unavailable, failed.
  • query.estimate_complete invalidation in websocketManager; cost_estimate types in types/api.ts; i18n keys in all 5 locales.
  • Routing-policy guided builder supports the two new condition types (routingPolicyForm.ts, RoutingPoliciesPage).

e2e

Extended e2e/tests/query-submit.spec.ts: after submitting UPDATE inventory.products …, the detail page shows the cost-estimate panel with plan + affected-row count (WS-driven, no reload).

Docs & website updated

  • docs/03-data-model.mdquery_estimates table; routing condition wire-format rows
  • docs/04-api-spec.mdcost_estimate in the detail response; WS event row; routing-policy condition list
  • docs/05-backend.md — new "Automatic pre-flight cost estimate (AF-624)" section; routing-engine + prompt-template updates; engine SPI table
  • docs/06-frontend.md — QueryDetailPage panel bullet; WS invalidations table
  • docs/09-deployment.md + CLAUDE.mdACCESSFLOW_PROXY_ESTIMATE_TIMEOUT; updated AiAnalyzerStrategy signature quote
  • docs/15-engine-sdk.mdcountAffectedRows SPI method row
  • README.md, website/index.html, website/docs/index.html — feature pitch + routing-policy condition docs

Verification

  • Backend mvn verify -Pcoverage: 5,428 tests green (incl. ApplicationModulesTest, ApiPackageDependencyTest)
  • All four engine plugin builds green (unit + Testcontainers ITs + shaded-jar ServiceLoader ITs); SHA pins verified via check-engine-pins.mjs
  • Frontend lint + typecheck + test:coverage (90/80 gate) + build green
  • e2e: full stack — query-submit, ws-realtime, admin-routing-policies, reviews-approve, query-execute specs green locally

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Frontend Test Results

1 296 tests   1 296 ✅  4m 42s ⏱️
  169 suites      0 💤
    1 files        0 ❌

Results for commit 93882cc.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Frontend Coverage (frontend)

Status Category Percentage Covered / Total
🟢 Lines 93.81% (🎯 90%) 2079 / 2216
🟢 Statements 91.79% (🎯 90%) 2317 / 2524
🟢 Functions 90.96% (🎯 90%) 634 / 697
🟢 Branches 83.59% (🎯 80%) 1310 / 1567
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
frontend/src/pages/admin/routingPolicyForm.ts 97.87% 78.91% 95% 99.23% 121, 127, 381
frontend/src/realtime/websocketManager.ts 85.89% 70.51% 94.44% 89.78% 35, 64, 87, 92, 99-100, 146, 149, 155, 163, 193-194, 223-236
frontend/src/utils/enumLabels.ts 96.52% 100% 90.56% 96.5% 192, 200, 321, 446, 504
Generated in workflow #823 for commit 93882cc by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Backend Test Results

5 428 tests  +39   5 428 ✅ +39   18m 14s ⏱️ + 1m 24s
  676 suites + 5       0 💤 ± 0 
  676 files   + 5       0 ❌ ± 0 

Results for commit 93882cc. ± Comparison against base commit 82c8be8.

♻️ This comment has been updated with latest results.

@github-actions

Copy link
Copy Markdown
Contributor

@babltiga
babltiga merged commit cb363d2 into main Jul 24, 2026
34 checks passed
@babltiga
babltiga deleted the feature/AF-624-preflight-cost-estimation branch July 24, 2026 08:19
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.

proxy: pre-flight query cost & blast-radius estimation (EXPLAIN + affected-row counts for reviewers and routing policies)

1 participant