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 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 is designed to be storage-agnostic via the Strategy pattern.
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
- - **Sync & Async Support**: Separate `SyncMVCCManager` and `AsyncMVCCManager` for different use cases.
14
- - **Storage Agnostic**: Implement your own `Strategy` (e.g., File System, In-Memory, Key-Value Store) to handle actual data persistence.
15
- - **Transaction Management**: Methods to `create`, `commit`, and `rollback` transactions easily.
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
- First, you need to define how data is stored by extending `MVCCStrategy`. Here is a simple example using Node.js `fs/promises`.
40
+ Define how data is stored by extending `MVCCStrategy`.
28
41
 
29
42
  ```typescript
30
- import fs from 'node:fs/promises'
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
- try {
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 the Manager with your Strategy and start using transactions.
64
+ Initialize a root transaction with your strategy. You can then create nested transactions for isolated work.
57
65
 
58
66
  ```typescript
59
- import { AsyncMVCCManager } from 'mvcc-api'
60
- import { AsyncFileStrategy } from './AsyncFileStrategy' // Your strategy
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
- const db = new AsyncMVCCManager(strategy)
72
+ // Create a Root Transaction (it can be committed multiple times)
73
+ const root = new AsyncMVCCTransaction(strategy)
65
74
 
66
- // Start a transaction
67
- const tx = db.createTransaction()
75
+ // Start a Nested Transaction for isolated work
76
+ const tx = root.createNested()
68
77
 
69
78
  try {
70
- // Write data (buffered in memory)
71
- tx.write('user:1', JSON.stringify({ name: 'Alice', balance: 100 }))
72
-
73
- // Read data (snapshot isolation)
74
- const data = await tx.read('user:1')
75
- console.log('Read within tx:', data)
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
- console.log('Transaction committed!')
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 follow diagram illustrates the flow of a transaction in `mvcc-api`.
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 App
96
- participant Manager
97
- participant Transaction
98
- participant Strategy
99
-
100
- Note over App, Manager: Initialization
101
- App->>Manager: new Manager(Strategy)
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
- Note over App, Transaction: Start Transaction
104
- App->>Manager: createTransaction()
105
- Manager-->>Transaction: new(snapshotVersion)
106
- Manager-->>App: tx instance
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
- App->>Transaction: write(key, value)
121
- Transaction-->>Transaction: Buffer write (In-Memory)
122
-
123
- Note over App, Strategy: Commit Phase
124
- App->>Transaction: commit()
125
- Transaction->>Manager: _commit(tx)
126
- Manager->>Manager: Check Conflicts (Optimistic Lock)
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
- ### `MVCCStrategy<T>` (Abstract)
140
- - `read(key: string): Deferred<T>`
141
- - `write(key: string, value: T): Deferred<void>`
142
- - `delete(key: string): Deferred<void>`
143
- - `exists(key: string): Deferred<boolean>`
144
-
145
- ### `MVCCManager<T, S>`
146
- - `createTransaction(): Transaction`
147
- - `version`: Current global version.
148
-
149
- ### `MVCCTransaction<T>`
150
- - `read(key: string): Deferred<T | null>`
151
- - `write(key: string, value: T): this`
152
- - `delete(key: string): this`
153
- - `commit(): Deferred<this>`
154
- - `rollback(): this`
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