Backup Flow
The backup operation is orchestrated by theBackupManager in internal/engine/. Here’s the complete flow:
1. Initialization
SourceInfo:
type: e.g. “gdrive”, “local”, “onedrive”identity: stable container identitypath_id: stable selected-root identity within the containeraccount: friendly display account labelpath: friendly display pathdrive_name: friendly container label
type + identity + path_idtype + identity + path(bridge fallback)type + volume_uuid + path(legacy fallback)type + account + path(legacy fallback)
2. Source Scanning
Full scan (forlocal, sftp, gdrive, onedrive):
gdrive-changes, onedrive-changes):
-
Look up the file ID in the old HAMT:
-
Fast-check metadata (name, size, mtime, type, parents):
- If identical and the source doesn’t provide a content hash, carry the old hash forward
- This avoids false-positive diffs for metadata-only changes
-
Determine action:
- Unchanged: Re-insert into new HAMT by reference (structural sharing)
- Changed or new: Queue for upload
3. Upload Phase
Changed/new files are processed by concurrent workers (default: 10 workers):FastCDC Chunking
Frominternal/engine/chunker.go and the spec:
FastCDC uses a rolling hash to find content-defined boundaries:
- Compute a rolling hash of a 64-byte window
- When the hash matches a pattern (e.g. last 20 bits are zero), create a boundary
- Enforce min/max size constraints
- The final chunk may be smaller than the minimum
Content-defined chunking ensures that inserting bytes at the start of a file doesn’t invalidate all subsequent chunks. Only the chunks containing modified data change.
4. HAMT Flush
After all files are uploaded and inserted into the HAMT:- Start a queue with the root node ref
- For each node in the queue:
- If it’s in the in-memory buffer, write it to persistent storage
- If it’s an internal node, add all child refs to the queue
- Discard any buffered nodes that were never visited
5. Snapshot Commit
The commit point is updating
index/latest. Until this write completes, the previous snapshot remains the “latest” and the repository is in a consistent state.6. Lock Release
The backup lock is released, allowing other operations to proceed.Restore Flow
The restore operation is orchestrated by theRestoreManager in internal/engine/.
1. Snapshot Resolution
2. HAMT Traversal
Walk the HAMT to collect all file metadata entries:3. Topological Sort
Ensure parent directories are created before their children:4. Path Reconstruction
Walk the parent chain of each entry to reconstruct the full relative path:For files with multiple parents (Google Drive shared folders), only the first parent is used to construct the primary path. Other parents are ignored during restore.
5. ZIP Archive Creation
Write entries to a ZIP archive in topologically-sorted order:6. Output
The ZIP archive is:- Written to stdout (CLI)
- Returned as a byte stream (web API)
- Saved to a file (with
-oflag)
Performance Optimizations
Concurrent Upload
The backup manager uses a worker pool (default: 10 concurrent workers) to parallelize file uploads:Chunk-Level Deduplication
Before writing a chunk:KeyCacheStore layer caches existence checks in a local bbolt database to avoid redundant Exists calls:
- First check: network request → cache result
- Subsequent checks: local cache hit (0 network requests)
Content-Level Deduplication
Before streaming a file, check if its content object already exists:Packfile Bundling
Small objects (< 512KB) are bundled into 8MB packfiles:- Reduces API calls from thousands to dozens
- LRU cache (128MB) keeps hot packs in memory
- Typical metadata read: 0-1 network requests after initial pack fetch
Structural Sharing
Unchanged files reuse their filemeta refs, and unchanged subtrees reuse their HAMT node refs:Error Handling
Transient Errors
Network errors during upload are retried with exponential backoff:Permanent Errors
Permanent errors (authentication failure, permission denied) abort the backup immediately:Partial Upload Cleanup
If a backup is interrupted, orphaned objects remain in the store but are not reachable from any snapshot. Runningprune after an interrupted backup will:
- Mark all reachable objects from existing snapshots
- Sweep and delete any orphaned objects
- Repack fragmented packfiles
Orphaned objects are harmless. They consume storage but don’t affect backup correctness. Prune reclaims this space.
Diff Operation
Thediff command leverages the HAMT’s structural diff: