Skip to content

perf(server): keep worktree ensure work and index builds out of the query timeout - #28

Merged
JRedeker merged 1 commit into
mainfrom
work/work-33fe59b853c9bd6785fbc58f
Sep 30, 2026
Merged

JRedeker merged 1 commit into
mainfrom
work/work-33fe59b853c9bd6785fbc58f

Conversation

@JRedeker

Copy link
Copy Markdown
Contributor

Related to LGREP-25

Concord work item: work-33fe59b853c9bd6785fbc58f (workflow.implementation, contract v1).

LGREP-24 evidence: 44/73 provider_timeout calls targeted Concord worktree paths, 45/73 overlapped a cache build, and 6 calls ran 41-71s where the tool-level wait_for never fired — synchronous first-touch LanceDB I/O on the event loop.

Changes

  • Off-loop store assembly (src/lgrep/server/lifecycle.py): the blocking half of _ensure_project_initialized — ChunkStore construction, for_checkout overlay init, write_project_meta, Indexer construction, forced first table touch — now runs as one ensure_store job in the worker pool via a pure-assembly factory (_assemble_project_state). The app lock stays on the loop; concurrent callers are no longer frozen behind first-touch I/O.
  • Base-window budget: _base_index_ready(wait=False) runs the staleness check plus at most one base window bounded by LGREP_ENSURE_BUDGET_S (default 8.0, documented below LGREP_TOOL_TIMEOUT_S). Beyond the budget, or on a non-converging window, the remaining base work continues as the existing single-flight background continuation and the worktree search answers from the partial index.
  • Build lane (src/lgrep/server/runtime.py): RuntimeSupervisor gains a second executor (lgrep-build, sized by LGREP_BUILD_MAX_THREADS, default 1) for build kinds (index_window, compute_pending_files, index_all, startup_orphan_sweep, prune_orphans, prune_symbols, index_repo). Queries keep LGREP_WORKER_MAX_THREADS. Lane is derived from job kind via BUILD_JOB_KINDS (an explicit lane= parameter can override); runtime_job_finished events and job snapshots carry the new lane field; shutdown shuts down both executors.
  • Docs: README env-table rows for LGREP_ENSURE_BUDGET_S and LGREP_BUILD_MAX_THREADS; CHANGELOG entry.

Verification

  • New tests (written against this design): loop stays responsive while a slow store opens; budget overrun defers the base window to the background continuation; a query completes while the build lane is saturated; lane routing/snapshot/event coverage; zero-budget and non-converging-window paths.
  • Red-check: the 23 tests across tests/test_lifecycle_ensure.py, tests/test_worktree_base_budget.py, tests/test_daemon_runtime.py fail (12 failed) against the parent commit and pass (23 passed) with this change.
  • Full suite: 1027 passed, 1 skipped, 1 xfailed.

…e loop; add build lane

Three changes keep worktree ensure work and index builds out of the
query timeout:

- _ensure_project_initialized now assembles the store view, overlay
  init, meta write, indexer, and first table touch inside one
  'ensure_store' job on the worker pool, so first-touch LanceDB I/O on
  a large cache no longer freezes the event loop for every concurrent
  caller.
- A foreground worktree search may spend at most LGREP_ENSURE_BUDGET_S
  (default 8.0s) making its trunk current: beyond the budget the
  remaining base work continues as the existing single-flight
  background continuation and the search answers from the partial
  index.
- RuntimeSupervisor gains a dedicated build lane
  (LGREP_BUILD_MAX_THREADS, default 1) for index windows, pending-file
  computation, full reindexes, and prune sweeps, so builds cannot
  occupy the query threads sized by LGREP_WORKER_MAX_THREADS.
  runtime_job_finished events now carry the lane.

Related to LGREP-25
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