Commit Patterns¶
This document describes application patterns for using the Commit Engine effectively.
CommitStore (Redux-Inspired)¶
CommitStore is the recommended pattern for state management, inspired by Redux:
class CommitStore {
public:
void dispatch(std::string const & label,
std::function<void(std::shared_ptr<AttachmentMutating>const&)> const & function) {
try {
auto const ms {mutableState()};
function(ms);
commitMutations(label, ms);
} catch(Viper::Error const & e) {
notifyDispatchError(e);
}
}
};
The Dispatch Pattern¶
Get mutable state: Isolated working copy
Apply mutations: Call
set(),update(), etc.Commit: Persist mutations as a new Commit
Notify: Inform observers of new state
Dispatch Variants¶
Method |
Purpose |
|---|---|
|
General mutation function |
|
Execute FunctionPool function |
|
Simple document set |
|
Differential update |
|
Path-based update |
State Isolation¶
CommitState (Readonly)¶
Immutable snapshot at a specific commitId:
auto state = db->state(commitId); // std::shared_ptr<CommitState>
auto opt = state->get(attachment, key); // Returns ValueOptional
if (!opt->isNil()) {
auto doc = opt->unwrap(); // Read-only access
}
// state->set(...) // ERROR: CommitState has no set() method
CommitMutableState (Mutable)¶
Mutable working copy for building mutations:
auto state = db->state(commitId);
auto mutable_ = CommitMutableState::make(state); // Wraps CommitState
mutable_->set(attachment, key, document); // Accumulates mutations
mutable_->update(attachment, key, path, value);
// Later: db->commitMutations("label", mutable_);
Two-Level Read¶
CommitMutableState wraps a CommitState:
+------------------------------------------------------------+
| CommitMutableState |
| +-------------+ +-----------------------------+ |
| | mutations | | CommitState | |
| | (pending) | | (immutable, cached) | |
| +-------------+ +-----------------------------+ |
| | | |
| hasDocumentSet? _get(ccKey) |
| | | |
| YES: Skip CommitState NO: Apply mutations on result |
+------------------------------------------------------------+
XArray: CRDT Pattern¶
XArray provides CRDT-like behavior for ordered lists with concurrent editing.
The Problem with Integer Indices¶
Traditional arrays fail with concurrent insertions:
Base state: [a, b, c]
Client A: insert X at index 1 -> [a, X, b, c]
Client B: insert Y at index 1 -> [a, Y, b, c]
Merge: AMBIGUOUS! Which element is at index 1?
The Solution: UUID Positions¶
XArray uses UUID-based positions instead of indices:
std::vector<UUId> _positions; // Ordered by insertion
std::set<UUId> _disablePositions; // Tombstones
std::map<UUId, std::shared_ptr<Value>> _elements;
Concurrent Insertions Coexist¶
Base state: [pos1, pos2, END]
Client A: insert newPosA BEFORE pos2
Client B: insert newPosB BEFORE pos2
Merge: [pos1, newPosA, newPosB, pos2, END]
Both elements coexist. Deterministic ordering.
Tombstoning¶
Tombstones are created when a commit containing XArray_Insert is disabled:
void ignore(opcode, result) { // Called for DISABLED commits
if (opcode->type == XArray_Insert) {
xarray->insertPosition(before, pos); // Insert position
xarray->disablePosition(pos); // Then TOMBSTONE it
}
}
This ensures:
Position ordering preserved for disabled commits
Updates to tombstoned positions silently ignored
Disabled work cannot be “resurrected”
Undo/Redo Pattern¶
Commit Engine provides tree-based undo/redo via Disable/Enable:
Timeline:
A -> B -> C -> D (current)
Undo B:
A -> B -> C -> D -> Disable(B)
State at Disable(B):
- B's mutations are skipped during evaluation
- C, D mutations still apply (if paths exist)
Redo B:
A -> B -> C -> D -> Disable(B) -> Enable(B)
CommitUndoStack¶
class CommitUndoStack {
// Stack of (commitId, disableCommitId) pairs
void undo(); // Disable the top commit
void redo(); // Enable the disabled commit
};
Synchronization Pattern¶
CommitSynchronizer enables synchronization between databases:
1. Fetch: Unknown source commits/blobs -> add to target
2. Push: Unknown target commits/blobs -> add to source
Incremental Transfer¶
Commits added via topological sort (parents first)
Small blobs packed together to reduce RPC traffic
Definitions merged before commits
Value Reconstruction Optimization¶
Two-Phase Algorithm¶
When reading a document via CommitState::get():
evalActions: [A0] [A1] [A2] [A3] [A4] [A5]
^DocumentSet
Forward Scan ----------------->
i=0 i=1 i=2 (found!)
Backward Sweep <---------------
i=0 <- i=1 <- i=2
Result: Apply opcodes from A0, A1, A2 only
Document_Set establishes a baseline, making later opcodes irrelevant.
Transparent Memoization¶
CommitState caches reconstructed values:
Aspect |
Details |
|---|---|
Cache Key |
|
Invalidation |
None required - |
Statistics |
|
Concurrent Operations Matrix¶
Type |
Concurrent Operations |
Outcome |
|---|---|---|
Document |
|
Last evaluated wins |
Document |
|
Both apply (disjoint) |
Document |
|
Last evaluated wins |
Set |
|
Union of unions |
Set |
|
Order-dependent |
Map |
|
Both apply if k1 != k2 |
Map |
|
Last evaluated wins |
XArray |
|
Both coexist (UUID) |
XArray |
|
Order-dependent |
See Also¶
The Dual-Layer Contract - Obligations
Commit Overview - Architecture
Commit Glossary - Terminology