You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Issue #55 covers parallelizing analyzer work while keeping backend writes serial. That is the correct first boundary, but it leaves a second performance question open: whether Codira can support concurrent backend writes after analysis results are available.
The current backend write contract is centered on one mutable IndexWriteSession for one repository index pass. That shape works for in-process SQLite, DuckDB, and the in-memory contract backend, but it does not expose a safe or portable concurrency model. It also does not map cleanly to future server-backed storage such as PostgreSQL, MySQL, or MariaDB, where independent connections and transactions can make parallel writes practical.
This issue proposes a backend concurrency contract that can support both:
current in-process/file-backed backends, which may decline concurrent writes
future independent-process/server DB backends, which may support concurrent writer transactions
Correctness and deterministic output remain more important than throughput.
Contract updates
Add a write-concurrency capability contract
Add an explicit backend concurrency declaration. The goal is to make concurrency a backend capability, not a hidden property of a storage engine.
The existing IndexWriteSession can remain as the serial baseline. New concurrent-capable backends can implement the staged contract in addition to the existing one.
ID allocation and natural keys
Concurrent backend writes should not depend on session-local integer ID allocation unless the backend can guarantee deterministic allocation under concurrency.
Preferred strategies:
use stable natural keys for file-owned rows where possible
use backend-generated IDs only inside run-scoped tables when final output does not depend on ID order
derive deterministic keys from stable IDs and file identity
postpone derived graph row construction until finalization when needed
Deterministic finalization contract
Even if per-file writes are concurrent, finalization must be deterministic.
Finalization should own:
duplicate stable-ID validation across the run
derived call/reference/include index rebuild
runtime inventory persistence
analyzer/backend inventory persistence
activation of the staged run
deletion of stale active rows
cleanup of abandoned run rows
Configuration updates
Add backend write-concurrency configuration separate from analyzer concurrency.
current _SQLiteIndexWriteSession owns one shared sqlite3.Connection
session state includes completion flags, _embedding_backend, and _pending_embedding_rows
per-file shared-connection persistence uses a fixed savepoint name
commit flushes session-level pending embeddings once
full rebuild currently clears/replaces active index state rather than staging a new run
Possible future SQLite improvements:
retain serial writes as the default
optionally add a staged-run model in the same SQLite file
use one writer connection only; SQLite generally serializes writes anyway
allow concurrent readers only if active-run isolation is implemented
SQLite should not be the first target for backend write parallelism.
DuckDB backend
Recommended initial declaration:
supports_concurrent_file_writes = False
supports_staged_index_runs = False
supports_atomic_run_activation = False
supports_concurrent_readers_during_index = False
default_writer_strategy = "serial"
max_recommended_writers = 1
Technical reasons:
current full rebuild path rolls back, closes the connection, unlinks the DB/WAL files, reinitializes the DB, opens a new connection, and starts a transaction
current session owns an ID allocator
current session owns structural row buffers
current session owns pending embedding/import/docstring/reference/call buffers
DuckDB shared-connection path does not use SQLite-style savepoints and has special transaction handling
Possible future DuckDB improvements:
avoid file deletion during full rebuild by using staged run tables
avoid session-local ID allocation for concurrent rows
move structural/index rebuild into deterministic finalization
evaluate whether multiple writer connections are actually beneficial for the DuckDB driver and workload
DuckDB may benefit more from staged-run correctness improvements than from concurrent writes.
Memory backend
Recommended initial declaration:
supports_concurrent_file_writes = False
supports_staged_index_runs = False
supports_atomic_run_activation = False
supports_concurrent_readers_during_index = False
default_writer_strategy = "serial"
max_recommended_writers = 1
Technical reasons:
current memory backend is root-scoped mutable Python state
no locking or copy-on-write isolation exists
it is used for deterministic contract validation, not production concurrency
Possible future improvements:
implement a locked variant only if needed for contract tests
use staged immutable snapshots for deterministic comparison tests
keep production concurrency validation focused on real backend plugins
Future independent-process backend caveats
Server DB backends such as PostgreSQL, MySQL, and MariaDB can make concurrent writes practical, but they do not remove the need for a strict Codira contract.
PostgreSQL
Likely strongest fit for concurrent backend writes:
robust transactional DDL/DML behavior
row-level locking
INSERT ... ON CONFLICT
advisory locks if needed for one active index run per root
good support for staging tables and atomic activation
Caveats:
choose isolation level explicitly
avoid deadlocks from inconsistent row ordering
validate deterministic final results independent of writer completion order
handle connection pool sizing and server backpressure
MySQL / MariaDB
Potentially feasible with InnoDB, but contract must account for engine differences.
Caveats:
transaction behavior depends on storage engine
INSERT ... ON DUPLICATE KEY UPDATE semantics differ from PostgreSQL upsert
gap locks and isolation behavior may surprise concurrent file writes
DDL and metadata operations may not behave like PostgreSQL
final activation and cleanup must be tested under the target engine
Shared requirements for server DB backends
Server DB backends must define:
root/repository identity mapping
one active index run per root, or explicit multi-run arbitration
connection pool ownership
transaction scope
retry policy for serialization failures/deadlocks
isolation level
run activation semantics
cleanup of abandoned runs
reader visibility during indexing
migration/current-schema policy
Implementation strategy
Phase 1: Add declarations and fail-closed configuration
backend package READMEs: declare current SQLite/DuckDB serial-only behavior.
Docs must state:
analyzer concurrency and backend write concurrency are separate concerns
current SQLite, DuckDB, and memory backends are serial-write only
server DB backends can support concurrent writes only with staged run semantics and deterministic finalization
concurrent backend writes must not expose partial indexes to readers
performance tuning must not weaken deterministic output
Validation commands
Required validation for a contract/config-only PR:
uv run codira caps --json
uv run codira config init --full --output <tmp-config>
uv run codira config validate --path <tmp-config> --json
uv run pytest -q tests/test_contracts.py tests/test_incremental_indexing.py tests/test_memory_backend.py
uv run pytest -q packages/codira-backend-sqlite/tests/test_sqlite_backend_package.py
uv run pytest -q packages/codira-backend-duckdb/tests/test_duckdb_backend_package.py
uv run pre-commit run --all-files
Required validation for any backend behavior change:
uv run codira index --full --backend-concurrency serial
uv run codira index --full --backend-concurrency auto --backend-writers 1
uv run pytest -q
uv run pre-commit run --all-files
For future server DB plugins, add backend-specific integration tests with a disposable database service.
Acceptance criteria
Backend write-concurrency support is explicit in contracts and caps --json.
Current SQLite, DuckDB, and memory backends declare serial-only support.
Unsupported concurrent backend modes fail before mutating backend state.
Persistent config and CLI controls exist for backend write concurrency.
Serial backend behavior remains unchanged.
A staged-run contract is documented and testable.
Tests prove deterministic finalization under out-of-order concurrent writes using a fake backend before any production backend adopts concurrency.
Documentation covers current backend limitations and future server DB caveats.
Validation includes config, capabilities, current backend package tests, memory backend contract tests, pre-commit, and full pytest.
Open questions
Should staged-run support become a required capability for any backend that declares concurrent writes?
Should Codira allow concurrent writes without preserving active-reader visibility, or should reader isolation be mandatory?
Should server DB plugins use root-scoped advisory locks to ensure only one active index run per repository root?
Should max_writers be configured globally, per backend, or both?
Should derived graph indexes be rebuilt from staged raw rows serially, or can derived rebuilds also become backend-specific concurrent operations later?
Problem
Issue #55 covers parallelizing analyzer work while keeping backend writes serial. That is the correct first boundary, but it leaves a second performance question open: whether Codira can support concurrent backend writes after analysis results are available.
The current backend write contract is centered on one mutable
IndexWriteSessionfor one repository index pass. That shape works for in-process SQLite, DuckDB, and the in-memory contract backend, but it does not expose a safe or portable concurrency model. It also does not map cleanly to future server-backed storage such as PostgreSQL, MySQL, or MariaDB, where independent connections and transactions can make parallel writes practical.This issue proposes a backend concurrency contract that can support both:
Correctness and deterministic output remain more important than throughput.
Contract updates
Add a write-concurrency capability contract
Add an explicit backend concurrency declaration. The goal is to make concurrency a backend capability, not a hidden property of a storage engine.
Suggested dataclass:
Add an optional protocol:
Default behavior for undeclared backends:
supports_concurrent_file_writes = Falsesupports_staged_index_runs = Falsesupports_atomic_run_activation = Falsesupports_concurrent_readers_during_index = Falsedefault_writer_strategy = "serial"max_recommended_writers = 1Preserve a single portable contract
Prefer one contract that covers both in-process and server DB backends. Backends should be allowed to implement only the serial subset.
The common contract should distinguish:
Add staged index-run semantics
Concurrent writes are safest if the backend supports staging by index run.
Proposed model:
begin_index_run(root, mode)creates a run identity.index_run_id.This avoids clearing the active index before the replacement index is complete.
Suggested contract additions:
The existing
IndexWriteSessioncan remain as the serial baseline. New concurrent-capable backends can implement the staged contract in addition to the existing one.ID allocation and natural keys
Concurrent backend writes should not depend on session-local integer ID allocation unless the backend can guarantee deterministic allocation under concurrency.
Preferred strategies:
Deterministic finalization contract
Even if per-file writes are concurrent, finalization must be deterministic.
Finalization should own:
Configuration updates
Add backend write-concurrency configuration separate from analyzer concurrency.
Suggested config:
Field semantics:
enabled: defaultfalseinitially.strategy: one of"serial","auto","connection_pool","staged_run".max_writers:1means serial; values greater than 1 require backend support.min_files: minimum file count before concurrent writes are considered.transaction_scope: one of"file","batch","run".staged_runs: one of"off","auto","required".reader_isolation:"preserve_active"means readers continue seeing the old active index until activation.CLI overrides:
Recommended behavior:
--backend-writers 1forces serial persistence.--backend-concurrency serialuses the existingIndexWriteSessionpath.--backend-concurrency autouses concurrent writes only when the active backend declares support.--backend-concurrency connection-poolfails fast unless the backend declaressupports_concurrent_file_writes.--backend-concurrency staged-runfails fast unless the backend declares staged run support and atomic activation.Environment overrides, if desired:
Persistent config surfaces to update:
codira config init --fullcodira config validate --jsonCurrent backend implementation changes
SQLite backend
Recommended initial declaration:
supports_concurrent_file_writes = Falsesupports_staged_index_runs = Falsesupports_atomic_run_activation = Falsesupports_concurrent_readers_during_index = Falsedefault_writer_strategy = "serial"max_recommended_writers = 1Technical reasons:
_SQLiteIndexWriteSessionowns one sharedsqlite3.Connection_embedding_backend, and_pending_embedding_rowsPossible future SQLite improvements:
SQLite should not be the first target for backend write parallelism.
DuckDB backend
Recommended initial declaration:
supports_concurrent_file_writes = Falsesupports_staged_index_runs = Falsesupports_atomic_run_activation = Falsesupports_concurrent_readers_during_index = Falsedefault_writer_strategy = "serial"max_recommended_writers = 1Technical reasons:
Possible future DuckDB improvements:
DuckDB may benefit more from staged-run correctness improvements than from concurrent writes.
Memory backend
Recommended initial declaration:
supports_concurrent_file_writes = Falsesupports_staged_index_runs = Falsesupports_atomic_run_activation = Falsesupports_concurrent_readers_during_index = Falsedefault_writer_strategy = "serial"max_recommended_writers = 1Technical reasons:
Possible future improvements:
Future independent-process backend caveats
Server DB backends such as PostgreSQL, MySQL, and MariaDB can make concurrent writes practical, but they do not remove the need for a strict Codira contract.
PostgreSQL
Likely strongest fit for concurrent backend writes:
INSERT ... ON CONFLICTCaveats:
MySQL / MariaDB
Potentially feasible with InnoDB, but contract must account for engine differences.
Caveats:
INSERT ... ON DUPLICATE KEY UPDATEsemantics differ from PostgreSQL upsertShared requirements for server DB backends
Server DB backends must define:
Implementation strategy
Phase 1: Add declarations and fail-closed configuration
codira caps --json.This phase changes contracts and documentation without changing persistence behavior.
Phase 2: Refactor index persistence into strategy objects
Introduce a core persistence strategy boundary:
The serial strategy should remain byte-for-byte behaviorally equivalent.
Phase 3: Add staged-run contract tests with a fake backend
Before modifying SQLite or DuckDB, create a deterministic fake backend implementing staged concurrent writes.
Tests should prove:
Phase 4: Consider a real backend target
Choose one implementation target after benchmarks show DB persistence is a bottleneck.
Recommended order:
Phase 5: Benchmark and tune
Benchmark serial persistence vs concurrent persistence with:
max_writers = 1max_writers = 2max_writers = 4Measure:
Verification
Static verification
Add contract checks for declarations:
caps --jsonreports serial-only current backendsDynamic verification
Required properties:
Performance verification
Add benchmarks or benchmark scripts that report:
Performance tests should not replace correctness tests.
Tests to add
Unit tests
caps --json[index.backend_concurrency]max_writers,strategy,transaction_scope, andstaged_runsIndexWriteSessionContract tests
SQLite tests
--backend-concurrency autostays serial--backend-concurrency connection-poolfails fast--backend-writers 2fails fast unless strategy is serial-compatibleDuckDB tests
Memory backend tests
Future PostgreSQL/MySQL/MariaDB tests
For any future server backend:
Documentation updates
Update developer and user documentation:
docs/configuration.md: document[index.backend_concurrency]and CLI/env precedence.caps --json.IndexWriteSessionvs staged concurrent backend writes.Docs must state:
Validation commands
Required validation for a contract/config-only PR:
Required validation for any backend behavior change:
For future server DB plugins, add backend-specific integration tests with a disposable database service.
Acceptance criteria
caps --json.Open questions
max_writersbe configured globally, per backend, or both?