Skip to content

v0.8.49: knowledge search improvements, tables perf improvements - #8108

Merged
waleedlatif1 merged 6 commits into
mainfrom
staging
Sep 21, 2026
Merged

waleedlatif1 merged 6 commits into
mainfrom
staging

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

… search deadline, and drop the dead search paths (#8101)
…end on protocol (#8104)

* improvement(tables): cut the DB round trips a table read and write spend on protocol

The grid's first page spent more time on round trips than on work. Four of them
were avoidable:

- `pendingDeleteMask` probed `table_jobs` on every read, though the table a
  request just loaded already carries its latest non-export job, and the
  `one_active_per_table` unique index makes that row the running delete when one
  exists. Callers that hold a table across a long walk (the export stream, the
  snapshot builder) keep probing per page, so a delete starting mid-walk still
  begins masking.
- The run-state sidecar was read for every table, including the ones that
  declare no workflow group and therefore cannot have a row — four chunked
  queries on a 1000-row page, all returning nothing.
- The drain opened a transaction per batch. The guards are fixed for the call,
  so each extra batch paid `BEGIN` + `set_config` + `COMMIT` for nothing.
- `setTableTxTimeouts` issued three `SET LOCAL` statements; `set_config(…, true)`
  is the same thing and fits in one round trip, as the read guards already do.

A 1000-row page goes from 22 statements to 14, a 50-row page from 15 to 13, and
every write transaction drops two.

* review(tables): drop the delete-mask elision and correct the provenance snapshot doc

Reading the delete job from the table a request already loaded widened a race
the mask probe has always had — a job committing between the check and the row
read is missed either way, but trusting the loaded fields moves the check two
queries earlier. Closing it properly means evaluating the job inside the row
read's own snapshot, which is a larger change than this one, so the elision is
removed and `pending-delete-mask.ts` is back to what it was.

The three remaining reductions are untouched: they were the bulk of the win,
and each is a read this code cannot need rather than a read it takes on faith.

Also updates `TableRowProvenanceReader`'s doc, which still described one
repeatable-read transaction per batch.
…#8107)

Two round trips a table request paid for data it was already carrying:

- `checkAccess` — the gate every raw `/api/table/**` route shares — resolved the
  caller's workspace access, then asked the capability check for `tables.use`
  without telling it which organization owns the workspace. The resolver looks
  that up itself when it is omitted, so the workspace was read twice per
  request. It now goes through `checkWorkspaceAccess`, which `getUserEntityPermissions`
  already delegates to, and passes the organization it hands back. Same single
  resolution, one fewer read, on views, dispatches, export, import, columns,
  metadata and the rest.
- An appending insert asked for `max(order_key)` and `max(position)` as two
  statements, both inside the row-order advisory lock every other inserting
  request is queued behind. Postgres plans each `max()` as its own InitPlan, so
  one statement still serves each from its own index — the same two index-only
  backward scans, in one round trip instead of two.

Statements per request: views 7→6, dispatches 8→7, export 14→13, insert 14→13.
… exactly (#8106)

* fix(knowledge): walk a large bounded set on the row before ranking it exactly

A member reading most of a large source, with that source selected as a filter, enumerated a
bounded permitted set of tens of thousands of documents and then ranked every chunk of it
exactly on both legs: the vector leg read every chunk's projected vector, and the keyword leg
materialized every chunk of the set before it matched the term. Cold, each leg outran its budget
and the search returned nothing.

A bounded set past a size limit is now walked on the row first, where the plan's source and ACL
decide readability and the walk stops at its tuple cap, and ranked exactly only when the walk
cannot fill its pool, so recall is never below the exact ranking's. The keyword leg treats the
same set as a narrow on-row reader: Tin windows where Tin serves, otherwise the GIN shape whose
cost follows the term's matches. Sets under the limit keep their exact paths.

* fix(knowledge): refill a large bounded set's pool with the exact ranking once hydration runs it short

The walk decides readability on the projection row, which is broader than the document predicate hydration applies, so a pool the walk filled can still run short of readable rows. The refill for a large bounded set is now the exact ranking, complete over the set, placed behind the rows already read so the pages keep their offsets.

* fix(knowledge): rank a large bounded set's refill past the rows already read

The refill's exact ranking excludes the chunks the pool already holds inside the statement, so every refill is a full window of fresh rows rather than a window thinned by the rows the walk found first.

* fix(knowledge): hand a large bounded set's exhausted Tin windows to the GIN ranking

A narrow reader's page is left short by design once the widest window cannot fill it; a large bounded set's read was exhaustive before, so its widest window that still falls short now hands the page to the GIN ranking, which covers every match.

* fix(knowledge): hand only a large bounded set's first page to the GIN ranking

Tin and GIN order candidates differently, so an offset advanced through one ranking cannot resume the other. A large bounded set's first page that Tin's widest window cannot fill goes to GIN; a later page stays with Tin and is left short as a narrow reader's is.
@vercel

vercel Bot commented Sep 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs Skipped Skipped Sep 21, 2026 5:58pm UTC

Request Review

@greptile-apps

greptile-apps Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The PR is not yet safe to merge because large bounded vector searches can return a less relevant result set without running the exhaustive ranking that previously covered the caller’s complete scope.

Findings

  1. P1 **Bounded ranking loses results**

Summary

This PR reduces database round trips in knowledge and table operations, moves search metadata authorization into result hydration, changes large bounded knowledge searches to projection-first ranking, and updates search indexes and migrations.

  • Adds deadline-aware memo reads and broader projection-first vector/keyword planning.
  • Combines table protocol operations and avoids unnecessary execution-sidecar reads.
  • Drops unused binary ANN indexes and adds a document index for the Tin keyword projection.
  • One bounded-vector-search path can skip its exhaustive ranking based on the fullness of a broader global walk, causing relevant permitted results to be omitted.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Resolve caller's bounded document set] --> B{At least 5,000 documents?}
  B -- No --> C[Rank bounded set exactly]
  B -- Yes --> D[Walk global ANN projection using broader on-row predicate]
  D --> E{Global candidate pool full?}
  E -- No --> C
  E -- Yes --> F[Hydrate candidates under full document predicate]
  F --> G{topK hydrated results reached?}
  G -- Yes --> H[Return approximate walk results]
  G -- No --> I[Request larger pool and append exact ranking]
  H -. missing path .-> C
Loading

Reviews (1) · Last reviewed commit: "fix(knowledge): walk a large bounded set..."

Comment thread apps/sim/lib/knowledge/search/queries.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 35 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Fix all with cubic | Re-trigger cubic

Comment thread packages/db/schema.ts
Comment thread apps/sim/lib/knowledge/search/queries.ts
Comment thread apps/sim/lib/knowledge/search/queries.ts
@waleedlatif1
waleedlatif1 merged commit 9d00669 into main Sep 21, 2026
58 of 59 checks passed

This branch was previously deployed

1 inactive deployment
Preview d2a4e47d Deployed Sep 21, 2026 by vercel[bot]
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.

1 participant