Services.BackgroundIndexer (fnord v0.8.83)
View SourceOverview
The BackgroundIndexer is a silent, cancellable GenServer that indexes project files one at a time. It generates per-file derivatives (summary, outline, embeddings) and saves them to the project store.
This module is intentionally designed to be both:
- sequential (one file at a time) for predictability and resource control
- promptly cancellable so the parent command (e.g.,
ask) can stop it immediately
Strategy (literate walkthrough)
- We do not pre-queue a large list of files. Instead, we:
- Optionally accept a small explicit
files_queuefrom the caller (mainly used by tests) - Otherwise fetch exactly one stale entry dynamically between tasks
- Optionally accept a small explicit
- Each file is processed in a linked Task so the GenServer stays responsive
- We monitor the Task and, when it finishes, we schedule processing of the next file
- If there is no next file, we stop the server with
:normal - On shutdown, we kill any in-flight Task to guarantee prompt cancellation
Lifecycle checkpoints
init/1: set per-process HTTP pool, determine project & initial files_queue, prime statehandle_continue(:process_next): run the state machine to start the next Task or stophandle_info({:DOWN, ...}): clear task state and schedule the next stepterminate/2: kill in-flight Task and clear HttpPool override
Why one-at-a-time?
- Prevents overwhelming APIs (summaries, outlines, embeddings)
- Simplifies cancellation and error isolation
- Ensures the background indexer does not keep running long after
askcompletes
Summary
Functions
Returns a specification to start this module under a supervisor.
handle_continue(:process_next) operates in these modes
When a monitored Task completes, we receive a :DOWN message. We clear the task state and trigger the next file via {:continue, :process_next}. This guarantees one-at-a-time processing and ensures prompt stop semantics.
init/1 sets up the GenServer state, performing the following steps
Stop the BackgroundIndexer GenServer safely. This function is idempotent, swallows exits, and accepts non-pid values. Always returns :ok.
terminate/2 ensures prompt cancellation and cleanup
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
handle_continue(:process_next) operates in these modes:
- If a Task is already running, do nothing and wait for :DOWN
- If files_queue has entries, pop one and start a Task to process it
- If files_queue is empty but we have a project, fetch one stale entry and start a Task for it; if none remain, stop normally
- If there is no project and no files, stop normally
When a monitored Task completes, we receive a :DOWN message. We clear the task state and trigger the next file via {:continue, :process_next}. This guarantees one-at-a-time processing and ensures prompt stop semantics.
init/1 sets up the GenServer state, performing the following steps:
- Configure HttpPool for AI indexer requests (efficient, reusable connections)
- Determine
projectcontext from opts or Store.get_project/0 - Build
files_queuefrom opts (if provided) or default to [] - Initialize state and immediately continue to :process_next
@spec start_link( opts :: [project: Store.Project.t(), files: [Store.Project.Entry.t()]] ) :: GenServer.on_start()
Stop the BackgroundIndexer GenServer safely. This function is idempotent, swallows exits, and accepts non-pid values. Always returns :ok.
terminate/2 ensures prompt cancellation and cleanup:
- Kills any in-flight Task to stop work immediately
- Clears the HttpPool override for this process