How the Auto-Improvement Scheduler Handles Concurrent Projects Without Blocking in ai-memory
The auto-improvement scheduler achieves non-blocking concurrency by scoping all state to the (workspace_id, project_id) pair and using an atomic claim table that isolates session processing across projects.
The ai-memory repository implements a background auto-improvement system that reviews completed sessions and generates learning proposals. To handle multiple projects simultaneously without global locks or cross-project interference, the scheduler employs a lightweight, database-backed coordination mechanism. This design allows concurrent processing while maintaining exactly-once semantics per session.
Per-Project State Isolation via Watermark Tracking
Each project maintains independent progress tracking through the auto_improve_scheduler_state table. A single row per project stores a watermark_ended_at timestamp representing the latest processed session end time. This watermark is strictly scoped to the (workspace_id, project_id) pair, ensuring that projects advance at their own pace without coordination overhead.
When the scheduler runs, it queries auto_improve_scheduler_state for the current watermark, then selects any session whose ended_at is newer than that watermark within the same workspace and project scope. According to the implementation in crates/ai-memory-store/src/reader.rs at line 2708, this query ensures each project advances independently of others.
Atomic Session Claims for Lock-Free Coordination
Before processing a session, the scheduler attempts to claim it atomically. The function claim_auto_improve_scheduler_session in crates/ai-memory-store/src/writer.rs (lines 1611–1615) handles this coordination without explicit locks.
The Insert-or-Ignore Pattern
The claim mechanism uses an INSERT OR IGNORE ... SELECT 1 FROM auto_improve_scheduler_state ... query to insert a row into auto_improve_scheduler_claims. This creates a unique entry keyed by (workspace_id, project_id, session_id). Only the first concurrent caller succeeds; subsequent attempts see the existing claim and skip the session. This atomic operation eliminates blocking waits, limiting contention to a cheap, scoped INSERT.
Idempotence Across Restarts
Because claim rows persist in the SQLite database, a scheduler that crashes or restarts will re-read the auto_improve_scheduler_claims table and avoid re-processing sessions. The watermark advances only after successful completion, guaranteeing exactly-once semantics per project regardless of process restarts. The initialization logic in ensure_auto_improve_scheduler_state and the claim function are implemented in writer.rs between lines 1593 and 1615.
Database-Level Integrity Enforcement
SQLite triggers enforce strict scoping to prevent cross-project contamination. The trigger auto_improve_scheduler_claims_session_pairing_ai, defined in migration V22__auto_improve_scheduler.sql (lines 31–36), validates that every claim's workspace_id and project_id match the associated session's scope. This database constraint ensures that even erroneous application code cannot accidentally claim a session for the wrong project.
Configuration and Operation
The scheduler operates as a background job that iterates over projects, applying the claim-and-process pattern independently to each. It can be triggered via the CLI command memory_auto_improve or launched automatically by the server when enabled.
Configuration is controlled via TOML:
[auto_improve.scheduler]
enabled = true # Turn the background reviewer on/off
interval_seconds = 300 # How often the scheduler wakes up
The core scheduling loop resides in crates/ai-memory-consolidate/src/auto_improve_schedule.rs, which orchestrates the candidate selection, claiming, and review invocation.
Practical Implementation Example
To interact with the scheduler programmatically:
// Ensure the scheduler state exists for a specific project
store
.ensure_auto_improve_scheduler_state(workspace_id, project_id)
.await?;
// Attempt to atomically claim a session for processing
let claimed = store
.claim_auto_improve_scheduler_session(
workspace_id,
project_id,
session_id,
session_ended_at,
)
.await?;
if claimed {
// This instance owns the session – run the reviewer and stage proposals
} else {
// Another instance already claimed this session; skip it
}
The data model definitions in crates/ai-memory-store/src/auto_improve.rs describe the scheduler tables and their relationships, while crates/ai-memory-store/src/ops.rs contains migration helpers for workspace ID changes that maintain scheduler state integrity.
Summary
- Per-project watermarks in
auto_improve_scheduler_stateisolate progress tracking to(workspace_id, project_id)pairs, preventing cross-project blocking. - Atomic claims using
INSERT OR IGNOREonauto_improve_scheduler_claimsenable lock-free coordination where only the first caller processes a session. - Exactly-once semantics are guaranteed by persisting claims in SQLite and advancing watermarks only after successful processing, surviving crashes and restarts.
- Database triggers enforce scope integrity, ensuring claims cannot leak across projects even in the presence of application bugs.
- No global locks exist; the scheduler iterates over projects independently, with contention limited to cheap, scoped inserts on the claims table.
Frequently Asked Questions
How does the auto-improvement scheduler prevent two workers from processing the same session?
The scheduler uses the claim_auto_improve_scheduler_session function in writer.rs to perform an atomic INSERT OR IGNORE into the auto_improve_scheduler_claims table. This creates a unique row keyed by (workspace_id, project_id, session_id). Only the first concurrent caller succeeds; subsequent attempts detect the existing row and skip the session, ensuring exclusive processing without explicit locks.
What happens if the scheduler process crashes while processing a session?
Because claim rows persist in the SQLite database, a restarted scheduler re-reads the auto_improve_scheduler_claims table on startup and recognizes sessions already marked as claimed. The watermark_ended_at timestamp is only updated after successful processing completes, so incomplete work is automatically picked up where it left off, maintaining exactly-once semantics.
Is there a global lock that blocks all projects during auto-improvement scheduling?
No. The design explicitly avoids global locks by scoping all scheduler state to individual projects via the (workspace_id, project_id) pair. Each project maintains its own watermark and claim set. The background job simply iterates through projects and processes them independently, so activity on one project never blocks or interferes with another.
How does the system prevent a session from being claimed by the wrong project?
SQLite triggers enforce this at the database level. The trigger auto_improve_scheduler_claims_session_pairing_ai in migration V22__auto_improve_scheduler.sql validates that every claim's workspace_id and project_id columns match the corresponding session's scope. If a claim attempt violates this constraint, the database rejects the insert, preventing cross-project data leakage.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →