@@ -18,10 +18,6 @@ vi.mock('@/lib/workspace-files/search/indexing', () => ({
1818 indexWorkspaceFileForSearch : vi . fn ( ) ,
1919 markWorkspaceFileSearchIndexFailed : vi . fn ( ) ,
2020} ) )
21- vi . mock ( '@/lib/workspace-files/search/index-state' , async ( importOriginal ) => ( {
22- ...( await importOriginal < typeof import ( '@/lib/workspace-files/search/index-state' ) > ( ) ) ,
23- cleanupFileSearchBuilds : vi . fn ( ) . mockResolvedValue ( 0 ) ,
24- } ) )
2521vi . mock ( '@trigger.dev/sdk' , ( ) => ( { tasks : { batchTrigger : mocks . batchTrigger } } ) )
2622vi . mock ( '@/lib/core/config/env-flags' , ( ) => ( { isTriggerDevEnabled : true } ) )
2723vi . mock ( '@/lib/core/async-jobs/region' , ( ) => ( { resolveTriggerRegion : async ( ) => 'us-east-1' } ) )
@@ -39,6 +35,19 @@ import {
3935 prepareWorkspaceFileSearchDispatch ,
4036} from '@/lib/workspace-files/search/dispatcher'
4137
38+ interface QueryPlan {
39+ 'Node Type' : string
40+ 'Relation Name' ?: string
41+ 'Index Name' ?: string
42+ 'Shared Hit Blocks' : number
43+ 'Shared Read Blocks' : number
44+ Plans ?: QueryPlan [ ]
45+ }
46+
47+ function planNodes ( plan : QueryPlan ) : QueryPlan [ ] {
48+ return [ plan , ...( plan . Plans ?? [ ] ) . flatMap ( planNodes ) ]
49+ }
50+
4251describe ( 'workspace file search dispatch PostgreSQL deadlines' , ( ) => {
4352 const schemaName = `dispatch_test_${ generateId ( ) . replaceAll ( '-' , '' ) } `
4453 const databaseUrl = readTestDatabaseUrl ( )
@@ -93,7 +102,8 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => {
93102 ON workspace_file_search_revision
94103 (workspace_id, updated_at, file_id, source_content_updated_at)
95104 WHERE status = 'pending' AND dispatched_at IS NULL`
96- await connection `CREATE INDEX ON workspace_file_search_revision (workspace_id, dispatched_at)
105+ await connection `CREATE INDEX workspace_file_search_revision_active_idx
106+ ON workspace_file_search_revision (dispatched_at, workspace_id)
97107 WHERE status = 'pending' AND dispatched_at IS NOT NULL`
98108 await connection `CREATE TABLE workspace_file_search_build (id text PRIMARY KEY, expires_at timestamp)`
99109 await connection `CREATE INDEX workspace_file_search_build_cleanup_idx
@@ -627,22 +637,39 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => {
627637 await expect ( hasWorkspaceFileSearchDispatchWork ( new Date ( ) ) ) . resolves . toBe ( true )
628638 } )
629639
630- it ( 'probes the cleanup and active-claim indexes rather than scanning ' , async ( ) => {
640+ it ( 'probes builds and claims through their indexes beside a large settled backlog ' , async ( ) => {
631641 await seedIdleDeployment ( )
642+ await connection `INSERT INTO workspace_file_search_revision
643+ (file_id, workspace_id, source_content_updated_at, status)
644+ SELECT 'settled-' || n, 'workspace-' || (n % 50), '2026-09-16', 'ready'
645+ FROM generate_series(1, 100000) n`
646+ await connection `INSERT INTO workspace_file_search_build (id, expires_at)
647+ SELECT 'published-' || n, NULL FROM generate_series(1, 100000) n`
648+ await connection `VACUUM (ANALYZE) workspace_file_search_revision, workspace_file_search_build`
632649 statements . length = 0
633650 await expect ( hasWorkspaceFileSearchDispatchWork ( new Date ( ) ) ) . resolves . toBe ( false )
634651
635652 const probe = statements . find ( ( statement ) => statement . query . includes ( '"hasWork"' ) )
636653 expect ( probe ) . toBeDefined ( )
637- const plan = await connection . begin ( async ( tx ) => {
638- await tx `SET LOCAL enable_seqscan = off`
639- const rows = await tx . unsafe ( `EXPLAIN ${ probe ?. query } ` , probe ?. params as never [ ] )
640- return rows . map ( ( row : Record < string , unknown > ) => row [ 'QUERY PLAN' ] ) . join ( '\n' )
641- } )
642-
643- expect ( plan ) . toContain ( 'workspace_file_search_build_cleanup_idx' )
644- expect ( plan ) . toMatch ( / u s i n g w o r k s p a c e _ f i l e _ s e a r c h _ r e v i s i o n _ w o r k s p a c e _ i d _ d i s p a t c h e d _ a t _ i d x / )
645- expect ( plan ) . not . toMatch ( / S e q S c a n / )
646- } )
654+ const [ explained ] = await connection . unsafe (
655+ `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) ${ probe ?. query } ` ,
656+ probe ?. params as never [ ]
657+ )
658+ const plan = ( explained [ 'QUERY PLAN' ] as { Plan : QueryPlan } [ ] ) [ 0 ] . Plan
659+ const nodes = planNodes ( plan )
660+ const indexes = nodes . map ( ( node ) => node [ 'Index Name' ] )
661+
662+ expect ( indexes ) . toContain ( 'workspace_file_search_build_cleanup_idx' )
663+ expect ( indexes ) . toContain ( 'workspace_file_search_revision_active_idx' )
664+ expect (
665+ nodes . filter (
666+ ( node ) =>
667+ node [ 'Node Type' ] === 'Seq Scan' &&
668+ node [ 'Relation Name' ] !== 'workspace_file_search_dispatch_queue'
669+ )
670+ ) . toEqual ( [ ] )
671+ /** Buffer work, unlike wall-clock time, catches a backlog scan even on a warm local database. */
672+ expect ( plan [ 'Shared Hit Blocks' ] + plan [ 'Shared Read Blocks' ] ) . toBeLessThan ( 100 )
673+ } , 60_000 )
647674 } )
648675} )
0 commit comments