docs(8.0): consistency-model concept + snapshots guide — Db API replaces branching docs

This commit is contained in:
David Snelling 2026-06-11 08:37:26 -07:00
parent e5feae4104
commit cc8037db10
23 changed files with 1053 additions and 1871 deletions

View file

@ -1,3 +1,16 @@
---
title: Transactions & Atomicity
slug: guides/transactions
public: true
category: guides
template: guide
order: 10
description: How Brainy keeps every write atomic — automatic per-operation transactions with rollback, and brain.transact() for atomic multi-write batches with compare-and-swap.
next:
- concepts/consistency-model
- guides/optimistic-concurrency
---
# Transaction System
**Status:** ✅ Production Ready
@ -11,18 +24,18 @@ Brainy's transaction system provides **atomic operations** with automatic rollba
- **Atomicity**: All operations succeed or all rollback
- **Consistency**: Indexes and storage remain consistent
- **Automatic**: Transparently used by all `brain.add()`, `brain.update()`, `brain.delete()`, and `brain.relate()` operations
- **Compatible**: Works seamlessly with COW, sharding, and type-aware storage
- **Composable**: `brain.transact()` runs a multi-write batch through the same machinery as exactly one atomic commit
## Architecture
```
User Code (brain.add(), brain.update(), etc.)
User Code (brain.add(), brain.update(), brain.transact(), etc.)
Transaction Manager (orchestration)
Operations (SaveNounMetadataOperation, SaveNounOperation, etc.)
Storage Adapter (COW, sharding, type-aware routing)
Storage Adapter (sharding, ID-first routing)
```
### How It Works
@ -74,31 +87,41 @@ class SaveNounMetadataOperation {
## Compatibility with Advanced Features
### COW (Copy-on-Write)
### Multi-Write Batches: `brain.transact()`
✅ **Fully Compatible**
✅ **The 8.0 path for atomic multi-entity writes**
Transactions work transparently with COW branches:
Single-operation methods each commit their own transaction. When several
writes must succeed or fail **together**, use `brain.transact()` — a
declarative batch that commits as exactly one generation, with optional
whole-store compare-and-swap and durable transaction metadata:
```typescript
// Create branch
await brain.cow.createBranch('feature-branch')
await brain.cow.checkout('feature-branch')
const db = await brain.transact([
{ op: 'add', id: orderId, type: NounType.Document, subtype: 'order', data: 'Order #1042' },
{ op: 'update', id: customerId, metadata: { lastOrderAt: Date.now() }, ifRev: customer._rev },
{ op: 'relate', from: customerId, to: orderId, type: VerbType.Creates, subtype: 'purchase' }
], { meta: { author: 'order-service' } })
// Add entity (uses transaction on this branch)
const id = await brain.add({
data: { name: 'Feature Entity' },
type: NounType.Thing
})
// On rollback: Branch remains clean, no partial commits
db.receipt.ids // resolved id per operation, in input order
```
**How It Works:**
- Transactions use `StorageAdapter` interface
- COW operates at storage layer (refManager, blobStorage, commitLog)
- Branch isolation prevents cross-branch contamination
- Rollback = discard uncommitted changes (COW makes this trivial)
- The batch executes through the same TransactionManager as single
operations, wrapped in the generational commit protocol: before-images are
staged and fsynced first, and the atomic manifest rename is the commit
point — a crash anywhere before it rolls back to the exact
pre-transaction bytes.
- Per-entity `ifRev` and whole-store `ifAtGeneration` provide
compare-and-swap at two granularities; any conflict rejects the entire
batch before anything is staged.
- The returned `Db` is a pinned, snapshot-isolated view of the committed
state.
See the **[consistency model](concepts/consistency-model.md)** for the
full guarantees (snapshot isolation, time travel, snapshots) and
**[Snapshots & Time Travel](guides/snapshots-and-time-travel.md)** for
recipes.
### Sharding
@ -306,37 +329,31 @@ try {
### Transaction Overhead
**MEASURED Performance Impact:**
- Average overhead: ~2-5ms per transaction (measured: `tests/transaction/transaction.bench.ts`)
- Operations per transaction: 2-8 (metadata + data + indexes)
- Rollback cost: ~1-3ms (restore previous state)
**What a transaction costs:**
- A typical single-operation write wraps 2-8 operations (metadata + data + indexes) in one transaction
- The overhead is bookkeeping (operation objects + undo state), not extra I/O on the success path
- Rollback cost is proportional to the operations already applied (each is undone in reverse order)
**Optimization:**
- Operations executed sequentially (not parallel) for consistency
- Rollback only happens on failure (success path is fast)
- Index updates batched within transaction
### Statistics and Monitoring
### Auditing Committed Batches
Every committed `brain.transact()` batch is recorded in the transaction
log, newest first:
```typescript
// Get transaction statistics
const stats = brain.transactionManager?.getStats()
console.log(stats)
// {
// totalTransactions: 1234,
// successfulTransactions: 1200,
// failedTransactions: 34,
// rollbacks: 34,
// averageOperationsPerTransaction: 4.2
// }
await brain.transact(ops, { meta: { author: 'import-job' } })
const entries = await brain.transactionLog({ limit: 10 })
// [{ generation: 1042, timestamp: 1765432100000, meta: { author: 'import-job' } }]
```
**Metrics Available:**
- `totalTransactions`: Total number of transactions executed
- `successfulTransactions`: Number of successful commits
- `failedTransactions`: Number of rollbacks
- `rollbacks`: Total rollback count
- `averageOperationsPerTransaction`: Average operations per transaction
Single-operation writes advance the generation counter but do not append
log entries — see the [consistency model](concepts/consistency-model.md)
for the history-granularity contract.
## Best Practices
@ -376,19 +393,18 @@ if (!isValidVector(vector, brain.dimension)) {
await brain.add({ data, type, vector })
```
### 4. Monitor Transaction Statistics
### 4. Batch Related Writes with `transact()`
```typescript
// ✅ Recommended: Monitor in production
setInterval(() => {
const stats = brain.transactionManager?.getStats()
if (stats) {
const failureRate = stats.failedTransactions / stats.totalTransactions
if (failureRate > 0.05) { // > 5% failure rate
console.warn('High transaction failure rate:', failureRate)
}
}
}, 60000) // Check every minute
// ✅ Recommended: writes that must land together go in one batch
await brain.transact([
{ op: 'add', id: orderId, type: NounType.Document, subtype: 'order', data: 'Order #1042' },
{ op: 'relate', from: customerId, to: orderId, type: VerbType.Creates, subtype: 'purchase' }
])
// ❌ Avoid: sequential single operations when partial application is unacceptable
const id = await brain.add({ ... }) // commits alone
await brain.relate({ ... }) // a crash here leaves the entity unlinked
```
### 5. Understand Atomicity Guarantees
@ -440,15 +456,19 @@ describe('Transaction Tests', () => {
### Integration Tests
See `tests/transaction/integration/` for comprehensive integration tests covering:
- COW integration (`cow-transactions.test.ts`)
- Sharding integration (`sharding-transactions.test.ts`)
- TypeAware integration (`typeaware-transactions.test.ts`)
- Type-aware integration (`typeaware-transactions.test.ts`)
- Distributed scenarios (`distributed-transactions.test.ts`)
The atomicity guarantees of `brain.transact()` — including crash recovery
through the real recovery path — are proven in
`tests/integration/db-mvcc.test.ts`.
## Troubleshooting
### High Rollback Rate
**Symptom:** `failedTransactions` / `totalTransactions` > 5%
**Symptom:** a high share of writes throw and roll back
**Possible Causes:**
1. Invalid vector dimensions
@ -478,21 +498,6 @@ See `tests/transaction/integration/` for comprehensive integration tests coverin
- Disable unused indexes
- Use SSD storage
### Transaction Statistics Missing
**Symptom:** `brain.transactionManager?.getStats()` returns `undefined`
**Cause:** TransactionManager not initialized
**Solution:**
```typescript
// Ensure Brainy is initialized
await brain.init()
// Then access stats
const stats = brain.transactionManager?.getStats()
```
## Architecture Details
### Transaction Lifecycle
@ -545,28 +550,19 @@ interface StorageAdapter {
## Additional Resources
- **Unit Tests:** `tests/transaction/transaction.test.ts` (36 passing tests)
- **Integration Tests:** `tests/transaction/integration/` (35 test scenarios)
- **Compatibility Analysis:** `.strategy/TRANSACTION_COMPATIBILITY_ANALYSIS.md` (internal)
- **Performance Benchmarks:** `tests/transaction/transaction.bench.ts`
## Version History
- Initial transaction system release
- Atomic operations with rollback
- Compatible with COW, sharding, type-aware storage
- 36/36 unit tests passing
- 35 integration test scenarios
- **Unit Tests:** `tests/transaction/Transaction.test.ts`, `tests/transaction/TransactionManager.test.ts`
- **Integration Tests:** `tests/transaction/integration/`
- **MVCC Proofs:** `tests/integration/db-mvcc.test.ts` (atomicity, CAS, crash recovery for `brain.transact()`)
- **Consistency Model:** [docs/concepts/consistency-model.md](concepts/consistency-model.md)
## Summary
Brainy's transaction system provides **production-ready atomic operations** with automatic rollback. It works transparently with all Brainy APIs and is fully compatible with advanced features like COW, sharding, and type-aware storage.
Brainy's transaction system provides **production-ready atomic operations** with automatic rollback. Every single-operation write is transactional out of the box, and `brain.transact()` extends the same guarantee to multi-write batches — one atomic commit, with compare-and-swap and durable transaction metadata.
**Key Takeaways:**
- ✅ **Automatic**: No manual transaction management needed
- ✅ **Atomic**: All operations succeed or all rollback
- ✅ **Automatic**: No manual transaction management needed for single operations
- ✅ **Atomic**: All operations succeed or all rollback — per operation and per `transact()` batch
- ✅ **Compatible**: Works with all storage adapters and features
- ✅ **Production-Ready**: Tested with 71 test scenarios (36 unit + 35 integration)
- ✅ **Performant**: ~2-5ms overhead per transaction (measured)
- ✅ **Coordinated**: Per-entity `ifRev` and whole-store `ifAtGeneration` CAS reject conflicting batches before anything is staged
Start using transactions today - they're already built into `brain.add()`, `brain.update()`, `brain.delete()`, and `brain.relate()`!
Start using transactions today - they're already built into `brain.add()`, `brain.update()`, `brain.delete()`, and `brain.relate()` — and reach for `brain.transact()` whenever several writes must land together.