@push.rocks/smartdb 1.0.1 → 2.1.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/.smartconfig.json +18 -4
- package/dist_rust/rustdb_linux_amd64 +0 -0
- package/dist_rust/rustdb_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/ts_local/classes.localsmartdb.d.ts +5 -5
- package/dist_ts/ts_local/classes.localsmartdb.js +7 -9
- package/dist_ts/ts_local/plugins.d.ts +1 -2
- package/dist_ts/ts_local/plugins.js +3 -3
- package/dist_ts/ts_smartdb/index.d.ts +2 -24
- package/dist_ts/ts_smartdb/index.js +4 -29
- package/dist_ts/ts_smartdb/plugins.d.ts +2 -10
- package/dist_ts/ts_smartdb/plugins.js +3 -13
- package/dist_ts/ts_smartdb/rust-db-bridge.d.ts +122 -0
- package/dist_ts/ts_smartdb/rust-db-bridge.js +113 -0
- package/dist_ts/ts_smartdb/server/SmartdbServer.d.ts +39 -37
- package/dist_ts/ts_smartdb/server/SmartdbServer.js +87 -206
- package/dist_ts/ts_smartdb/server/index.d.ts +0 -4
- package/dist_ts/ts_smartdb/server/index.js +1 -5
- package/dist_ts_debugserver/bundled.d.ts +4 -0
- package/dist_ts_debugserver/bundled.js +12 -0
- package/dist_ts_debugserver/classes.debugserver.d.ts +36 -0
- package/dist_ts_debugserver/classes.debugserver.js +95 -0
- package/dist_ts_debugserver/index.d.ts +2 -0
- package/dist_ts_debugserver/index.js +2 -0
- package/dist_ts_debugserver/plugins.d.ts +2 -0
- package/dist_ts_debugserver/plugins.js +3 -0
- package/dist_ts_debugui/index.d.ts +2 -0
- package/dist_ts_debugui/index.js +2 -0
- package/dist_ts_debugui/plugins.d.ts +1 -0
- package/dist_ts_debugui/plugins.js +2 -0
- package/dist_ts_debugui/smartdb-debugui.d.ts +62 -0
- package/dist_ts_debugui/smartdb-debugui.js +1132 -0
- package/license +3 -1
- package/package.json +14 -13
- package/readme.md +209 -177
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/index.ts +11 -0
- package/ts/ts_local/classes.localsmartdb.ts +5 -6
- package/ts/ts_local/plugins.ts +1 -3
- package/ts/ts_smartdb/index.ts +14 -41
- package/ts/ts_smartdb/plugins.ts +2 -15
- package/ts/ts_smartdb/rust-db-bridge.ts +262 -0
- package/ts/ts_smartdb/server/SmartdbServer.ts +115 -246
- package/ts/ts_smartdb/server/index.ts +0 -7
- package/dist_ts/ts_smartdb/engine/AggregationEngine.d.ts +0 -66
- package/dist_ts/ts_smartdb/engine/AggregationEngine.js +0 -189
- package/dist_ts/ts_smartdb/engine/IndexEngine.d.ts +0 -97
- package/dist_ts/ts_smartdb/engine/IndexEngine.js +0 -678
- package/dist_ts/ts_smartdb/engine/QueryEngine.d.ts +0 -54
- package/dist_ts/ts_smartdb/engine/QueryEngine.js +0 -271
- package/dist_ts/ts_smartdb/engine/QueryPlanner.d.ts +0 -64
- package/dist_ts/ts_smartdb/engine/QueryPlanner.js +0 -308
- package/dist_ts/ts_smartdb/engine/SessionEngine.d.ts +0 -117
- package/dist_ts/ts_smartdb/engine/SessionEngine.js +0 -232
- package/dist_ts/ts_smartdb/engine/TransactionEngine.d.ts +0 -85
- package/dist_ts/ts_smartdb/engine/TransactionEngine.js +0 -287
- package/dist_ts/ts_smartdb/engine/UpdateEngine.d.ts +0 -47
- package/dist_ts/ts_smartdb/engine/UpdateEngine.js +0 -461
- package/dist_ts/ts_smartdb/errors/SmartdbErrors.d.ts +0 -100
- package/dist_ts/ts_smartdb/errors/SmartdbErrors.js +0 -155
- package/dist_ts/ts_smartdb/server/CommandRouter.d.ts +0 -87
- package/dist_ts/ts_smartdb/server/CommandRouter.js +0 -222
- package/dist_ts/ts_smartdb/server/WireProtocol.d.ts +0 -117
- package/dist_ts/ts_smartdb/server/WireProtocol.js +0 -298
- package/dist_ts/ts_smartdb/server/handlers/AdminHandler.d.ts +0 -100
- package/dist_ts/ts_smartdb/server/handlers/AdminHandler.js +0 -668
- package/dist_ts/ts_smartdb/server/handlers/AggregateHandler.d.ts +0 -31
- package/dist_ts/ts_smartdb/server/handlers/AggregateHandler.js +0 -277
- package/dist_ts/ts_smartdb/server/handlers/DeleteHandler.d.ts +0 -8
- package/dist_ts/ts_smartdb/server/handlers/DeleteHandler.js +0 -95
- package/dist_ts/ts_smartdb/server/handlers/FindHandler.d.ts +0 -31
- package/dist_ts/ts_smartdb/server/handlers/FindHandler.js +0 -291
- package/dist_ts/ts_smartdb/server/handlers/HelloHandler.d.ts +0 -11
- package/dist_ts/ts_smartdb/server/handlers/HelloHandler.js +0 -62
- package/dist_ts/ts_smartdb/server/handlers/IndexHandler.d.ts +0 -20
- package/dist_ts/ts_smartdb/server/handlers/IndexHandler.js +0 -183
- package/dist_ts/ts_smartdb/server/handlers/InsertHandler.d.ts +0 -8
- package/dist_ts/ts_smartdb/server/handlers/InsertHandler.js +0 -79
- package/dist_ts/ts_smartdb/server/handlers/UpdateHandler.d.ts +0 -24
- package/dist_ts/ts_smartdb/server/handlers/UpdateHandler.js +0 -296
- package/dist_ts/ts_smartdb/server/handlers/index.d.ts +0 -8
- package/dist_ts/ts_smartdb/server/handlers/index.js +0 -10
- package/dist_ts/ts_smartdb/storage/FileStorageAdapter.d.ts +0 -85
- package/dist_ts/ts_smartdb/storage/FileStorageAdapter.js +0 -465
- package/dist_ts/ts_smartdb/storage/IStorageAdapter.d.ts +0 -145
- package/dist_ts/ts_smartdb/storage/IStorageAdapter.js +0 -2
- package/dist_ts/ts_smartdb/storage/MemoryStorageAdapter.d.ts +0 -67
- package/dist_ts/ts_smartdb/storage/MemoryStorageAdapter.js +0 -378
- package/dist_ts/ts_smartdb/storage/OpLog.d.ts +0 -93
- package/dist_ts/ts_smartdb/storage/OpLog.js +0 -221
- package/dist_ts/ts_smartdb/storage/WAL.d.ts +0 -117
- package/dist_ts/ts_smartdb/storage/WAL.js +0 -286
- package/dist_ts/ts_smartdb/types/interfaces.d.ts +0 -363
- package/dist_ts/ts_smartdb/types/interfaces.js +0 -2
- package/dist_ts/ts_smartdb/utils/checksum.d.ts +0 -30
- package/dist_ts/ts_smartdb/utils/checksum.js +0 -77
- package/dist_ts/ts_smartdb/utils/index.d.ts +0 -1
- package/dist_ts/ts_smartdb/utils/index.js +0 -2
- package/ts/ts_smartdb/engine/AggregationEngine.ts +0 -283
- package/ts/ts_smartdb/engine/IndexEngine.ts +0 -798
- package/ts/ts_smartdb/engine/QueryEngine.ts +0 -301
- package/ts/ts_smartdb/engine/QueryPlanner.ts +0 -393
- package/ts/ts_smartdb/engine/SessionEngine.ts +0 -292
- package/ts/ts_smartdb/engine/TransactionEngine.ts +0 -351
- package/ts/ts_smartdb/engine/UpdateEngine.ts +0 -506
- package/ts/ts_smartdb/errors/SmartdbErrors.ts +0 -181
- package/ts/ts_smartdb/server/CommandRouter.ts +0 -289
- package/ts/ts_smartdb/server/WireProtocol.ts +0 -416
- package/ts/ts_smartdb/server/handlers/AdminHandler.ts +0 -719
- package/ts/ts_smartdb/server/handlers/AggregateHandler.ts +0 -342
- package/ts/ts_smartdb/server/handlers/DeleteHandler.ts +0 -115
- package/ts/ts_smartdb/server/handlers/FindHandler.ts +0 -330
- package/ts/ts_smartdb/server/handlers/HelloHandler.ts +0 -78
- package/ts/ts_smartdb/server/handlers/IndexHandler.ts +0 -207
- package/ts/ts_smartdb/server/handlers/InsertHandler.ts +0 -97
- package/ts/ts_smartdb/server/handlers/UpdateHandler.ts +0 -344
- package/ts/ts_smartdb/server/handlers/index.ts +0 -10
- package/ts/ts_smartdb/storage/FileStorageAdapter.ts +0 -562
- package/ts/ts_smartdb/storage/IStorageAdapter.ts +0 -208
- package/ts/ts_smartdb/storage/MemoryStorageAdapter.ts +0 -455
- package/ts/ts_smartdb/storage/OpLog.ts +0 -282
- package/ts/ts_smartdb/storage/WAL.ts +0 -375
- package/ts/ts_smartdb/types/interfaces.ts +0 -433
- package/ts/ts_smartdb/utils/checksum.ts +0 -88
- package/ts/ts_smartdb/utils/index.ts +0 -1
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
/**
|
|
3
|
-
* Operation Log for tracking all mutations
|
|
4
|
-
* Used primarily for change stream support
|
|
5
|
-
*/
|
|
6
|
-
export class OpLog {
|
|
7
|
-
storage;
|
|
8
|
-
counter = 0;
|
|
9
|
-
listeners = [];
|
|
10
|
-
constructor(storage) {
|
|
11
|
-
this.storage = storage;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Generate a new timestamp for oplog entries
|
|
15
|
-
*/
|
|
16
|
-
generateTimestamp() {
|
|
17
|
-
this.counter++;
|
|
18
|
-
return new plugins.bson.Timestamp({ t: Math.floor(Date.now() / 1000), i: this.counter });
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Generate a resume token from a timestamp
|
|
22
|
-
*/
|
|
23
|
-
generateResumeToken(ts) {
|
|
24
|
-
// Create a resume token similar to MongoDB's format
|
|
25
|
-
// It's a base64-encoded BSON document containing the timestamp
|
|
26
|
-
const tokenData = {
|
|
27
|
-
_data: Buffer.from(JSON.stringify({
|
|
28
|
-
ts: { t: ts.high, i: ts.low },
|
|
29
|
-
version: 1,
|
|
30
|
-
})).toString('base64'),
|
|
31
|
-
};
|
|
32
|
-
return tokenData;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Parse a resume token to get the timestamp
|
|
36
|
-
*/
|
|
37
|
-
parseResumeToken(token) {
|
|
38
|
-
try {
|
|
39
|
-
const data = JSON.parse(Buffer.from(token._data, 'base64').toString('utf-8'));
|
|
40
|
-
return new plugins.bson.Timestamp({ t: data.ts.t, i: data.ts.i });
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
throw new Error('Invalid resume token');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Record an insert operation
|
|
48
|
-
*/
|
|
49
|
-
async recordInsert(dbName, collName, document, txnInfo) {
|
|
50
|
-
const entry = {
|
|
51
|
-
ts: this.generateTimestamp(),
|
|
52
|
-
op: 'i',
|
|
53
|
-
ns: `${dbName}.${collName}`,
|
|
54
|
-
o: document,
|
|
55
|
-
...txnInfo,
|
|
56
|
-
};
|
|
57
|
-
await this.storage.appendOpLog(entry);
|
|
58
|
-
this.notifyListeners(entry);
|
|
59
|
-
return entry;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Record an update operation
|
|
63
|
-
*/
|
|
64
|
-
async recordUpdate(dbName, collName, filter, update, txnInfo) {
|
|
65
|
-
const entry = {
|
|
66
|
-
ts: this.generateTimestamp(),
|
|
67
|
-
op: 'u',
|
|
68
|
-
ns: `${dbName}.${collName}`,
|
|
69
|
-
o: update,
|
|
70
|
-
o2: filter,
|
|
71
|
-
...txnInfo,
|
|
72
|
-
};
|
|
73
|
-
await this.storage.appendOpLog(entry);
|
|
74
|
-
this.notifyListeners(entry);
|
|
75
|
-
return entry;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Record a delete operation
|
|
79
|
-
*/
|
|
80
|
-
async recordDelete(dbName, collName, filter, txnInfo) {
|
|
81
|
-
const entry = {
|
|
82
|
-
ts: this.generateTimestamp(),
|
|
83
|
-
op: 'd',
|
|
84
|
-
ns: `${dbName}.${collName}`,
|
|
85
|
-
o: filter,
|
|
86
|
-
...txnInfo,
|
|
87
|
-
};
|
|
88
|
-
await this.storage.appendOpLog(entry);
|
|
89
|
-
this.notifyListeners(entry);
|
|
90
|
-
return entry;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Record a command (drop, rename, etc.)
|
|
94
|
-
*/
|
|
95
|
-
async recordCommand(dbName, command) {
|
|
96
|
-
const entry = {
|
|
97
|
-
ts: this.generateTimestamp(),
|
|
98
|
-
op: 'c',
|
|
99
|
-
ns: `${dbName}.$cmd`,
|
|
100
|
-
o: command,
|
|
101
|
-
};
|
|
102
|
-
await this.storage.appendOpLog(entry);
|
|
103
|
-
this.notifyListeners(entry);
|
|
104
|
-
return entry;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Get oplog entries after a timestamp
|
|
108
|
-
*/
|
|
109
|
-
async getEntriesAfter(ts, limit) {
|
|
110
|
-
return this.storage.getOpLogAfter(ts, limit);
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Get the latest timestamp
|
|
114
|
-
*/
|
|
115
|
-
async getLatestTimestamp() {
|
|
116
|
-
return this.storage.getLatestOpLogTimestamp();
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Subscribe to oplog changes (for change streams)
|
|
120
|
-
*/
|
|
121
|
-
subscribe(listener) {
|
|
122
|
-
this.listeners.push(listener);
|
|
123
|
-
return () => {
|
|
124
|
-
const idx = this.listeners.indexOf(listener);
|
|
125
|
-
if (idx >= 0) {
|
|
126
|
-
this.listeners.splice(idx, 1);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Notify all listeners of a new entry
|
|
132
|
-
*/
|
|
133
|
-
notifyListeners(entry) {
|
|
134
|
-
for (const listener of this.listeners) {
|
|
135
|
-
try {
|
|
136
|
-
listener(entry);
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
console.error('Error in oplog listener:', error);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Convert an oplog entry to a change stream document
|
|
145
|
-
*/
|
|
146
|
-
opLogEntryToChangeEvent(entry, fullDocument, fullDocumentBeforeChange) {
|
|
147
|
-
const [db, coll] = entry.ns.split('.');
|
|
148
|
-
const resumeToken = this.generateResumeToken(entry.ts);
|
|
149
|
-
const baseEvent = {
|
|
150
|
-
_id: resumeToken,
|
|
151
|
-
ns: { db, coll: coll === '$cmd' ? undefined : coll },
|
|
152
|
-
clusterTime: entry.ts,
|
|
153
|
-
};
|
|
154
|
-
switch (entry.op) {
|
|
155
|
-
case 'i':
|
|
156
|
-
return {
|
|
157
|
-
...baseEvent,
|
|
158
|
-
operationType: 'insert',
|
|
159
|
-
fullDocument: fullDocument || entry.o,
|
|
160
|
-
documentKey: entry.o._id ? { _id: entry.o._id } : undefined,
|
|
161
|
-
};
|
|
162
|
-
case 'u':
|
|
163
|
-
const updateEvent = {
|
|
164
|
-
...baseEvent,
|
|
165
|
-
operationType: 'update',
|
|
166
|
-
documentKey: entry.o2?._id ? { _id: entry.o2._id } : undefined,
|
|
167
|
-
};
|
|
168
|
-
if (fullDocument) {
|
|
169
|
-
updateEvent.fullDocument = fullDocument;
|
|
170
|
-
}
|
|
171
|
-
if (fullDocumentBeforeChange) {
|
|
172
|
-
updateEvent.fullDocumentBeforeChange = fullDocumentBeforeChange;
|
|
173
|
-
}
|
|
174
|
-
// Parse update description
|
|
175
|
-
if (entry.o.$set || entry.o.$unset) {
|
|
176
|
-
updateEvent.updateDescription = {
|
|
177
|
-
updatedFields: entry.o.$set || {},
|
|
178
|
-
removedFields: entry.o.$unset ? Object.keys(entry.o.$unset) : [],
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
return updateEvent;
|
|
182
|
-
case 'd':
|
|
183
|
-
return {
|
|
184
|
-
...baseEvent,
|
|
185
|
-
operationType: 'delete',
|
|
186
|
-
documentKey: entry.o._id ? { _id: entry.o._id } : undefined,
|
|
187
|
-
fullDocumentBeforeChange,
|
|
188
|
-
};
|
|
189
|
-
case 'c':
|
|
190
|
-
if (entry.o.drop) {
|
|
191
|
-
return {
|
|
192
|
-
...baseEvent,
|
|
193
|
-
operationType: 'drop',
|
|
194
|
-
ns: { db, coll: entry.o.drop },
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
if (entry.o.dropDatabase) {
|
|
198
|
-
return {
|
|
199
|
-
...baseEvent,
|
|
200
|
-
operationType: 'dropDatabase',
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
if (entry.o.renameCollection) {
|
|
204
|
-
return {
|
|
205
|
-
...baseEvent,
|
|
206
|
-
operationType: 'rename',
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
return {
|
|
210
|
-
...baseEvent,
|
|
211
|
-
operationType: 'invalidate',
|
|
212
|
-
};
|
|
213
|
-
default:
|
|
214
|
-
return {
|
|
215
|
-
...baseEvent,
|
|
216
|
-
operationType: 'invalidate',
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3BMb2cuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy90c19zbWFydGRiL3N0b3JhZ2UvT3BMb2cudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFJekM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLEtBQUs7SUFDUixPQUFPLENBQWtCO0lBQ3pCLE9BQU8sR0FBRyxDQUFDLENBQUM7SUFDWixTQUFTLEdBQXdDLEVBQUUsQ0FBQztJQUU1RCxZQUFZLE9BQXdCO1FBQ2xDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7T0FFRztJQUNILGlCQUFpQjtRQUNmLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNmLE9BQU8sSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDM0YsQ0FBQztJQUVEOztPQUVHO0lBQ0gsbUJBQW1CLENBQUMsRUFBMEI7UUFDNUMsb0RBQW9EO1FBQ3BELCtEQUErRDtRQUMvRCxNQUFNLFNBQVMsR0FBRztZQUNoQixLQUFLLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO2dCQUNoQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsRUFBRTtnQkFDN0IsT0FBTyxFQUFFLENBQUM7YUFDWCxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO1NBQ3ZCLENBQUM7UUFDRixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxnQkFBZ0IsQ0FBQyxL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
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import type { IStoredDocument } from '../types/interfaces.js';
|
|
2
|
-
/**
|
|
3
|
-
* WAL entry operation types
|
|
4
|
-
*/
|
|
5
|
-
export type TWalOperation = 'insert' | 'update' | 'delete' | 'checkpoint' | 'begin' | 'commit' | 'abort';
|
|
6
|
-
/**
|
|
7
|
-
* WAL entry structure
|
|
8
|
-
*/
|
|
9
|
-
export interface IWalEntry {
|
|
10
|
-
/** Log Sequence Number - monotonically increasing */
|
|
11
|
-
lsn: number;
|
|
12
|
-
/** Timestamp of the operation */
|
|
13
|
-
timestamp: number;
|
|
14
|
-
/** Operation type */
|
|
15
|
-
operation: TWalOperation;
|
|
16
|
-
/** Database name */
|
|
17
|
-
dbName: string;
|
|
18
|
-
/** Collection name */
|
|
19
|
-
collName: string;
|
|
20
|
-
/** Document ID (hex string) */
|
|
21
|
-
documentId: string;
|
|
22
|
-
/** Document data (BSON serialized, base64 encoded) */
|
|
23
|
-
data?: string;
|
|
24
|
-
/** Previous document data for updates (for rollback) */
|
|
25
|
-
previousData?: string;
|
|
26
|
-
/** Transaction ID if part of a transaction */
|
|
27
|
-
txnId?: string;
|
|
28
|
-
/** CRC32 checksum of the entry (excluding this field) */
|
|
29
|
-
checksum: number;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Write-Ahead Log (WAL) for durability and crash recovery
|
|
33
|
-
*
|
|
34
|
-
* The WAL ensures durability by writing operations to a log file before
|
|
35
|
-
* they are applied to the main storage. On crash recovery, uncommitted
|
|
36
|
-
* operations can be replayed to restore the database to a consistent state.
|
|
37
|
-
*/
|
|
38
|
-
export declare class WAL {
|
|
39
|
-
private walPath;
|
|
40
|
-
private currentLsn;
|
|
41
|
-
private lastCheckpointLsn;
|
|
42
|
-
private entries;
|
|
43
|
-
private isInitialized;
|
|
44
|
-
private fs;
|
|
45
|
-
private uncommittedTxns;
|
|
46
|
-
private checkpointInterval;
|
|
47
|
-
constructor(walPath: string, options?: {
|
|
48
|
-
checkpointInterval?: number;
|
|
49
|
-
});
|
|
50
|
-
/**
|
|
51
|
-
* Initialize the WAL, loading existing entries and recovering if needed
|
|
52
|
-
*/
|
|
53
|
-
initialize(): Promise<{
|
|
54
|
-
recoveredEntries: IWalEntry[];
|
|
55
|
-
}>;
|
|
56
|
-
/**
|
|
57
|
-
* Log an insert operation
|
|
58
|
-
*/
|
|
59
|
-
logInsert(dbName: string, collName: string, doc: IStoredDocument, txnId?: string): Promise<number>;
|
|
60
|
-
/**
|
|
61
|
-
* Log an update operation
|
|
62
|
-
*/
|
|
63
|
-
logUpdate(dbName: string, collName: string, oldDoc: IStoredDocument, newDoc: IStoredDocument, txnId?: string): Promise<number>;
|
|
64
|
-
/**
|
|
65
|
-
* Log a delete operation
|
|
66
|
-
*/
|
|
67
|
-
logDelete(dbName: string, collName: string, doc: IStoredDocument, txnId?: string): Promise<number>;
|
|
68
|
-
/**
|
|
69
|
-
* Log transaction begin
|
|
70
|
-
*/
|
|
71
|
-
logBeginTransaction(txnId: string): Promise<number>;
|
|
72
|
-
/**
|
|
73
|
-
* Log transaction commit
|
|
74
|
-
*/
|
|
75
|
-
logCommitTransaction(txnId: string): Promise<number>;
|
|
76
|
-
/**
|
|
77
|
-
* Log transaction abort
|
|
78
|
-
*/
|
|
79
|
-
logAbortTransaction(txnId: string): Promise<number>;
|
|
80
|
-
/**
|
|
81
|
-
* Get entries to roll back for an aborted transaction
|
|
82
|
-
*/
|
|
83
|
-
getTransactionEntries(txnId: string): IWalEntry[];
|
|
84
|
-
/**
|
|
85
|
-
* Create a checkpoint - marks a consistent point in the log
|
|
86
|
-
*/
|
|
87
|
-
checkpoint(): Promise<number>;
|
|
88
|
-
/**
|
|
89
|
-
* Truncate the WAL file, removing entries before the last checkpoint
|
|
90
|
-
*/
|
|
91
|
-
private truncate;
|
|
92
|
-
/**
|
|
93
|
-
* Get current LSN
|
|
94
|
-
*/
|
|
95
|
-
getCurrentLsn(): number;
|
|
96
|
-
/**
|
|
97
|
-
* Get entries after a specific LSN (for recovery)
|
|
98
|
-
*/
|
|
99
|
-
getEntriesAfter(lsn: number): IWalEntry[];
|
|
100
|
-
/**
|
|
101
|
-
* Close the WAL
|
|
102
|
-
*/
|
|
103
|
-
close(): Promise<void>;
|
|
104
|
-
private appendEntry;
|
|
105
|
-
private serializeDocument;
|
|
106
|
-
private deserializeDocument;
|
|
107
|
-
private calculateChecksum;
|
|
108
|
-
private verifyChecksum;
|
|
109
|
-
/**
|
|
110
|
-
* Recover document from WAL entry
|
|
111
|
-
*/
|
|
112
|
-
recoverDocument(entry: IWalEntry): IStoredDocument | null;
|
|
113
|
-
/**
|
|
114
|
-
* Recover previous document state from WAL entry (for rollback)
|
|
115
|
-
*/
|
|
116
|
-
recoverPreviousDocument(entry: IWalEntry): IStoredDocument | null;
|
|
117
|
-
}
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
/**
|
|
3
|
-
* Write-Ahead Log (WAL) for durability and crash recovery
|
|
4
|
-
*
|
|
5
|
-
* The WAL ensures durability by writing operations to a log file before
|
|
6
|
-
* they are applied to the main storage. On crash recovery, uncommitted
|
|
7
|
-
* operations can be replayed to restore the database to a consistent state.
|
|
8
|
-
*/
|
|
9
|
-
export class WAL {
|
|
10
|
-
walPath;
|
|
11
|
-
currentLsn = 0;
|
|
12
|
-
lastCheckpointLsn = 0;
|
|
13
|
-
entries = [];
|
|
14
|
-
isInitialized = false;
|
|
15
|
-
fs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
|
16
|
-
// In-memory uncommitted entries per transaction
|
|
17
|
-
uncommittedTxns = new Map();
|
|
18
|
-
// Checkpoint interval (number of entries between checkpoints)
|
|
19
|
-
checkpointInterval = 1000;
|
|
20
|
-
constructor(walPath, options) {
|
|
21
|
-
this.walPath = walPath;
|
|
22
|
-
if (options?.checkpointInterval) {
|
|
23
|
-
this.checkpointInterval = options.checkpointInterval;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Initialize the WAL, loading existing entries and recovering if needed
|
|
28
|
-
*/
|
|
29
|
-
async initialize() {
|
|
30
|
-
if (this.isInitialized) {
|
|
31
|
-
return { recoveredEntries: [] };
|
|
32
|
-
}
|
|
33
|
-
// Ensure WAL directory exists
|
|
34
|
-
const walDir = this.walPath.substring(0, this.walPath.lastIndexOf('/'));
|
|
35
|
-
if (walDir) {
|
|
36
|
-
await this.fs.directory(walDir).recursive().create();
|
|
37
|
-
}
|
|
38
|
-
// Try to load existing WAL
|
|
39
|
-
const exists = await this.fs.file(this.walPath).exists();
|
|
40
|
-
if (exists) {
|
|
41
|
-
const content = await this.fs.file(this.walPath).encoding('utf8').read();
|
|
42
|
-
const lines = content.split('\n').filter(line => line.trim());
|
|
43
|
-
for (const line of lines) {
|
|
44
|
-
try {
|
|
45
|
-
const entry = JSON.parse(line);
|
|
46
|
-
// Verify checksum
|
|
47
|
-
if (this.verifyChecksum(entry)) {
|
|
48
|
-
this.entries.push(entry);
|
|
49
|
-
if (entry.lsn > this.currentLsn) {
|
|
50
|
-
this.currentLsn = entry.lsn;
|
|
51
|
-
}
|
|
52
|
-
if (entry.operation === 'checkpoint') {
|
|
53
|
-
this.lastCheckpointLsn = entry.lsn;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
// Skip corrupted entries
|
|
59
|
-
console.warn('Skipping corrupted WAL entry');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
this.isInitialized = true;
|
|
64
|
-
// Return entries after last checkpoint that need recovery
|
|
65
|
-
const recoveredEntries = this.entries.filter(e => e.lsn > this.lastCheckpointLsn &&
|
|
66
|
-
(e.operation === 'insert' || e.operation === 'update' || e.operation === 'delete'));
|
|
67
|
-
return { recoveredEntries };
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Log an insert operation
|
|
71
|
-
*/
|
|
72
|
-
async logInsert(dbName, collName, doc, txnId) {
|
|
73
|
-
return this.appendEntry({
|
|
74
|
-
operation: 'insert',
|
|
75
|
-
dbName,
|
|
76
|
-
collName,
|
|
77
|
-
documentId: doc._id.toHexString(),
|
|
78
|
-
data: this.serializeDocument(doc),
|
|
79
|
-
txnId,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Log an update operation
|
|
84
|
-
*/
|
|
85
|
-
async logUpdate(dbName, collName, oldDoc, newDoc, txnId) {
|
|
86
|
-
return this.appendEntry({
|
|
87
|
-
operation: 'update',
|
|
88
|
-
dbName,
|
|
89
|
-
collName,
|
|
90
|
-
documentId: oldDoc._id.toHexString(),
|
|
91
|
-
data: this.serializeDocument(newDoc),
|
|
92
|
-
previousData: this.serializeDocument(oldDoc),
|
|
93
|
-
txnId,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Log a delete operation
|
|
98
|
-
*/
|
|
99
|
-
async logDelete(dbName, collName, doc, txnId) {
|
|
100
|
-
return this.appendEntry({
|
|
101
|
-
operation: 'delete',
|
|
102
|
-
dbName,
|
|
103
|
-
collName,
|
|
104
|
-
documentId: doc._id.toHexString(),
|
|
105
|
-
previousData: this.serializeDocument(doc),
|
|
106
|
-
txnId,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Log transaction begin
|
|
111
|
-
*/
|
|
112
|
-
async logBeginTransaction(txnId) {
|
|
113
|
-
this.uncommittedTxns.set(txnId, []);
|
|
114
|
-
return this.appendEntry({
|
|
115
|
-
operation: 'begin',
|
|
116
|
-
dbName: '',
|
|
117
|
-
collName: '',
|
|
118
|
-
documentId: '',
|
|
119
|
-
txnId,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Log transaction commit
|
|
124
|
-
*/
|
|
125
|
-
async logCommitTransaction(txnId) {
|
|
126
|
-
this.uncommittedTxns.delete(txnId);
|
|
127
|
-
return this.appendEntry({
|
|
128
|
-
operation: 'commit',
|
|
129
|
-
dbName: '',
|
|
130
|
-
collName: '',
|
|
131
|
-
documentId: '',
|
|
132
|
-
txnId,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Log transaction abort
|
|
137
|
-
*/
|
|
138
|
-
async logAbortTransaction(txnId) {
|
|
139
|
-
this.uncommittedTxns.delete(txnId);
|
|
140
|
-
return this.appendEntry({
|
|
141
|
-
operation: 'abort',
|
|
142
|
-
dbName: '',
|
|
143
|
-
collName: '',
|
|
144
|
-
documentId: '',
|
|
145
|
-
txnId,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Get entries to roll back for an aborted transaction
|
|
150
|
-
*/
|
|
151
|
-
getTransactionEntries(txnId) {
|
|
152
|
-
return this.entries.filter(e => e.txnId === txnId);
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Create a checkpoint - marks a consistent point in the log
|
|
156
|
-
*/
|
|
157
|
-
async checkpoint() {
|
|
158
|
-
const lsn = await this.appendEntry({
|
|
159
|
-
operation: 'checkpoint',
|
|
160
|
-
dbName: '',
|
|
161
|
-
collName: '',
|
|
162
|
-
documentId: '',
|
|
163
|
-
});
|
|
164
|
-
this.lastCheckpointLsn = lsn;
|
|
165
|
-
// Truncate old entries (keep only entries after checkpoint)
|
|
166
|
-
await this.truncate();
|
|
167
|
-
return lsn;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Truncate the WAL file, removing entries before the last checkpoint
|
|
171
|
-
*/
|
|
172
|
-
async truncate() {
|
|
173
|
-
// Keep entries after last checkpoint
|
|
174
|
-
const newEntries = this.entries.filter(e => e.lsn >= this.lastCheckpointLsn);
|
|
175
|
-
this.entries = newEntries;
|
|
176
|
-
// Rewrite the WAL file
|
|
177
|
-
const lines = this.entries.map(e => JSON.stringify(e)).join('\n');
|
|
178
|
-
await this.fs.file(this.walPath).encoding('utf8').write(lines);
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Get current LSN
|
|
182
|
-
*/
|
|
183
|
-
getCurrentLsn() {
|
|
184
|
-
return this.currentLsn;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Get entries after a specific LSN (for recovery)
|
|
188
|
-
*/
|
|
189
|
-
getEntriesAfter(lsn) {
|
|
190
|
-
return this.entries.filter(e => e.lsn > lsn);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Close the WAL
|
|
194
|
-
*/
|
|
195
|
-
async close() {
|
|
196
|
-
if (this.isInitialized) {
|
|
197
|
-
// Final checkpoint before close
|
|
198
|
-
await this.checkpoint();
|
|
199
|
-
}
|
|
200
|
-
this.isInitialized = false;
|
|
201
|
-
}
|
|
202
|
-
// ============================================================================
|
|
203
|
-
// Private Methods
|
|
204
|
-
// ============================================================================
|
|
205
|
-
async appendEntry(partial) {
|
|
206
|
-
await this.initialize();
|
|
207
|
-
this.currentLsn++;
|
|
208
|
-
const entry = {
|
|
209
|
-
...partial,
|
|
210
|
-
lsn: this.currentLsn,
|
|
211
|
-
timestamp: Date.now(),
|
|
212
|
-
checksum: 0, // Will be calculated
|
|
213
|
-
};
|
|
214
|
-
// Calculate checksum
|
|
215
|
-
entry.checksum = this.calculateChecksum(entry);
|
|
216
|
-
// Track in transaction if applicable
|
|
217
|
-
if (partial.txnId && this.uncommittedTxns.has(partial.txnId)) {
|
|
218
|
-
this.uncommittedTxns.get(partial.txnId).push(entry);
|
|
219
|
-
}
|
|
220
|
-
// Add to in-memory log
|
|
221
|
-
this.entries.push(entry);
|
|
222
|
-
// Append to file (append mode for durability)
|
|
223
|
-
await this.fs.file(this.walPath).encoding('utf8').append(JSON.stringify(entry) + '\n');
|
|
224
|
-
// Check if we need a checkpoint
|
|
225
|
-
if (this.entries.length - this.lastCheckpointLsn >= this.checkpointInterval) {
|
|
226
|
-
await this.checkpoint();
|
|
227
|
-
}
|
|
228
|
-
return entry.lsn;
|
|
229
|
-
}
|
|
230
|
-
serializeDocument(doc) {
|
|
231
|
-
// Serialize document to BSON and encode as base64
|
|
232
|
-
const bsonData = plugins.bson.serialize(doc);
|
|
233
|
-
return Buffer.from(bsonData).toString('base64');
|
|
234
|
-
}
|
|
235
|
-
deserializeDocument(data) {
|
|
236
|
-
// Decode base64 and deserialize from BSON
|
|
237
|
-
const buffer = Buffer.from(data, 'base64');
|
|
238
|
-
return plugins.bson.deserialize(buffer);
|
|
239
|
-
}
|
|
240
|
-
calculateChecksum(entry) {
|
|
241
|
-
// Simple CRC32-like checksum
|
|
242
|
-
const str = JSON.stringify({
|
|
243
|
-
lsn: entry.lsn,
|
|
244
|
-
timestamp: entry.timestamp,
|
|
245
|
-
operation: entry.operation,
|
|
246
|
-
dbName: entry.dbName,
|
|
247
|
-
collName: entry.collName,
|
|
248
|
-
documentId: entry.documentId,
|
|
249
|
-
data: entry.data,
|
|
250
|
-
previousData: entry.previousData,
|
|
251
|
-
txnId: entry.txnId,
|
|
252
|
-
});
|
|
253
|
-
let crc = 0xFFFFFFFF;
|
|
254
|
-
for (let i = 0; i < str.length; i++) {
|
|
255
|
-
crc ^= str.charCodeAt(i);
|
|
256
|
-
for (let j = 0; j < 8; j++) {
|
|
257
|
-
crc = (crc >>> 1) ^ (crc & 1 ? 0xEDB88320 : 0);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return (~crc) >>> 0;
|
|
261
|
-
}
|
|
262
|
-
verifyChecksum(entry) {
|
|
263
|
-
const savedChecksum = entry.checksum;
|
|
264
|
-
entry.checksum = 0;
|
|
265
|
-
const calculatedChecksum = this.calculateChecksum(entry);
|
|
266
|
-
entry.checksum = savedChecksum;
|
|
267
|
-
return calculatedChecksum === savedChecksum;
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Recover document from WAL entry
|
|
271
|
-
*/
|
|
272
|
-
recoverDocument(entry) {
|
|
273
|
-
if (!entry.data)
|
|
274
|
-
return null;
|
|
275
|
-
return this.deserializeDocument(entry.data);
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Recover previous document state from WAL entry (for rollback)
|
|
279
|
-
*/
|
|
280
|
-
recoverPreviousDocument(entry) {
|
|
281
|
-
if (!entry.previousData)
|
|
282
|
-
return null;
|
|
283
|
-
return this.deserializeDocument(entry.previousData);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiV0FMLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvdHNfc21hcnRkYi9zdG9yYWdlL1dBTC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQTJDekM7Ozs7OztHQU1HO0FBQ0gsTUFBTSxPQUFPLEdBQUc7SUFDTixPQUFPLENBQVM7SUFDaEIsVUFBVSxHQUFXLENBQUMsQ0FBQztJQUN2QixpQkFBaUIsR0FBVyxDQUFDLENBQUM7SUFDOUIsT0FBTyxHQUFnQixFQUFFLENBQUM7SUFDMUIsYUFBYSxHQUFZLEtBQUssQ0FBQztJQUMvQixFQUFFLEdBQUcsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsbUJBQW1CLEVBQUUsQ0FBQyxDQUFDO0lBRXBGLGdEQUFnRDtJQUN4QyxlQUFlLEdBQTZCLElBQUksR0FBRyxFQUFFLENBQUM7SUFFOUQsOERBQThEO0lBQ3RELGtCQUFrQixHQUFXLElBQUksQ0FBQztJQUUxQyxZQUFZLE9BQWUsRUFBRSxPQUF5QztRQUNwRSxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxPQUFPLENBQUMsa0JBQWtCLENBQUM7UUFDdkQsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxVQUFVO1FBQ2QsSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDdkIsT0FBTyxFQUFFLGdCQUFnQixFQUFFLEVBQUUsRUFBRSxDQUFDO1FBQ2xDLENBQUM7UUFFRCw4QkFBOEI7UUFDOUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLEdB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
|