@optimystic/db-p2p 0.0.1
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/dist/index.min.js +52 -0
- package/dist/index.min.js.map +7 -0
- package/dist/src/cluster/client.d.ts +12 -0
- package/dist/src/cluster/client.d.ts.map +1 -0
- package/dist/src/cluster/client.js +65 -0
- package/dist/src/cluster/client.js.map +1 -0
- package/dist/src/cluster/cluster-repo.d.ts +79 -0
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -0
- package/dist/src/cluster/cluster-repo.js +613 -0
- package/dist/src/cluster/cluster-repo.js.map +1 -0
- package/dist/src/cluster/partition-detector.d.ts +59 -0
- package/dist/src/cluster/partition-detector.d.ts.map +1 -0
- package/dist/src/cluster/partition-detector.js +129 -0
- package/dist/src/cluster/partition-detector.js.map +1 -0
- package/dist/src/cluster/service.d.ts +49 -0
- package/dist/src/cluster/service.d.ts.map +1 -0
- package/dist/src/cluster/service.js +107 -0
- package/dist/src/cluster/service.js.map +1 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +29 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/it-utility.d.ts +4 -0
- package/dist/src/it-utility.d.ts.map +1 -0
- package/dist/src/it-utility.js +32 -0
- package/dist/src/it-utility.js.map +1 -0
- package/dist/src/libp2p-key-network.d.ts +59 -0
- package/dist/src/libp2p-key-network.d.ts.map +1 -0
- package/dist/src/libp2p-key-network.js +278 -0
- package/dist/src/libp2p-key-network.js.map +1 -0
- package/dist/src/libp2p-node.d.ts +28 -0
- package/dist/src/libp2p-node.d.ts.map +1 -0
- package/dist/src/libp2p-node.js +270 -0
- package/dist/src/libp2p-node.js.map +1 -0
- package/dist/src/logger.d.ts +3 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/network/get-network-manager.d.ts +4 -0
- package/dist/src/network/get-network-manager.d.ts.map +1 -0
- package/dist/src/network/get-network-manager.js +17 -0
- package/dist/src/network/get-network-manager.js.map +1 -0
- package/dist/src/network/network-manager-service.d.ts +82 -0
- package/dist/src/network/network-manager-service.d.ts.map +1 -0
- package/dist/src/network/network-manager-service.js +283 -0
- package/dist/src/network/network-manager-service.js.map +1 -0
- package/dist/src/peer-utils.d.ts +2 -0
- package/dist/src/peer-utils.d.ts.map +1 -0
- package/dist/src/peer-utils.js +28 -0
- package/dist/src/peer-utils.js.map +1 -0
- package/dist/src/protocol-client.d.ts +12 -0
- package/dist/src/protocol-client.d.ts.map +1 -0
- package/dist/src/protocol-client.js +34 -0
- package/dist/src/protocol-client.js.map +1 -0
- package/dist/src/repo/client.d.ts +17 -0
- package/dist/src/repo/client.d.ts.map +1 -0
- package/dist/src/repo/client.js +82 -0
- package/dist/src/repo/client.js.map +1 -0
- package/dist/src/repo/cluster-coordinator.d.ts +59 -0
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -0
- package/dist/src/repo/cluster-coordinator.js +539 -0
- package/dist/src/repo/cluster-coordinator.js.map +1 -0
- package/dist/src/repo/coordinator-repo.d.ts +29 -0
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -0
- package/dist/src/repo/coordinator-repo.js +102 -0
- package/dist/src/repo/coordinator-repo.js.map +1 -0
- package/dist/src/repo/redirect.d.ts +14 -0
- package/dist/src/repo/redirect.d.ts.map +1 -0
- package/dist/src/repo/redirect.js +9 -0
- package/dist/src/repo/redirect.js.map +1 -0
- package/dist/src/repo/service.d.ts +52 -0
- package/dist/src/repo/service.d.ts.map +1 -0
- package/dist/src/repo/service.js +181 -0
- package/dist/src/repo/service.js.map +1 -0
- package/dist/src/repo/types.d.ts +7 -0
- package/dist/src/repo/types.d.ts.map +1 -0
- package/dist/src/repo/types.js +2 -0
- package/dist/src/repo/types.js.map +1 -0
- package/dist/src/routing/libp2p-known-peers.d.ts +4 -0
- package/dist/src/routing/libp2p-known-peers.d.ts.map +1 -0
- package/dist/src/routing/libp2p-known-peers.js +19 -0
- package/dist/src/routing/libp2p-known-peers.js.map +1 -0
- package/dist/src/routing/responsibility.d.ts +14 -0
- package/dist/src/routing/responsibility.d.ts.map +1 -0
- package/dist/src/routing/responsibility.js +45 -0
- package/dist/src/routing/responsibility.js.map +1 -0
- package/dist/src/routing/simple-cluster-coordinator.d.ts +23 -0
- package/dist/src/routing/simple-cluster-coordinator.d.ts.map +1 -0
- package/dist/src/routing/simple-cluster-coordinator.js +59 -0
- package/dist/src/routing/simple-cluster-coordinator.js.map +1 -0
- package/dist/src/storage/arachnode-fret-adapter.d.ts +65 -0
- package/dist/src/storage/arachnode-fret-adapter.d.ts.map +1 -0
- package/dist/src/storage/arachnode-fret-adapter.js +93 -0
- package/dist/src/storage/arachnode-fret-adapter.js.map +1 -0
- package/dist/src/storage/block-storage.d.ts +31 -0
- package/dist/src/storage/block-storage.d.ts.map +1 -0
- package/dist/src/storage/block-storage.js +154 -0
- package/dist/src/storage/block-storage.js.map +1 -0
- package/dist/src/storage/file-storage.d.ts +30 -0
- package/dist/src/storage/file-storage.d.ts.map +1 -0
- package/dist/src/storage/file-storage.js +127 -0
- package/dist/src/storage/file-storage.js.map +1 -0
- package/dist/src/storage/helpers.d.ts +3 -0
- package/dist/src/storage/helpers.d.ts.map +1 -0
- package/dist/src/storage/helpers.js +28 -0
- package/dist/src/storage/helpers.js.map +1 -0
- package/dist/src/storage/i-block-storage.d.ts +32 -0
- package/dist/src/storage/i-block-storage.d.ts.map +1 -0
- package/dist/src/storage/i-block-storage.js +2 -0
- package/dist/src/storage/i-block-storage.js.map +1 -0
- package/dist/src/storage/i-raw-storage.d.ts +20 -0
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -0
- package/dist/src/storage/i-raw-storage.js +2 -0
- package/dist/src/storage/i-raw-storage.js.map +1 -0
- package/dist/src/storage/memory-storage.d.ts +27 -0
- package/dist/src/storage/memory-storage.d.ts.map +1 -0
- package/dist/src/storage/memory-storage.js +87 -0
- package/dist/src/storage/memory-storage.js.map +1 -0
- package/dist/src/storage/restoration-coordinator-v2.d.ts +63 -0
- package/dist/src/storage/restoration-coordinator-v2.d.ts.map +1 -0
- package/dist/src/storage/restoration-coordinator-v2.js +157 -0
- package/dist/src/storage/restoration-coordinator-v2.js.map +1 -0
- package/dist/src/storage/ring-selector.d.ts +56 -0
- package/dist/src/storage/ring-selector.d.ts.map +1 -0
- package/dist/src/storage/ring-selector.js +118 -0
- package/dist/src/storage/ring-selector.js.map +1 -0
- package/dist/src/storage/storage-monitor.d.ts +23 -0
- package/dist/src/storage/storage-monitor.d.ts.map +1 -0
- package/dist/src/storage/storage-monitor.js +40 -0
- package/dist/src/storage/storage-monitor.js.map +1 -0
- package/dist/src/storage/storage-repo.d.ts +17 -0
- package/dist/src/storage/storage-repo.d.ts.map +1 -0
- package/dist/src/storage/storage-repo.js +267 -0
- package/dist/src/storage/storage-repo.js.map +1 -0
- package/dist/src/storage/struct.d.ts +29 -0
- package/dist/src/storage/struct.d.ts.map +1 -0
- package/dist/src/storage/struct.js +2 -0
- package/dist/src/storage/struct.js.map +1 -0
- package/dist/src/sync/client.d.ts +27 -0
- package/dist/src/sync/client.d.ts.map +1 -0
- package/dist/src/sync/client.js +32 -0
- package/dist/src/sync/client.js.map +1 -0
- package/dist/src/sync/protocol.d.ts +58 -0
- package/dist/src/sync/protocol.d.ts.map +1 -0
- package/dist/src/sync/protocol.js +12 -0
- package/dist/src/sync/protocol.js.map +1 -0
- package/dist/src/sync/service.d.ts +62 -0
- package/dist/src/sync/service.d.ts.map +1 -0
- package/dist/src/sync/service.js +168 -0
- package/dist/src/sync/service.js.map +1 -0
- package/package.json +73 -0
- package/readme.md +497 -0
- package/src/cluster/client.ts +63 -0
- package/src/cluster/cluster-repo.ts +711 -0
- package/src/cluster/partition-detector.ts +158 -0
- package/src/cluster/service.ts +156 -0
- package/src/index.ts +30 -0
- package/src/it-utility.ts +36 -0
- package/src/libp2p-key-network.ts +334 -0
- package/src/libp2p-node.ts +335 -0
- package/src/logger.ts +9 -0
- package/src/network/get-network-manager.ts +17 -0
- package/src/network/network-manager-service.ts +334 -0
- package/src/peer-utils.ts +24 -0
- package/src/protocol-client.ts +54 -0
- package/src/repo/client.ts +112 -0
- package/src/repo/cluster-coordinator.ts +592 -0
- package/src/repo/coordinator-repo.ts +137 -0
- package/src/repo/redirect.ts +17 -0
- package/src/repo/service.ts +219 -0
- package/src/repo/types.ts +7 -0
- package/src/routing/libp2p-known-peers.ts +26 -0
- package/src/routing/responsibility.ts +63 -0
- package/src/routing/simple-cluster-coordinator.ts +70 -0
- package/src/storage/arachnode-fret-adapter.ts +128 -0
- package/src/storage/block-storage.ts +182 -0
- package/src/storage/file-storage.ts +163 -0
- package/src/storage/helpers.ts +29 -0
- package/src/storage/i-block-storage.ts +40 -0
- package/src/storage/i-raw-storage.ts +30 -0
- package/src/storage/memory-storage.ts +108 -0
- package/src/storage/restoration-coordinator-v2.ts +191 -0
- package/src/storage/ring-selector.ts +155 -0
- package/src/storage/storage-monitor.ts +59 -0
- package/src/storage/storage-repo.ts +320 -0
- package/src/storage/struct.ts +34 -0
- package/src/sync/client.ts +42 -0
- package/src/sync/protocol.ts +71 -0
- package/src/sync/service.ts +229 -0
package/readme.md
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
# Optimystic DB-P2P
|
|
2
|
+
|
|
3
|
+
A distributed peer-to-peer database system that provides concrete implementations of the Optimystic database abstractions using filesystem storage and libp2p networking. This package transforms the interfaces and abstractions from `@optimystic/db-core` into a fully operational distributed database system.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@optimystic/db-core` package provides abstractions and interfaces for database operations—immutable blocks, versioned transactions, repository interfaces, and collection data structures. However, it doesn't provide concrete implementations for storage or distribution. `db-p2p` provides concrete implementations of the core abstractions using:
|
|
8
|
+
|
|
9
|
+
- **Filesystem storage** with serialization for persistent, versioned block storage
|
|
10
|
+
- **libp2p networking** for decentralized peer-to-peer communication
|
|
11
|
+
- **Distributed consensus** using 2-phase commit protocols for consistency
|
|
12
|
+
- **Fault-tolerant coordination** with automatic recovery and data restoration
|
|
13
|
+
|
|
14
|
+
The result is a fully operational distributed database system that implements the interfaces defined in the core abstractions to provide a complete distributed database system.
|
|
15
|
+
|
|
16
|
+
## Architecture Overview
|
|
17
|
+
|
|
18
|
+
The system provides three distinct communication interfaces, each serving different roles in the distributed architecture:
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
graph TD
|
|
22
|
+
subgraph "External Client Process"
|
|
23
|
+
A[Application Code]
|
|
24
|
+
B[RepoClient]
|
|
25
|
+
A --> B
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
subgraph "Coordinator Node Process<br/>(Orchestrates Transactions)"
|
|
29
|
+
C[RepoService]
|
|
30
|
+
D[CoordinatorRepo]
|
|
31
|
+
E[ClusterClient]
|
|
32
|
+
F[StorageRepo]
|
|
33
|
+
subgraph "Local Storage"
|
|
34
|
+
F1[BlockStorage]
|
|
35
|
+
F2[FileRawStorage]
|
|
36
|
+
F3[Local Filesystem]
|
|
37
|
+
end
|
|
38
|
+
C --> D
|
|
39
|
+
D --> E
|
|
40
|
+
D --> F
|
|
41
|
+
F --> F1
|
|
42
|
+
F1 --> F2
|
|
43
|
+
F2 --> F3
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
subgraph "Cluster Peer 1 Process<br/>(Participates in Consensus)"
|
|
47
|
+
G1[ClusterService]
|
|
48
|
+
H1[ClusterMember]
|
|
49
|
+
I1[StorageRepo]
|
|
50
|
+
subgraph "Local Storage 1"
|
|
51
|
+
I1A[BlockStorage]
|
|
52
|
+
I1B[FileRawStorage]
|
|
53
|
+
I1C[Local Filesystem]
|
|
54
|
+
end
|
|
55
|
+
G1 --> H1
|
|
56
|
+
H1 --> I1
|
|
57
|
+
I1 --> I1A
|
|
58
|
+
I1A --> I1B
|
|
59
|
+
I1B --> I1C
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
subgraph "Cluster Peer 2 Process<br/>(Participates in Consensus)"
|
|
63
|
+
G2[ClusterService]
|
|
64
|
+
H2[ClusterMember]
|
|
65
|
+
I2[StorageRepo]
|
|
66
|
+
subgraph "Local Storage 2"
|
|
67
|
+
I2A[BlockStorage]
|
|
68
|
+
I2B[FileRawStorage]
|
|
69
|
+
I2C[Local Filesystem]
|
|
70
|
+
end
|
|
71
|
+
G2 --> H2
|
|
72
|
+
H2 --> I2
|
|
73
|
+
I2 --> I2A
|
|
74
|
+
I2A --> I2B
|
|
75
|
+
I2B --> I2C
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
subgraph "Cluster Peer N Process<br/>(Participates in Consensus)"
|
|
79
|
+
G3[ClusterService]
|
|
80
|
+
H3[ClusterMember]
|
|
81
|
+
I3[StorageRepo]
|
|
82
|
+
subgraph "Local Storage N"
|
|
83
|
+
I3A[BlockStorage]
|
|
84
|
+
I3B[FileRawStorage]
|
|
85
|
+
I3C[Local Filesystem]
|
|
86
|
+
end
|
|
87
|
+
G3 --> H3
|
|
88
|
+
H3 --> I3
|
|
89
|
+
I3 --> I3A
|
|
90
|
+
I3A --> I3B
|
|
91
|
+
I3B --> I3C
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
%% Network communication
|
|
95
|
+
B -.->|"/db-p2p/repo/1.0.0"| C
|
|
96
|
+
E -.->|"/db-p2p/cluster/1.0.0"| G1
|
|
97
|
+
E -.->|"/db-p2p/cluster/1.0.0"| G2
|
|
98
|
+
E -.->|"/db-p2p/cluster/1.0.0"| G3
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Repo Layer: External Client Interface
|
|
102
|
+
|
|
103
|
+
The repo layer provides the interface for external clients to communicate with the distributed database:
|
|
104
|
+
|
|
105
|
+
- **RepoClient**: Allows external clients to connect to any coordinator node
|
|
106
|
+
- **RepoService**: Handles incoming requests from external clients
|
|
107
|
+
- **CoordinatorRepo**: Orchestrates distributed transactions by coordinating with cluster peers
|
|
108
|
+
- **Network transparency**: Same `IRepo` interface whether local or distributed
|
|
109
|
+
|
|
110
|
+
### Cluster Layer: Peer-to-Peer Coordination Interface
|
|
111
|
+
|
|
112
|
+
The cluster layer provides the interface for coordinators to communicate with other peers in the cluster:
|
|
113
|
+
|
|
114
|
+
- **ClusterClient**: Allows coordinators to send consensus requests to cluster peers
|
|
115
|
+
- **ClusterService**: Handles incoming consensus requests from coordinators
|
|
116
|
+
- **ClusterMember**: Implements 2-phase commit protocol for distributed consensus
|
|
117
|
+
- **Byzantine fault tolerance**: Ensures consistency even with peer failures
|
|
118
|
+
|
|
119
|
+
### Storage Layer: Persistent Data Implementation
|
|
120
|
+
|
|
121
|
+
The storage layer provides concrete implementations of the core database abstractions:
|
|
122
|
+
|
|
123
|
+
- **StorageRepo**: Implements `IRepo` interface with filesystem-based persistence
|
|
124
|
+
- **BlockStorage**: Provides versioned block storage with conflict resolution
|
|
125
|
+
- **FileRawStorage**: JSON-based file storage with atomic operations
|
|
126
|
+
- **Data restoration**: Pluggable restoration for missing data from network peers
|
|
127
|
+
|
|
128
|
+
## Detailed Component Architecture
|
|
129
|
+
|
|
130
|
+
### Repo Layer Components
|
|
131
|
+
|
|
132
|
+
#### `RepoClient`
|
|
133
|
+
Allows external clients to connect to coordinator nodes:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
class RepoClient extends ProtocolClient implements IRepo {
|
|
137
|
+
static create(peerId: PeerId, peerNetwork: IPeerNetwork): RepoClient
|
|
138
|
+
// Implements same IRepo interface as local storage
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Key Features:**
|
|
143
|
+
- Network-transparent database operations for external clients
|
|
144
|
+
- Automatic protocol handling and message serialization
|
|
145
|
+
- Error handling for network failures and timeouts
|
|
146
|
+
- Connection pooling and optimization
|
|
147
|
+
|
|
148
|
+
#### `RepoService`
|
|
149
|
+
Handles incoming repository protocol messages from external clients:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
class RepoService implements Startable {
|
|
153
|
+
// Protocol: /db-p2p/repo/1.0.0
|
|
154
|
+
// Transport: Length-prefixed JSON over libp2p streams
|
|
155
|
+
async start(): Promise<void>
|
|
156
|
+
async stop(): Promise<void>
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Key Features:**
|
|
161
|
+
- Registers and handles the repository protocol
|
|
162
|
+
- Routes operations from external clients to coordinator implementation
|
|
163
|
+
- Manages concurrent connections and stream processing
|
|
164
|
+
- Provides comprehensive error handling and logging
|
|
165
|
+
|
|
166
|
+
#### `CoordinatorRepo`
|
|
167
|
+
Orchestrates distributed transactions by coordinating with cluster peers:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
class CoordinatorRepo implements IRepo {
|
|
171
|
+
constructor(
|
|
172
|
+
keyNetwork: IKeyNetwork,
|
|
173
|
+
createClusterClient: (peerId: PeerId) => ClusterClient,
|
|
174
|
+
storageRepo: IRepo
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Key Features:**
|
|
180
|
+
- Manages distributed transactions using cluster consensus
|
|
181
|
+
- Integrates with key network for peer discovery
|
|
182
|
+
- Uses cluster layer to coordinate with other peers
|
|
183
|
+
- Maintains local storage directly while coordinating remote operations
|
|
184
|
+
|
|
185
|
+
### Cluster Layer Components
|
|
186
|
+
|
|
187
|
+
#### `ClusterClient`
|
|
188
|
+
Allows coordinators to send consensus requests to cluster peers:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
class ClusterClient extends ProtocolClient implements ICluster {
|
|
192
|
+
async update(record: ClusterRecord): Promise<ClusterRecord>
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Key Features:**
|
|
197
|
+
- Network communication for consensus operations
|
|
198
|
+
- Handles cluster record updates and responses
|
|
199
|
+
- Manages timeouts and retries for consensus requests
|
|
200
|
+
|
|
201
|
+
#### `ClusterService`
|
|
202
|
+
Handles incoming consensus requests from coordinators:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
class ClusterService implements Startable {
|
|
206
|
+
// Protocol: /db-p2p/cluster/1.0.0
|
|
207
|
+
// Handles ClusterRecord updates for consensus
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Key Features:**
|
|
212
|
+
- Registers and handles the cluster consensus protocol
|
|
213
|
+
- Routes consensus requests to local cluster member
|
|
214
|
+
- Manages concurrent consensus operations
|
|
215
|
+
|
|
216
|
+
#### `ClusterMember`
|
|
217
|
+
Implements 2-phase commit protocol for distributed consensus:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
class ClusterMember implements ICluster {
|
|
221
|
+
async update(record: ClusterRecord): Promise<ClusterRecord>
|
|
222
|
+
// Handles: Promise collection → Majority consensus → Commit execution
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Key Features:**
|
|
227
|
+
- Complete 2-phase commit protocol implementation
|
|
228
|
+
- Conflict detection and resolution
|
|
229
|
+
- Cryptographic signature verification
|
|
230
|
+
- Automatic timeout and cleanup management
|
|
231
|
+
- Uses storage layer to execute operations when consensus is reached
|
|
232
|
+
|
|
233
|
+
### Storage Layer Components
|
|
234
|
+
|
|
235
|
+
#### `StorageRepo`
|
|
236
|
+
Implements `IRepo` interface with filesystem-based persistence:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
class StorageRepo implements IRepo {
|
|
240
|
+
async get(blockGets: BlockGets): Promise<GetBlockResults>
|
|
241
|
+
async pend(request: PendRequest): Promise<PendResult>
|
|
242
|
+
async commit(request: CommitRequest): Promise<CommitResult>
|
|
243
|
+
async cancel(trxRef: TrxBlocks): Promise<void>
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Key Features:**
|
|
248
|
+
- Concrete implementation of core database abstractions
|
|
249
|
+
- Orchestrates transactions across multiple blocks
|
|
250
|
+
- Handles revision conflicts and missing transaction detection
|
|
251
|
+
- Provides atomic commit operations with proper locking
|
|
252
|
+
|
|
253
|
+
#### `BlockStorage`
|
|
254
|
+
Provides versioned block storage with conflict resolution:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
class BlockStorage implements IBlockStorage {
|
|
258
|
+
async getBlock(rev?: number): Promise<{ block: IBlock, actionRev: ActionRev }>
|
|
259
|
+
async savePendingAction(actionId: ActionId, transform: Transform): Promise<void>
|
|
260
|
+
async promotePendingAction(actionId: ActionId): Promise<void>
|
|
261
|
+
async ensureRevision(rev: number): Promise<void>
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Key Features:**
|
|
266
|
+
- Maintains complete revision history for each block
|
|
267
|
+
- Reconstructs blocks by applying transforms to base versions
|
|
268
|
+
- Integrates with restoration callbacks for missing data
|
|
269
|
+
- Uses latches for thread-safe concurrent access
|
|
270
|
+
|
|
271
|
+
#### `FileRawStorage`
|
|
272
|
+
JSON-based file storage with atomic operations:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// File system organization
|
|
276
|
+
{basePath}/
|
|
277
|
+
├── {blockId}/
|
|
278
|
+
│ ├── meta.json # Block metadata and revision ranges
|
|
279
|
+
│ ├── revs/{rev}.json # Revision → ActionId mappings
|
|
280
|
+
│ ├── pend/{actionId}.json # Pending actions
|
|
281
|
+
│ ├── trx/{actionId}.json # Committed actions
|
|
282
|
+
│ └── blocks/{actionId}.json # Materialized blocks
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Key Features:**
|
|
286
|
+
- Concrete filesystem implementation of storage interfaces
|
|
287
|
+
- JSON serialization for cross-platform compatibility
|
|
288
|
+
- Atomic file operations for consistency
|
|
289
|
+
- Organized directory structure for efficient access
|
|
290
|
+
|
|
291
|
+
## Integration Patterns
|
|
292
|
+
|
|
293
|
+
### Relationship to `@optimystic/db-core`
|
|
294
|
+
|
|
295
|
+
The `db-p2p` package provides concrete implementations of the core database abstractions:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// Core provides interfaces and abstractions
|
|
299
|
+
import { IRepo, IBlock, Transform, IBlockStorage } from '@optimystic/db-core';
|
|
300
|
+
|
|
301
|
+
// P2P provides concrete implementations
|
|
302
|
+
class StorageRepo implements IRepo { /* filesystem-based repo */ }
|
|
303
|
+
class BlockStorage implements IBlockStorage { /* versioned block storage */ }
|
|
304
|
+
class RepoClient implements IRepo { /* network-transparent repo */ }
|
|
305
|
+
class CoordinatorRepo implements IRepo { /* distributed consensus repo */ }
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Implementation Points:**
|
|
309
|
+
- **Storage Layer**: Provides concrete filesystem-based implementations of core interfaces
|
|
310
|
+
- **Network Layer**: Implements peer-to-peer communication using libp2p
|
|
311
|
+
- **Consensus Layer**: Adds distributed consensus while maintaining core interfaces
|
|
312
|
+
- **API Compatibility**: Same interfaces as core abstractions for seamless integration
|
|
313
|
+
|
|
314
|
+
### Libp2p Integration
|
|
315
|
+
|
|
316
|
+
The package integrates deeply with libp2p for networking:
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// Node creation with integrated services
|
|
320
|
+
const node = await createLibp2pNode({
|
|
321
|
+
services: {
|
|
322
|
+
repo: repoService({ protocol: '/db-p2p/repo/1.0.0' }),
|
|
323
|
+
cluster: clusterService({ protocol: '/db-p2p/cluster/1.0.0' })
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// Key network integration
|
|
328
|
+
const keyNetwork = new Libp2pKeyPeerNetwork(node);
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Integration Features:**
|
|
332
|
+
- **Protocol Management**: Registers custom protocols for repo and cluster operations
|
|
333
|
+
- **Stream Handling**: Uses libp2p streams for reliable message transport
|
|
334
|
+
- **Peer Discovery**: Integrates with libp2p DHT for peer discovery
|
|
335
|
+
- **Security**: Leverages libp2p's cryptographic peer identity
|
|
336
|
+
|
|
337
|
+
## Usage Examples
|
|
338
|
+
|
|
339
|
+
### Setting Up a Coordinator Node
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { createLibp2pNode } from '@optimystic/db-p2p';
|
|
343
|
+
import { repoService, clusterService, StorageRepo, CoordinatorRepo } from '@optimystic/db-p2p';
|
|
344
|
+
|
|
345
|
+
// Create a libp2p node with database services
|
|
346
|
+
const node = await createLibp2pNode({
|
|
347
|
+
services: {
|
|
348
|
+
repo: repoService(), // Handles external client requests
|
|
349
|
+
cluster: clusterService() // Handles peer coordination requests
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Set up storage layer with filesystem persistence
|
|
354
|
+
const storageRepo = new StorageRepo(/* filesystem config */);
|
|
355
|
+
|
|
356
|
+
// Set up coordinator that uses cluster layer for consensus
|
|
357
|
+
const coordinatorRepo = new CoordinatorRepo(
|
|
358
|
+
keyNetwork, // For peer discovery
|
|
359
|
+
createClusterClient, // For communicating with cluster peers
|
|
360
|
+
storageRepo // For local storage operations
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
// Start the node
|
|
364
|
+
await node.start();
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### External Client Operations
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// External clients connect to coordinator nodes
|
|
371
|
+
const client = RepoClient.create(coordinatorPeerId, peerNetwork);
|
|
372
|
+
|
|
373
|
+
// Operations work the same as core database interfaces
|
|
374
|
+
const blocks = await client.get({
|
|
375
|
+
blockIds: ['block1', 'block2'],
|
|
376
|
+
context: { rev: 10 }
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// But automatically coordinate across the cluster
|
|
380
|
+
const pendResult = await client.pend({
|
|
381
|
+
actionId: 'tx1',
|
|
382
|
+
transforms: { block1: [/* operations */] },
|
|
383
|
+
rev: 11
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const commitResult = await client.commit({
|
|
387
|
+
actionId: 'tx1',
|
|
388
|
+
blockIds: ['block1'],
|
|
389
|
+
rev: 11
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### How Coordination Works
|
|
394
|
+
|
|
395
|
+
```mermaid
|
|
396
|
+
sequenceDiagram
|
|
397
|
+
participant Client as External Client
|
|
398
|
+
participant RC as RepoClient
|
|
399
|
+
participant CR as CoordinatorRepo
|
|
400
|
+
participant CC as ClusterClient
|
|
401
|
+
participant CS as ClusterService
|
|
402
|
+
participant CM as ClusterMember
|
|
403
|
+
participant SR as StorageRepo
|
|
404
|
+
|
|
405
|
+
Client->>RC: pend(transaction)
|
|
406
|
+
RC->>CR: pend(transaction)
|
|
407
|
+
CR->>CC: consensus request
|
|
408
|
+
CC->>CS: cluster protocol
|
|
409
|
+
CS->>CM: update(ClusterRecord)
|
|
410
|
+
CM->>CM: 2-phase commit
|
|
411
|
+
CM->>SR: execute operation
|
|
412
|
+
SR-->>CM: result
|
|
413
|
+
CM-->>CS: consensus result
|
|
414
|
+
CS-->>CC: response
|
|
415
|
+
CC-->>CR: consensus complete
|
|
416
|
+
CR-->>RC: transaction result
|
|
417
|
+
RC-->>Client: pend result
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Documentation
|
|
421
|
+
|
|
422
|
+
For detailed information about specific components:
|
|
423
|
+
|
|
424
|
+
- **[Storage System](./docs/storage.md)**: Versioned block storage and persistence
|
|
425
|
+
- **[Repo Interface](./docs/repo.md)**: Distributed database operations
|
|
426
|
+
- **[Cluster Consensus](./docs/cluster.md)**: 2-phase commit and distributed consensus
|
|
427
|
+
|
|
428
|
+
## Block Restoration & Arachnode Integration
|
|
429
|
+
|
|
430
|
+
The db-p2p package includes **dynamic Arachnode ring discovery** for automatic block restoration across storage tiers.
|
|
431
|
+
|
|
432
|
+
### How Block Restoration Works
|
|
433
|
+
|
|
434
|
+
When a node is missing a block or revision, the restoration system:
|
|
435
|
+
|
|
436
|
+
1. **Determines storage rings** - Nodes self-select ring depth based on capacity
|
|
437
|
+
2. **Discovers peers via FRET** - Ring membership propagates via existing neighbor exchange
|
|
438
|
+
3. **Queries rings intelligently** - Tries transaction ring first, then inner storage rings
|
|
439
|
+
4. **Filters by partition** - Only queries peers responsible for the block's keyspace
|
|
440
|
+
|
|
441
|
+
### Ring Selection
|
|
442
|
+
|
|
443
|
+
Nodes calculate their appropriate ring depth using:
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
ringDepth = ceil(-log2(available_capacity / estimated_demand))
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**Examples:**
|
|
450
|
+
- 100% coverage → Ring 0 (full keyspace)
|
|
451
|
+
- 50% coverage → Ring 1 (2 partitions)
|
|
452
|
+
- 1% coverage → Ring 7 (128 partitions)
|
|
453
|
+
|
|
454
|
+
### Architecture
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
FRET (Pure DHT)
|
|
458
|
+
- Generic metadata transport
|
|
459
|
+
- Peer discovery & routing
|
|
460
|
+
|
|
461
|
+
ArachnodeFretAdapter (Plugin Layer)
|
|
462
|
+
- Arachnode-specific semantics
|
|
463
|
+
- Ring discovery methods
|
|
464
|
+
|
|
465
|
+
Restoration Components
|
|
466
|
+
- RingSelector: Capacity-based ring selection
|
|
467
|
+
- RestorationCoordinator: Multi-ring queries
|
|
468
|
+
- StorageMonitor: Capacity tracking
|
|
469
|
+
- SyncService/Client: Block request protocol
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Configuration
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
const node = await createLibp2pNode({
|
|
476
|
+
port: 9000,
|
|
477
|
+
networkName: 'mynet',
|
|
478
|
+
bootstrapNodes: ['...'],
|
|
479
|
+
clusterSize: 10, // Cluster size for peer discovery
|
|
480
|
+
arachnode: {
|
|
481
|
+
enableRingZulu: true // Default: enabled
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Ring Transitions
|
|
487
|
+
|
|
488
|
+
Nodes automatically transition between rings based on capacity thresholds:
|
|
489
|
+
- **Move OUT** (to more granular ring) when > 85% capacity used
|
|
490
|
+
- **Move IN** (to broader ring) when < 40% capacity used
|
|
491
|
+
|
|
492
|
+
## Related Packages
|
|
493
|
+
|
|
494
|
+
- **[@optimystic/db-core](../db-core)**: Core database interfaces and local operations
|
|
495
|
+
- **[@optimystic/db-quereus](../db-quereus)**: Query engine and data access patterns
|
|
496
|
+
- **[p2p-fret](../fret)**: DHT implementation for peer discovery
|
|
497
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type PeerId } from '@libp2p/interface';
|
|
2
|
+
import type { IPeerNetwork, ICluster, ClusterRecord } from '@optimystic/db-core';
|
|
3
|
+
import { ProtocolClient } from '../protocol-client.js';
|
|
4
|
+
import { peerIdFromString } from '@libp2p/peer-id';
|
|
5
|
+
|
|
6
|
+
export class ClusterClient extends ProtocolClient implements ICluster {
|
|
7
|
+
private constructor(peerId: PeerId, peerNetwork: IPeerNetwork, readonly protocolPrefix?: string) {
|
|
8
|
+
super(peerId, peerNetwork);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Create a new client instance */
|
|
12
|
+
public static create(peerId: PeerId, peerNetwork: IPeerNetwork, protocolPrefix?: string): ClusterClient {
|
|
13
|
+
return new ClusterClient(peerId, peerNetwork, protocolPrefix);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async update(record: ClusterRecord, hop: number = 0): Promise<ClusterRecord> {
|
|
17
|
+
const message = {
|
|
18
|
+
operation: 'update',
|
|
19
|
+
record
|
|
20
|
+
};
|
|
21
|
+
const preferred = (this.protocolPrefix ?? '/db-p2p') + '/cluster/1.0.0'
|
|
22
|
+
let response: any
|
|
23
|
+
try {
|
|
24
|
+
response = await this.processMessage<any>(message, preferred)
|
|
25
|
+
} catch (err) {
|
|
26
|
+
if (preferred !== '/db-p2p/cluster/1.0.0') {
|
|
27
|
+
response = await this.processMessage<any>(message, '/db-p2p/cluster/1.0.0')
|
|
28
|
+
} else {
|
|
29
|
+
throw err
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (response?.redirect?.peers?.length) {
|
|
33
|
+
if (hop >= 2) {
|
|
34
|
+
throw new Error('Redirect loop detected in ClusterClient (max hops reached)')
|
|
35
|
+
}
|
|
36
|
+
const currentIdStr = this.peerId.toString()
|
|
37
|
+
const next = response.redirect.peers.find((p: any) => p.id !== currentIdStr) ?? response.redirect.peers[0]
|
|
38
|
+
const nextId = peerIdFromString(next.id)
|
|
39
|
+
if (next.id === currentIdStr) {
|
|
40
|
+
throw new Error('Redirect loop detected in ClusterClient (same peer)')
|
|
41
|
+
}
|
|
42
|
+
this.recordCoordinatorForRecordIfSupported(record, nextId)
|
|
43
|
+
const nextClient = ClusterClient.create(nextId, this.peerNetwork, this.protocolPrefix)
|
|
44
|
+
return await nextClient.update(record, hop + 1)
|
|
45
|
+
}
|
|
46
|
+
return response as ClusterRecord;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private recordCoordinatorForRecordIfSupported(record: ClusterRecord, peerId: PeerId): void {
|
|
50
|
+
const rmsg: any = (record as any)?.message
|
|
51
|
+
let tailId: string | undefined
|
|
52
|
+
if (rmsg?.commit?.tailId) tailId = rmsg.commit.tailId
|
|
53
|
+
else if (rmsg?.pend?.transforms) {
|
|
54
|
+
const keys = Object.keys(rmsg.pend.transforms)
|
|
55
|
+
if (keys.length > 0) tailId = keys[0]
|
|
56
|
+
}
|
|
57
|
+
if (tailId) {
|
|
58
|
+
const kbytes = new TextEncoder().encode(tailId)
|
|
59
|
+
const pn: any = this.peerNetwork as any
|
|
60
|
+
if (typeof pn?.recordCoordinator === 'function') pn.recordCoordinator(kbytes, peerId)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|