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
fix: retry with backoff before wiping the graph on a non-lock-contention open failure
Infigraph::init() correctly avoids wiping the graph when Kuzu's open
error is lock contention ("Could not set lock on file"), since that
just means another live process (a watcher) already has it open. Any
other open error -- including a transient short read from a concurrent
writer mid-checkpoint -- fell straight through to wipe_graph with zero
retry, on the assumption it must be genuine corruption.
This destroyed a real repo's graph tonight: a manual `scip-import` ran
concurrently with a background watcher/MCP-server touching the same
graph file, producing "IO exception: Cannot read from file... 0 bytes"
on the next open -- not a lock message, so it wiped tens of thousands
of symbols that were sitting right there on disk moments earlier.
Retry with backoff (200ms, 500ms, 1s) before giving up. This self-heals
the transient-race case -- including slower writers than a single fixed
retry would tolerate, e.g. a large SCIP import still mid-checkpoint --
while still correctly wiping-and-rebuilding graphs that are durably
corrupt, since retries against those keep failing too. If a retry
attempt comes back as lock contention specifically, stop retrying
immediately and report that instead (now unambiguously another live
process, not a checkpoint race).
Investigated whether a more principled fix exists: the underlying
`lbug` (kuzu) crate has no structured error type to match on instead of
strings -- open failures cross the FFI boundary as an opaque
`cxx::Exception`, so there's no error code to check. Kuzu's own
concurrency docs don't cover retry/corruption-detection at all; their
documented model is strict single-writer, with concurrent-write access
meant to go through one dedicated writer process (the same pattern
already used for the Neo4j backend, which has no retry/wipe logic of
its own since Neo4j handles concurrency server-side). This fix is a
pragmatic safety net given that constraint, not a substitute for
routing writes through a single process -- that's a larger, separate
architectural change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAXDJyFdnA4BV1U2fxufdC
0 commit comments