mvcc-api 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -89
- package/dist/cjs/index.cjs +343 -242
- package/dist/esm/index.mjs +343 -233
- package/dist/types/core/async/Strategy.d.ts +5 -5
- package/dist/types/core/async/Transaction.d.ts +11 -3
- package/dist/types/core/async/index.d.ts +0 -1
- package/dist/types/core/base/Strategy.d.ts +6 -5
- package/dist/types/core/base/Transaction.d.ts +50 -16
- package/dist/types/core/base/index.d.ts +0 -1
- package/dist/types/core/sync/Strategy.d.ts +5 -5
- package/dist/types/core/sync/Transaction.d.ts +9 -3
- package/dist/types/core/sync/index.d.ts +0 -1
- package/package.json +2 -2
- package/dist/types/core/async/Manager.d.ts +0 -14
- package/dist/types/core/base/Manager.d.ts +0 -58
- package/dist/types/core/sync/Manager.d.ts +0 -11
package/README.md
CHANGED
|
@@ -5,153 +5,148 @@
|
|
|
5
5
|
|
|
6
6
|
Multiversion Concurrency Control (MVCC) API for TypeScript.
|
|
7
7
|
|
|
8
|
-
This library provides a robust framework for implementing Snapshot Isolation (SI) using MVCC. It supports both synchronous and asynchronous operations and
|
|
8
|
+
This library provides a robust framework for implementing Snapshot Isolation (SI) using MVCC. It supports both synchronous and asynchronous operations and features a flexible nested transaction system.
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
12
|
- **MVCC (Multiversion Concurrency Control)**: Provides Snapshot Isolation, allowing readers to not block writers and vice versa.
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **Transaction
|
|
13
|
+
- **Strict Committed-Only Visibility**: Nested transactions only see data that has been globally committed. Uncommitted changes in parent buffers are isolated from child transactions.
|
|
14
|
+
- **Reusable Root Transaction**: The Root transaction serves as a persistent gateway to storage and can be committed multiple times without being closed.
|
|
15
|
+
- **Unified Transaction Architecture**: Root and Nested transactions share the same API, simplifying complex workflows.
|
|
16
|
+
- **Indefinite Nesting**: Create child transactions from any existing transaction with proper conflict detection.
|
|
17
|
+
- **Storage Agnostic**: Implement your own `Strategy` (e.g., File System, In-Memory, Key-Value Store) via the Strategy pattern.
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
18
20
|
|
|
21
|
+
### Node.js
|
|
22
|
+
|
|
19
23
|
```bash
|
|
20
24
|
npm install mvcc-api
|
|
21
25
|
```
|
|
22
26
|
|
|
27
|
+
### ES Module (via CDN)
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import {
|
|
31
|
+
AsyncMVCCTransaction,
|
|
32
|
+
AsyncMVCCStrategy
|
|
33
|
+
} from 'https://cdn.jsdelivr.net/npm/mvcc-api@1/+esm'
|
|
34
|
+
```
|
|
35
|
+
|
|
23
36
|
## Usage
|
|
24
37
|
|
|
25
38
|
### 1. Implement a Strategy
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
Define how data is stored by extending `MVCCStrategy`.
|
|
28
41
|
|
|
29
42
|
```typescript
|
|
30
|
-
import fs from 'node:fs
|
|
43
|
+
import fs from 'node:fs'
|
|
31
44
|
import { AsyncMVCCStrategy } from 'mvcc-api'
|
|
32
45
|
|
|
33
|
-
export class AsyncFileStrategy extends AsyncMVCCStrategy<string> {
|
|
46
|
+
export class AsyncFileStrategy extends AsyncMVCCStrategy<string, string> {
|
|
34
47
|
async read(key: string): Promise<string> {
|
|
35
|
-
return fs.readFile(key, 'utf-8')
|
|
48
|
+
return fs.promises.readFile(key, 'utf-8')
|
|
36
49
|
}
|
|
37
50
|
async write(key: string, value: string): Promise<void> {
|
|
38
|
-
await fs.writeFile(key, value, 'utf-8')
|
|
51
|
+
await fs.promises.writeFile(key, value, 'utf-8')
|
|
39
52
|
}
|
|
40
53
|
async delete(key: string): Promise<void> {
|
|
41
|
-
await fs.unlink(key)
|
|
54
|
+
await fs.promises.unlink(key)
|
|
42
55
|
}
|
|
43
56
|
async exists(key: string): Promise<boolean> {
|
|
44
|
-
|
|
45
|
-
await fs.access(key)
|
|
46
|
-
return true
|
|
47
|
-
} catch {
|
|
48
|
-
return false
|
|
49
|
-
}
|
|
57
|
+
return fs.existsSync(key)
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
```
|
|
53
61
|
|
|
54
62
|
### 2. Run Transactions
|
|
55
63
|
|
|
56
|
-
Initialize
|
|
64
|
+
Initialize a root transaction with your strategy. You can then create nested transactions for isolated work.
|
|
57
65
|
|
|
58
66
|
```typescript
|
|
59
|
-
import {
|
|
60
|
-
import { AsyncFileStrategy } from './AsyncFileStrategy'
|
|
67
|
+
import { AsyncMVCCTransaction } from 'mvcc-api'
|
|
68
|
+
import { AsyncFileStrategy } from './AsyncFileStrategy'
|
|
61
69
|
|
|
62
70
|
async function main() {
|
|
63
71
|
const strategy = new AsyncFileStrategy()
|
|
64
|
-
|
|
72
|
+
// Create a Root Transaction (it can be committed multiple times)
|
|
73
|
+
const root = new AsyncMVCCTransaction(strategy)
|
|
65
74
|
|
|
66
|
-
// Start a
|
|
67
|
-
const tx =
|
|
75
|
+
// Start a Nested Transaction for isolated work
|
|
76
|
+
const tx = root.createNested()
|
|
68
77
|
|
|
69
78
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// Commit changes to storage
|
|
79
|
+
tx.write('data.json', JSON.stringify({ status: 'active' }))
|
|
80
|
+
|
|
81
|
+
// Child sees its own buffer, but parent's uncommitted buffer is hidden
|
|
82
|
+
const data = await tx.read('data.json')
|
|
83
|
+
|
|
84
|
+
// Commit merges changes up to the parent (Root)
|
|
78
85
|
await tx.commit()
|
|
79
|
-
|
|
86
|
+
|
|
87
|
+
// Root must commit to persist to storage
|
|
88
|
+
await root.commit()
|
|
80
89
|
} catch (err) {
|
|
81
|
-
console.error('Transaction failed:', err)
|
|
82
90
|
tx.rollback()
|
|
83
91
|
}
|
|
84
92
|
}
|
|
85
|
-
|
|
86
|
-
main()
|
|
87
93
|
```
|
|
88
94
|
|
|
89
|
-
## Architecture
|
|
95
|
+
## Architecture & Visibility
|
|
90
96
|
|
|
91
|
-
The
|
|
97
|
+
The following diagram illustrates how visibility and data flow work in `mvcc-api`. Child transactions create an immutable snapshot of the **globally committed state** at the moment of their creation.
|
|
92
98
|
|
|
93
99
|
```mermaid
|
|
94
100
|
sequenceDiagram
|
|
95
|
-
participant
|
|
96
|
-
participant
|
|
97
|
-
participant Transaction
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
participant S as Storage (Strategy)
|
|
102
|
+
participant R as Root Transaction
|
|
103
|
+
participant N as Nested Transaction
|
|
104
|
+
|
|
105
|
+
Note over S, R: Global Version 1
|
|
106
|
+
S->>R: Initial State
|
|
107
|
+
R->>R: write('k1', 'v1')
|
|
108
|
+
Note right of R: Buffer: k1=v1 (Uncommitted)
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Note over App, Transaction: Operations
|
|
109
|
-
App->>Transaction: read(key)
|
|
110
|
-
Transaction->>Manager: _diskRead(key, snapshotVersion)
|
|
111
|
-
Manager->>Manager: Check Version Index / Cache
|
|
112
|
-
alt Data in Cache/Index
|
|
113
|
-
Manager-->>Transaction: Return visible version
|
|
114
|
-
else Data in Strategy
|
|
115
|
-
Manager->>Strategy: read(key)
|
|
116
|
-
Strategy-->>Manager: data
|
|
117
|
-
Manager-->>Transaction: data
|
|
110
|
+
rect rgb(240, 240, 240)
|
|
111
|
+
Note over N: Create Nested
|
|
112
|
+
N->>S: read('k1') at v1
|
|
113
|
+
Note right of N: Sees: null (v1 not committed yet)
|
|
118
114
|
end
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
alt Conflict Detected
|
|
128
|
-
Manager-->>App: Throw Error
|
|
129
|
-
else No Config
|
|
130
|
-
Manager->>Strategy: write(key, value)
|
|
131
|
-
Strategy-->>Manager: success
|
|
132
|
-
Manager->>Manager: Update Version Index
|
|
133
|
-
Manager-->>App: Success
|
|
115
|
+
|
|
116
|
+
R->>S: commit()
|
|
117
|
+
Note over S, R: Global Version 2
|
|
118
|
+
|
|
119
|
+
rect rgb(200, 255, 200)
|
|
120
|
+
Note over N2: Create New Nested
|
|
121
|
+
N2->>S: read('k1') at v2
|
|
122
|
+
Note right of N2: Sees: 'v1'
|
|
134
123
|
end
|
|
135
124
|
```
|
|
136
125
|
|
|
126
|
+
### Visibility Rules
|
|
127
|
+
1. **Self-Visibility**: A transaction always sees its own uncommitted changes.
|
|
128
|
+
2. **Strict Isolation**: A child transaction **cannot** see its parent's uncommitted buffer. It only sees data committed to the storage at the time of the child's creation.
|
|
129
|
+
3. **Snapshot Integrity**: Once a child is created, it will never see any subsequent commits made by other transactions (including its parent) until it is closed.
|
|
130
|
+
|
|
137
131
|
## API Reference
|
|
138
132
|
|
|
139
|
-
### `
|
|
140
|
-
- `
|
|
141
|
-
- `
|
|
142
|
-
- `
|
|
143
|
-
- `
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
- `
|
|
153
|
-
- `
|
|
154
|
-
- `
|
|
133
|
+
### `MVCCTransaction<S, K, T>` (Sync/Async)
|
|
134
|
+
- **Constructor**: `new SyncMVCCTransaction(strategy?)` or `new AsyncMVCCTransaction(strategy?)`
|
|
135
|
+
- Pass a `strategy` only for the Root transaction.
|
|
136
|
+
- `read(key: K)`: Reads value from local buffer or globally committed snapshot.
|
|
137
|
+
- `write(key: K, value: T)`: Buffers a write operation.
|
|
138
|
+
- `delete(key: K)`: Buffers a delete operation.
|
|
139
|
+
- `commit()`:
|
|
140
|
+
- **Root**: Persists all buffered changes to storage and resets local buffers. The Root remains open for further operations.
|
|
141
|
+
- **Nested**: Merges changes into the parent's buffer and closes the transaction.
|
|
142
|
+
- `rollback()`: Discards all local changes and closes the transaction (if Nested).
|
|
143
|
+
- `createNested()`: Creates a new child transaction with a snapshot of the current globally committed state.
|
|
144
|
+
|
|
145
|
+
### `MVCCStrategy<K, T>` (Abstract)
|
|
146
|
+
- `read(key: K)`
|
|
147
|
+
- `write(key: K, value: T)`
|
|
148
|
+
- `delete(key: K)`
|
|
149
|
+
- `exists(key: K)`
|
|
155
150
|
|
|
156
151
|
## License
|
|
157
152
|
|