ragleap-graph v0.6.7
Fixed
Real, confirmed concurrency bug (closes #183): concurrent upsert_document() calls with identical document_id/user_id/namespace could create duplicate Document/Entity/PairWeight/RelationWeight nodes. MERGE on multiple plain properties is not atomic against concurrent writers without a uniqueness constraint, and Neo4j Community Edition only supports single-property uniqueness constraints (no composite constraints, unlike Enterprise Edition). Reproduced live before fixing: 8 duplicate Document nodes from 6 'successful' concurrent upserts of the same document.
Fix: a single hashed composite_key property (SHA256 over each label's real identity fields) is now MERGE'd on instead of the multiple plain properties, backed by a single-property (Community-compatible) uniqueness constraint created idempotently on every GraphIndex connect. Existing plain properties remain unchanged - every existing read/query keeps working.
Added retry-with-backoff for Neo4j transient deadlocks (documented by Neo4j as expected/retryable under concurrency, not corruption). Added backfill_composite_key() - a one-time, idempotent migration for pre-existing data, matching v0.6.5's backfill_user_id_defaults() pattern.
Verified
Live regression test (8-16 concurrent threads against real Neo4j, merged into the permanent suite): real deadlocks directly observed and transparently recovered via the retry logic - zero duplicates across dozens of runs, including a 16-thread stress test where 3 real deadlocks fired mid-run and all recovered.
The migration was live-verified end-to-end against a real legacy-style node.
Full suite: 88 passed, 1 skipped (unrelated, no GEMINI_API_KEY) - zero regressions.
Known residual gap: RelationWeight's composite_key fix uses the identical pattern as PairWeight and passes the full suite, but is not independently concurrency-stress-tested (requires an LLM-based relation extractor not configured in this test environment). Documented honestly, not silently claimed as fully proven.
Published to PyPI, verified via the version-specific JSON endpoint.
See CHANGELOG.md for full detail.