Skip to content

Commit d0e215d

Browse files
committed
fix(file-search): allow larger concurrent search bursts
1 parent 0853431 commit d0e215d

3 files changed

Lines changed: 23 additions & 5 deletions

File tree

‎apps/sim/lib/workspace-files/search/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Search joins the current file revision and resolved workspace/folder scope. A re
2020

2121
For regular chunks, PostgreSQL checks the pattern with newline-aware semantics, then verifies individual logical lines. Long-line fragments use only necessary three-character literals as a conservative prefilter, including all required alternation branches. Two-code-point overlap preserves those literals at every boundary. PostgreSQL reconstructs the complete candidate line and evaluates the original regex, so anchors, word boundaries, repetitions, and arbitrarily long match spans retain line semantics. Fixed overlap alone is never treated as proof of a match. The supported regex grammar and minimum literal requirement are unchanged.
2222

23-
Regular blocks are verified in batches of at most 16 (128 KiB of indexed text); long lines are reconstructed one at a time. Only bounded match-centered previews leave PostgreSQL: at most 201 rows to detect truncation, and at most 2 KiB per rendered result. A single search has a ten-second application deadline with per-statement guards. PostgreSQL 17 additionally enforces a total transaction timeout; PostgreSQL 16 uses the compatible idle-transaction guard. Transaction advisory locks admit at most two simultaneous searches per workspace and ten globally per database. Busy and timed-out searches fail explicitly; they never report an incomplete scan as an authoritative empty result. The reader uses the normal application database connection, so admission is coordinated on the same database as the index.
23+
Regular blocks are verified in batches of at most 16 (128 KiB of indexed text); long lines are reconstructed one at a time. Only bounded match-centered previews leave PostgreSQL: at most 201 rows to detect truncation, and at most 2 KiB per rendered result. A single search has a ten-second application deadline with per-statement guards. PostgreSQL 17 additionally enforces a total transaction timeout; PostgreSQL 16 uses the compatible idle-transaction guard. Transaction advisory locks admit at most 20 simultaneous searches per workspace and 5,000 globally per database. The application connection pool queues requests when its connections are occupied. These admission ceilings are operational safeguards, not throughput guarantees. Busy and timed-out searches fail explicitly; they never report an incomplete scan as an authoritative empty result. The reader uses the normal application database connection, so admission is coordinated on the same database as the index.
2424

2525
Arbitrary regex cannot have a fixed latency guarantee. Common terms, broad alternatives, and punctuation-only literals may require scanning significant scoped text. Larger capacity decisions need representative query plans and workload measurements; neither a per-file byte cap nor a PostgreSQL row-count claim establishes a total corpus capacity.
2626

‎apps/sim/lib/workspace-files/search/chunks.integration.ts‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ vi.mock('@/lib/file-parsers', () => ({ parseBuffer: vi.fn(), isSupportedFileType
2626
import {
2727
FILE_SEARCH_CLEANUP_BATCH_ROWS,
2828
FILE_SEARCH_CLEANUP_MAX_BATCHES,
29+
FILE_SEARCH_QUERY_GLOBAL_CONCURRENCY,
30+
FILE_SEARCH_QUERY_WORKSPACE_CONCURRENCY,
2931
} from '@/lib/workspace-files/search/constants'
3032
import { prepareWorkspaceFileSearchDispatch } from '@/lib/workspace-files/search/dispatcher'
3133
import {
@@ -429,11 +431,27 @@ describe('chunked workspace file search on PostgreSQL', () => {
429431
)
430432
})
431433

432-
it('releases query admission slots after a busy response', async () => {
434+
it('admits a search up to the workspace and global ceilings', async () => {
433435
const held = await connection.reserve()
434436
try {
435437
await held`BEGIN`
436-
await held`SELECT pg_advisory_xact_lock(hashtextextended('workspace-file-search-read:workspace:workspace-1:' || n::text, 0)) FROM generate_series(1, 2) n`
438+
await held`SELECT pg_advisory_xact_lock(hashtextextended('workspace-file-search-read:workspace:workspace-1:' || n::text, 0)) FROM generate_series(1, ${FILE_SEARCH_QUERY_WORKSPACE_CONCURRENCY - 1}) n`
439+
await held`SELECT pg_advisory_xact_lock(hashtextextended('workspace-file-search-read:global:' || n::text, 0)) FROM generate_series(1, ${FILE_SEARCH_QUERY_GLOBAL_CONCURRENCY - 1}) n`
440+
expect((await search('needle')).results).toEqual([])
441+
} finally {
442+
await held`ROLLBACK`
443+
held.release()
444+
}
445+
})
446+
447+
it.each([
448+
['workspace:workspace-1', FILE_SEARCH_QUERY_WORKSPACE_CONCURRENCY],
449+
['global', FILE_SEARCH_QUERY_GLOBAL_CONCURRENCY],
450+
])('releases query admission slots after %s saturation', async (scope, capacity) => {
451+
const held = await connection.reserve()
452+
try {
453+
await held`BEGIN`
454+
await held`SELECT pg_advisory_xact_lock(hashtextextended('workspace-file-search-read:' || ${scope} || ':' || n::text, 0)) FROM generate_series(1, ${capacity}) n`
437455
await expect(search('needle')).rejects.toThrow('busy')
438456
} finally {
439457
await held`ROLLBACK`

‎apps/sim/lib/workspace-files/search/constants.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ export const FILE_SEARCH_MAX_PREVIEW_BYTES = 2 * 1024
2323
export const FILE_SEARCH_CHUNK_BYTES = 8 * 1024
2424
export const FILE_SEARCH_CANDIDATE_PAGE_SIZE = 16
2525
export const FILE_SEARCH_CANDIDATE_PROBE_SIZE = 256
26-
export const FILE_SEARCH_QUERY_GLOBAL_CONCURRENCY = 10
27-
export const FILE_SEARCH_QUERY_WORKSPACE_CONCURRENCY = 2
26+
export const FILE_SEARCH_QUERY_GLOBAL_CONCURRENCY = 5000
27+
export const FILE_SEARCH_QUERY_WORKSPACE_CONCURRENCY = 20
2828
export const FILE_SEARCH_CANDIDATE_LITERAL_CHARS = 3
2929
export const FILE_SEARCH_BUILD_LEASE_MS = 20 * 60 * 1000
3030
export const FILE_SEARCH_CLEANUP_BATCH_ROWS = 1000

0 commit comments

Comments
 (0)