Skip to content

docs(scale): correct F1 — table-facts RPC slowness is a generic plan + verified one-line fix#391

Merged
BigSimmo merged 2 commits into
mainfrom
claude/scale-f1-plan-cache
Jul 8, 2026
Merged

docs(scale): correct F1 — table-facts RPC slowness is a generic plan + verified one-line fix#391
BigSimmo merged 2 commits into
mainfrom
claude/scale-f1-plan-cache

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Corrects the F1 finding in docs/scale-readiness-review.md after re-profiling live (read-only). My original F1 diagnosis was wrong, and the fix it recommended (rewrite the fuzzy disjunct to the % operator) is already on live and did not help.

What's actually happening

  • Live match_document_table_facts_text already uses the bounded-CTE + % form. Each arm is fast in isolation (tsv @@ 0.36 ms, normalized_terms && 5.5 ms, trigram % 158 ms), and the full body with a literal query runs in 70 ms.
  • But the RPC call is 5.3 s. Root cause: the function is LANGUAGE sql STABLE with SET search_path → non-inlinable → its body is planned once with an unknown $1, so the planner can't estimate trigram/tsv selectivity and picks near-seq-scan strategies. Every call pays that generic plan.
  • Verified on live: SET plan_cache_mode = force_custom_plan drops it 5.3 s → ~1.1 s (≈4.8×).

The fix (pure performance — results byte-identical, no eval gate)

alter function public.match_document_table_facts_text(text, integer, uuid[], uuid)
  set plan_cache_mode = 'force_custom_plan';

Body-agnostic (targets the function by argument signature), so it does not collide with the separate schema.sql↔live body/return-signature drift on this RPC (that reconciliation belongs to the drift backlog). Same one-liner applies to the other non-inlined hybrid RPCs (memory-cards, index-units, embedding-fields). A full ≈75× fix (→ ~70 ms) needs converting to plpgsql + RETURN QUERY EXECUTE, after the drifted body is reconciled.

Why docs, not a migration

Applying to live needs the linked supabase CLI (operator credentials — not authenticated in this environment) and the migration policy forbids db push on this project's divergent history. The ALTER FUNCTION above is a safe, results-identical, one-line change you can run in the dashboard SQL editor or land as a committed migration when you next apply migrations.

Refs #339
🤖 Generated with Claude Code

… the predicate

Re-profiled live (read-only). The % rewrite this section recommended is ALREADY
on live and did not help: each arm is fast (0.36/5.5/158 ms) and the full body
with a literal runs in 70 ms, but the RPC call is 5.3 s. Root cause: the
function is LANGUAGE sql + SET search_path (non-inlinable), so it runs a generic
plan for an unknown $1 and can't estimate trigram/tsv selectivity.

Fix (pure-performance, results identical, no eval gate):
  alter function public.match_document_table_facts_text(text,integer,uuid[],uuid)
    set plan_cache_mode = 'force_custom_plan';
Verified 5.3 s -> ~1.1 s (4.8x). Same one-liner for the other non-inlined hybrid
RPCs. Full 75x fix = convert to plpgsql + RETURN QUERY EXECUTE (needs the drifted
body reconciled first). Body-agnostic ALTER, so it does not collide with the
separate schema.sql<->live body/return-signature drift on this RPC.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@supabase

supabase Bot commented Jul 8, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@BigSimmo
BigSimmo merged commit 520a333 into main Jul 8, 2026
6 checks passed
@BigSimmo
BigSimmo deleted the claude/scale-f1-plan-cache branch July 8, 2026 16:22
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