Architecture & Parallel Fast-Track
Deep dive into how pg-smart-search executes queries concurrently and cancels zombie queries. Actual latency depends on your hardware and dataset — see Benchmarks.
Architecture & Parallel Fast-Track
Here is an interactive simulator showing how pg-smart-search processes queries in parallel and intercepts slow search branches via AbortController-driven cancellation:
The core philosophy of pg-smart-search is simple: don't wait for slow strategies if a fast one succeeds.
The initial FTS/standard race is parallel (hedged), not a waterfall. Everything past that first race is a waterfall, though: fuzzy, then layout correction, then transliteration, each only tried if the previous step came back empty. See Smart Hybrid Fallback for that chain.
How it Works
- Concurrent Execution: When a search query is received on the STANDARD tier, the engine triggers FTS and the standard (ILIKE + trigram pre-filter) search. If
ftsColumn(Turbo Mode) is configured, it hedges — starting the standard branch only after a short delay (25ms) or as soon as FTS comes back empty, whichever is first, since FTS is expected to reliably win when it's index-backed. WithoutftsColumn, both start immediately. - First Past the Post: As soon as either branch returns non-empty results, the engine prepares the response and cancels the other.
- Zombie Query Prevention: The engine fires its internal
AbortController, which propagates down to the losing query'sAbortSignal. The adapter maps that to cancelling the underlying PostgreSQL backend (via a dedicatedcancelPoolconnection callingpg_cancel_backend($pid)), freeing the connection and CPU cycles instead of letting the query run to completion for a result nobody reads.
Without Zombie Query Prevention, slow trigram searches would continue
consuming DB resources even after the client received the response.
AbortSignal ensures clean termination. To prevent database connection
starvation (Connection Starvation) under heavy concurrent lookups, the engine
manages cancellation signals through an isolated pool called cancelPool
(size 2-3) reserved exclusively for abort interrupts.
Ranking
FTS relevance uses PostgreSQL's ts_rank (not ts_rank_cd, and not BM25 — ts_rank is a
simpler term-frequency/document-length weighting, cheaper to compute than ts_rank_cd's
positional-proximity variant since it doesn't need lexeme position data). We haven't
published a controlled before/after benchmark for that choice on this codebase — treat any
specific percentage improvement as unverified until someone runs one.
Trigram tiers (STANDARD's pre-filter, ADVANCED) rank using word_similarity() instead.
Index Usage Is Tier-Dependent
- STANDARD uses the
<%trigram operator as an index-accelerated pre-filter (benefiting from a GiST or GINgist_trgm_ops/gin_trgm_opsindex on the search columns), then appliesILIKE/threshold checks on the surviving rows. - ADVANCED also pre-filters via
<%, but ranks with aword_similarity()expression computed per row and sorted withORDER BY ... DESC— Postgres has no index type that can serve that ordering directly (GIN/GiST/RUM included), so every candidate that survives theWHEREfilter gets its relevance computed and sorted in memory. This tier trades a wider index-accelerated pre-filter for reduced row volume, not for an index-served sort — it is not $O(\log N)$ end-to-end, only the pre-filter step is. - VECTOR is the tier that gets genuine index-order KNN sorting:
ORDER BY embedding <=> $1::vectoragainst an HNSW index natively supports nearest-neighbor ordering from the index itself.