feat(storage): add path locking and selective crash recovery for write operations#431
Merged
feat(storage): add path locking and selective crash recovery for write operations#431
Conversation
…recovery Implement a full transaction system for VikingFS storage operations including write-ahead journal, path locking, undo/rollback, context manager API, and crash recovery. Includes comprehensive tests and documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tions Add end-to-end tests covering rollback scenarios that were missing: - mv rollback: file moved back to original location on failure - mv commit: file persists at new location - Multi-step rollback: mkdir + write + mkdir all reversed in order - Partial step rollback: only completed entries are reversed - Nested directory rollback: child removed before parent - Best-effort rollback: single step failure does not block others Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
Author
|
/review |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
Collaborator
|
related discussion in #472 |
# Conflicts: # openviking/parse/tree_builder.py # openviking/session/session.py
19 tasks
Collaborator
|
关于隔离级别是如何设计的?感觉像是Read Uncommitted? |
Implement transaction system for VikingFS with ACID-like guarantees: - TransactionManager with configurable lock timeout and journal-based recovery - PathLock supporting point, subtree, and mv lock modes - Refactor VikingFS mv to use cp+rm to prevent lock files from being carried - Fix stale lock detection returning false for missing lock files - Update ragas eval to use LangchainLLMWrapper Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reconstruct RequestContext from undo params for vectordb_delete/update_uri rollback (previously skipped silently due to missing ctx) - Serialize ctx fields into undo params in rm/mv operations - Fix Phase 1 undo path to target archive dir instead of session root - Remove Phase 2 fs_write_new undo (overwrites are idempotent, checkpoint handles recovery) - Add ancestor SUBTREE recheck after lock creation in acquire_subtree - Move _collect_uris inside TransactionContext in rm/mv to close race window - Log journal persistence failures instead of silently swallowing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ests with real backends Remove all optional/fallback code paths where tx_manager could be None. get_transaction_manager() now raises RuntimeError if not initialized. Fix undo rollback to reconstruct ctx for vectordb_upsert and use correct agent_id default. Replace mock-based transaction tests with integration tests using real AGFS and VectorDB backends.
…mmit path - Convert execute_rollback/rollback_entry to async, removing sync run_async wrappers - Unify Session.commit() to delegate to commit_async(), removing duplicate phase methods - Fix SUBTREE lock to conflict with ancestor SUBTREE locks (was previously missing) - Fix mv lock mode: directory moves now use SUBTREE on both source and destination - Replace deprecated asyncio.get_event_loop() with get_running_loop() - Remove max_parallel_locks config option - Update docs (en/zh) and tests to match new async rollback signatures
1 task
…sh recovery Session commit no longer wraps archive phase in a transaction. Phase 2 uses redo semantics so crashed memory-extraction can be replayed from archive. PathLock stale-lock cleanup no longer redundantly re-checks timeout. Semantic processor vectorization runs concurrently via asyncio.gather.
…ghtweight lock + redo-log Remove the heavyweight TransactionManager/Journal/UndoEntry system (~4000 lines) and replace it with a simpler architecture: LockManager for path locking, LockContext as the async context manager, LockHandle/LockOwner protocol, and a RedoLog for crash recovery of session_memory operations. VikingFS rm/mv now use inline error handling instead of rollback semantics. Updated docs, observers, and tests accordingly. Co-Authored-By: Claude Opus 4.6
…fy mv lock param - Remove unused _write_checkpoint/_write_checkpoint_async/_read_checkpoint from Session (superseded by redo-log) - Re-resolve URI inside lock in resource_processor Phase 3.5 to prevent concurrent add_resource calls from resolving to the same final_uri - Rename acquire_mv dst_path to dst_parent_path with docstring to clarify that callers pass the destination parent directory
zhoujh01
approved these changes
Mar 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Implement path-level locking and selective crash recovery for VikingFS storage operations. Core write operations (
rm,mv,add_resource,session.commit) are coordinated throughLockManagerwith fencing-token-based path locks and aRedoLogfor session memory crash recovery.Note
This PR went through a major design iteration: the initial undo-based TransactionManager/Journal/UndoLog system (~4000 lines) was replaced with a lightweight LockManager + RedoLog architecture (~700 lines), reducing complexity by ~82%. See commit
4e44a5dfor the refactor rationale.cc @r266-tech
Related Issue
Closes #390
RFC Discussion: #115
Type of Change
Scope & Limitations
Important
This design is for single-node deployments only and does not support multi-node/distributed scenarios.
.path.ovlock)/local/_system/redo/RecoverStaleon startupArchitecture
Overview
Design Decisions
Inline error handling, not rollback: VikingFS
rm/mvuse sequential operations with explicit error cleanup instead of undo-log rollback. Failures are observable, not hidden.Selective crash recovery (RedoLog): Only
session.commitmemory extraction is redo-protected — the one operation where data loss is unacceptable and the operation is idempotent enough to safely replay. General FS operations don't need redo because partial failures are detectable.Fencing tokens for locks: Lock files contain
{owner_id}:{monotonic_ns}:{lock_type}for stale lock detection and ABA prevention.Fail-fast by default:
lock_timeout=0means operations fail immediately on lock conflict. Set to positive value for blocking retries.Lock Design
Two lock types:
Three locking modes (LockContext):
pointadd_resource,session.commitsubtreerm(directories)mvmvLivelock prevention: Deterministic backoff via fencing token timestamp comparison.
Stale lock cleanup: Background loop (every 60s) detects locks held >3600s;
lock_expireconfig (default 300s) enables force-release on acquisition conflict.Per-Command Design
rm()subtree; File:point(parent)mv()mv: SUBTREE(src) + SUBTREE(dst)add_resource(finalize)point(parent dir)session.commitSession Commit — Two-Phase with Redo Protection
If the process crashes during Phase 2, the redo marker persists. On restart,
LockManager.start()scans/local/_system/redo/and replays memory extraction.Crash Recovery
On
LockManager.start():/local/_system/redo/for pending markersChanges Made
lock_manager.py): Global singleton managing lock lifecycle, redo recovery, background stale cleanuplock_context.py): Async context manager for lock acquisition/releaselock_handle.py): Lock ownership tracking with LockOwner protocolpath_lock.py): Fencing-token-based path locks with POINT/SUBTREE types; fixed TOCTOU race in lock acquisitionredo_log.py): Selective crash recovery for session_memory operationslock_observer.py): Monitoring for active/hanging/stale locksrm()/mv()use LockContext with inline error handlingadd_resourcefinalize phase uses point lock on parent directorycommit_async()uses redo-log for memory extraction crash safetyRecoverStaleat-least-once)Recent Fixes
fe33516)add_resourcefinalize now acquires point lock on parent directory (715739d)1010bd4)Testing
Checklist
Additional Notes
4e44a5d), reducing transaction code from ~4000 lines to ~700 lines