@based/db 0.0.66 → 0.0.68
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 +2 -2
- package/dist/lib/darwin_aarch64/include/selva/colvec.h +71 -0
- package/dist/lib/darwin_aarch64/include/selva/db.h +3 -0
- package/dist/lib/darwin_aarch64/include/selva/fields.h +1 -1
- package/dist/lib/darwin_aarch64/include/selva/types.h +7 -0
- package/dist/lib/darwin_aarch64/libdeflate.dylib +0 -0
- package/dist/lib/darwin_aarch64/libjemalloc_selva.2.dylib +0 -0
- package/dist/lib/darwin_aarch64/libjemalloc_selva.so.2 +0 -0
- package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v24.node +0 -0
- package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
- package/dist/lib/darwin_aarch64/libxxhash.dylib +0 -0
- package/dist/lib/linux_aarch64/include/selva/colvec.h +71 -0
- package/dist/lib/linux_aarch64/include/selva/db.h +3 -0
- package/dist/lib/linux_aarch64/include/selva/fields.h +1 -1
- package/dist/lib/linux_aarch64/include/selva/types.h +7 -0
- package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v24.node +0 -0
- package/dist/lib/linux_aarch64/libselva.so +0 -0
- package/dist/lib/linux_x86_64/include/selva/colvec.h +71 -0
- package/dist/lib/linux_x86_64/include/selva/db.h +3 -0
- package/dist/lib/linux_x86_64/include/selva/fields.h +1 -1
- package/dist/lib/linux_x86_64/include/selva/types.h +7 -0
- package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v24.node +0 -0
- package/dist/lib/linux_x86_64/libselva.so +0 -0
- package/dist/src/client/flushModify.d.ts +2 -1
- package/dist/src/client/flushModify.js +12 -4
- package/dist/src/client/modify/create.js +11 -0
- package/dist/src/client/modify/delete.js +3 -0
- package/dist/src/client/modify/modify.js +2 -2
- package/dist/src/client/modify/setCursor.d.ts +2 -1
- package/dist/src/client/query/BasedDbQuery.d.ts +7 -2
- package/dist/src/client/query/BasedDbQuery.js +111 -14
- package/dist/src/client/query/aggregates/aggregation.js +24 -11
- package/dist/src/client/query/aggregates/types.d.ts +21 -2
- package/dist/src/client/query/aggregates/types.js +34 -1
- package/dist/src/client/query/display.js +8 -2
- package/dist/src/client/query/filter/createVariableFilterBuffer.d.ts +2 -3
- package/dist/src/client/query/filter/createVariableFilterBuffer.js +20 -7
- package/dist/src/client/query/filter/filter.js +13 -3
- package/dist/src/client/query/filter/primitiveFilter.d.ts +1 -2
- package/dist/src/client/query/include/props.js +18 -2
- package/dist/src/client/query/include/toBuffer.js +11 -3
- package/dist/src/client/query/include/walk.js +5 -1
- package/dist/src/client/query/queryDef.js +4 -1
- package/dist/src/client/query/read/read.js +50 -23
- package/dist/src/client/query/registerQuery.js +1 -0
- package/dist/src/client/query/search/index.d.ts +1 -1
- package/dist/src/client/query/search/index.js +21 -7
- package/dist/src/client/query/sort.d.ts +1 -1
- package/dist/src/client/query/toByteCode/default.d.ts +1 -1
- package/dist/src/client/query/toByteCode/default.js +0 -2
- package/dist/src/client/query/toByteCode/toBuffer.js +0 -7
- package/dist/src/client/query/types.d.ts +16 -5
- package/dist/src/client/query/validation.js +25 -2
- package/dist/src/client/xxHash64.d.ts +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/native.d.ts +2 -2
- package/dist/src/native.js +7 -8
- package/dist/src/server/IoWorker.d.ts +8 -0
- package/dist/src/server/IoWorker.js +39 -0
- package/dist/src/server/QueryWorker.d.ts +8 -0
- package/dist/src/server/QueryWorker.js +26 -0
- package/dist/src/server/blocks.d.ts +24 -0
- package/dist/src/server/blocks.js +112 -0
- package/dist/src/server/dbHash.d.ts +1 -1
- package/dist/src/server/index.d.ts +9 -16
- package/dist/src/server/index.js +37 -15
- package/dist/src/server/migrate/index.d.ts +5 -0
- package/dist/src/server/migrate/index.js +10 -7
- package/dist/src/server/save.d.ts +8 -6
- package/dist/src/server/save.js +34 -78
- package/dist/src/server/schema.js +6 -5
- package/dist/src/server/start.js +57 -60
- package/dist/src/server/tree.d.ts +24 -13
- package/dist/src/server/tree.js +95 -66
- package/dist/src/server/workers/DbWorker.d.ts +17 -0
- package/dist/src/server/{DbWorker.js → workers/DbWorker.js} +15 -17
- package/dist/src/server/workers/io_worker.js +39 -0
- package/dist/src/server/workers/io_worker_types.d.ts +12 -0
- package/dist/src/server/workers/io_worker_types.js +2 -0
- package/dist/src/server/workers/query_worker.d.ts +1 -0
- package/dist/src/server/workers/query_worker.js +4 -0
- package/dist/src/server/workers/worker.d.ts +1 -0
- package/dist/src/server/workers/worker.js +41 -0
- package/dist/src/shared/Emitter.d.ts +1 -0
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.js +1 -1
- package/package.json +3 -3
- package/dist/lib/darwin_aarch64/include/selva/history.h +0 -64
- package/dist/lib/linux_aarch64/include/selva/history.h +0 -64
- package/dist/lib/linux_x86_64/include/selva/history.h +0 -64
- package/dist/src/client/query/serialize.d.ts +0 -4
- package/dist/src/client/query/serialize.js +0 -26
- package/dist/src/server/DbWorker.d.ts +0 -13
- package/dist/src/server/csmt/draw-dot.d.ts +0 -4
- package/dist/src/server/csmt/draw-dot.js +0 -38
- package/dist/src/server/csmt/index.d.ts +0 -4
- package/dist/src/server/csmt/index.js +0 -5
- package/dist/src/server/csmt/match.d.ts +0 -7
- package/dist/src/server/csmt/match.js +0 -10
- package/dist/src/server/csmt/memebership-proof.d.ts +0 -7
- package/dist/src/server/csmt/memebership-proof.js +0 -122
- package/dist/src/server/csmt/tree-utils.d.ts +0 -6
- package/dist/src/server/csmt/tree-utils.js +0 -33
- package/dist/src/server/csmt/tree.d.ts +0 -3
- package/dist/src/server/csmt/tree.js +0 -270
- package/dist/src/server/csmt/types.d.ts +0 -46
- package/dist/src/server/csmt/types.js +0 -2
- package/dist/src/server/worker.js +0 -33
- /package/dist/src/server/{worker.d.ts → workers/io_worker.d.ts} +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import native from '../native.js';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { equals } from '@saulx/utils';
|
|
4
|
+
import { VerifTree, destructureTreeKey, makeTreeKey, } from './tree.js';
|
|
5
|
+
/**
|
|
6
|
+
* Save a block.
|
|
7
|
+
*/
|
|
8
|
+
export function saveBlock(db, typeId, start, end) {
|
|
9
|
+
const hash = new Uint8Array(16);
|
|
10
|
+
const mtKey = makeTreeKey(typeId, start);
|
|
11
|
+
const file = VerifTree.blockSdbFile(typeId, start, end);
|
|
12
|
+
const path = join(db.fileSystemPath, file);
|
|
13
|
+
const err = native.saveBlock(path, typeId, start, db.dbCtxExternal, hash);
|
|
14
|
+
if (err == -8) {
|
|
15
|
+
// TODO ENOENT
|
|
16
|
+
db.verifTree.remove(mtKey);
|
|
17
|
+
}
|
|
18
|
+
else if (err) {
|
|
19
|
+
// TODO print the error string
|
|
20
|
+
console.error(`Save ${typeId}:${start}-${end} failed: ${err}`);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
db.verifTree.update(mtKey, hash);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Load a block (typically of a partial type) back to memory.
|
|
28
|
+
*/
|
|
29
|
+
export async function loadBlock(db, def, start) {
|
|
30
|
+
const key = makeTreeKey(def.id, start);
|
|
31
|
+
const block = db.verifTree.getBlock(key);
|
|
32
|
+
if (!block) {
|
|
33
|
+
throw new Error(`No such block: ${key}`);
|
|
34
|
+
}
|
|
35
|
+
if (block.loadPromise) {
|
|
36
|
+
return block.loadPromise;
|
|
37
|
+
}
|
|
38
|
+
const prevHash = block.hash;
|
|
39
|
+
const filename = db.verifTree.getBlockFile(block);
|
|
40
|
+
const p = db.ioWorker.loadBlock(join(db.fileSystemPath, filename));
|
|
41
|
+
block.loadPromise = p;
|
|
42
|
+
await p;
|
|
43
|
+
delete block.loadPromise;
|
|
44
|
+
// Update and verify the hash
|
|
45
|
+
const hash = new Uint8Array(16);
|
|
46
|
+
const end = start + def.blockCapacity - 1;
|
|
47
|
+
const res = native.getNodeRangeHash(def.id, start, end, hash, db.dbCtxExternal);
|
|
48
|
+
if (res) {
|
|
49
|
+
const key = makeTreeKey(def.id, start);
|
|
50
|
+
db.verifTree.update(key, hash);
|
|
51
|
+
if (!equals(prevHash, hash)) {
|
|
52
|
+
throw new Error('Block hash mismatch');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Save a block and remove it from memory.
|
|
58
|
+
*/
|
|
59
|
+
export async function unloadBlock(db, def, start) {
|
|
60
|
+
const typeId = def.id;
|
|
61
|
+
const end = start + def.blockCapacity - 1;
|
|
62
|
+
const key = makeTreeKey(typeId, start);
|
|
63
|
+
const block = db.verifTree.getBlock(key);
|
|
64
|
+
if (!block) {
|
|
65
|
+
throw new Error(`No such block: ${key}`);
|
|
66
|
+
}
|
|
67
|
+
const filepath = join(db.fileSystemPath, VerifTree.blockSdbFile(typeId, start, end));
|
|
68
|
+
try {
|
|
69
|
+
const hash = await db.ioWorker.unloadBlock(filepath, typeId, start);
|
|
70
|
+
native.delBlock(db.dbCtxExternal, typeId, start);
|
|
71
|
+
db.verifTree.update(key, hash, false);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
// TODO Proper logging
|
|
75
|
+
// TODO err == -8 == SELVA_ENOENT => db.verifTree.remove(key) ??
|
|
76
|
+
console.error(`Save ${typeId}:${start}-${end} failed`);
|
|
77
|
+
console.error(e);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Execute cb() for each block in memory.
|
|
82
|
+
*/
|
|
83
|
+
export function foreachBlock(db, def, cb, includeEmptyBlocks = false) {
|
|
84
|
+
const step = def.blockCapacity;
|
|
85
|
+
for (let start = 1; start <= def.lastId; start += step) {
|
|
86
|
+
const end = start + step - 1;
|
|
87
|
+
const hash = new Uint8Array(16);
|
|
88
|
+
const res = native.getNodeRangeHash(def.id, start, end, hash, db.dbCtxExternal);
|
|
89
|
+
if (res || includeEmptyBlocks) {
|
|
90
|
+
cb(start, end, hash);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Execute cb() for each dirty block.
|
|
96
|
+
* A dirty block is one that is changed in memory but not yet persisted in the
|
|
97
|
+
* file system.
|
|
98
|
+
*/
|
|
99
|
+
export function foreachDirtyBlock(db, cb) {
|
|
100
|
+
const typeIdMap = {};
|
|
101
|
+
for (const typeName in db.schemaTypesParsed) {
|
|
102
|
+
const type = db.schemaTypesParsed[typeName];
|
|
103
|
+
const typeId = type.id;
|
|
104
|
+
typeIdMap[typeId] = type;
|
|
105
|
+
}
|
|
106
|
+
for (const mtKey of db.dirtyRanges) {
|
|
107
|
+
const [typeId, start] = destructureTreeKey(mtKey);
|
|
108
|
+
const end = start + typeIdMap[typeId].blockCapacity - 1;
|
|
109
|
+
cb(mtKey, typeId, start, end);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=blocks.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { LangName, StrictSchema } from '@based/schema';
|
|
2
|
-
import { createTree } from './csmt/index.js';
|
|
3
2
|
import { StartOpts } from './start.js';
|
|
4
|
-
import {
|
|
3
|
+
import { VerifTree } from './tree.js';
|
|
5
4
|
import { TransformFns } from './migrate/index.js';
|
|
6
5
|
import exitHook from 'exit-hook';
|
|
7
6
|
import { SchemaChecksum } from '../schema.js';
|
|
8
|
-
import {
|
|
7
|
+
import { IoWorker } from './IoWorker.js';
|
|
8
|
+
import { QueryWorker } from './QueryWorker.js';
|
|
9
9
|
import { DbShared } from '../shared/DbBase.js';
|
|
10
10
|
declare class SortIndex {
|
|
11
11
|
constructor(buf: Uint8Array, dbCtxExternal: any);
|
|
@@ -21,14 +21,10 @@ export declare class DbServer extends DbShared {
|
|
|
21
21
|
migrating: number;
|
|
22
22
|
saveInProgress: boolean;
|
|
23
23
|
fileSystemPath: string;
|
|
24
|
-
|
|
24
|
+
verifTree: VerifTree;
|
|
25
25
|
dirtyRanges: Set<number>;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
digest: (encoding?: "hex") => Uint8Array | string;
|
|
29
|
-
reset: () => void;
|
|
30
|
-
};
|
|
31
|
-
workers: DbWorker[];
|
|
26
|
+
ioWorker: IoWorker;
|
|
27
|
+
workers: QueryWorker[];
|
|
32
28
|
availableWorkerIndex: number;
|
|
33
29
|
processingQueries: number;
|
|
34
30
|
modifyQueue: Uint8Array[];
|
|
@@ -47,11 +43,8 @@ export declare class DbServer extends DbShared {
|
|
|
47
43
|
save(opts?: {
|
|
48
44
|
forceFullDump?: boolean;
|
|
49
45
|
}): Promise<void>;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
digest: (encoding?: "hex") => Uint8Array | string;
|
|
53
|
-
reset: () => void;
|
|
54
|
-
};
|
|
46
|
+
loadBlock(typeName: string, nodeId: number): Promise<void>;
|
|
47
|
+
unloadBlock(typeName: string, nodeId: number): Promise<void>;
|
|
55
48
|
sortIndexes: {
|
|
56
49
|
[type: number]: {
|
|
57
50
|
[field: number]: {
|
|
@@ -69,7 +62,7 @@ export declare class DbServer extends DbShared {
|
|
|
69
62
|
createSortIndexBuffer(typeId: number, field: number, start: number, lang: number): SortIndex;
|
|
70
63
|
setSchema(strictSchema: StrictSchema, transformFns?: TransformFns): Promise<SchemaChecksum>;
|
|
71
64
|
modify(bufWithHash: Uint8Array): Record<number, number> | null;
|
|
72
|
-
addToQueryQueue(resolve: any, buf: any): Promise<Uint8Array
|
|
65
|
+
addToQueryQueue(resolve: any, buf: any): Promise<Uint8Array<ArrayBuffer>>;
|
|
73
66
|
getQueryBuf(buf: Uint8Array, fromQueue?: boolean): Promise<Uint8Array>;
|
|
74
67
|
onQueryEnd(): any;
|
|
75
68
|
stop(noSave?: boolean): Promise<void>;
|
package/dist/src/server/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import native from '../native.js';
|
|
2
|
-
import createDbHash from './dbHash.js';
|
|
3
2
|
import { rm } from 'node:fs/promises';
|
|
4
3
|
import { langCodesMap } from '@based/schema';
|
|
5
4
|
import { start } from './start.js';
|
|
6
|
-
import {
|
|
5
|
+
import { destructureTreeKey, makeTreeKeyFromNodeId } from './tree.js';
|
|
7
6
|
import { save } from './save.js';
|
|
8
7
|
import { setTimeout } from 'node:timers/promises';
|
|
9
8
|
import { migrate } from './migrate/index.js';
|
|
@@ -14,6 +13,7 @@ import { strictSchemaToDbSchema } from './schema.js';
|
|
|
14
13
|
import { DbShared } from '../shared/DbBase.js';
|
|
15
14
|
import { setNativeSchema, setSchemaOnServer, writeSchemaFile, } from './schema.js';
|
|
16
15
|
import { resizeModifyDirtyRanges } from './resizeModifyDirtyRanges.js';
|
|
16
|
+
import { loadBlock, unloadBlock } from './blocks.js';
|
|
17
17
|
const emptyUint8Array = new Uint8Array(0);
|
|
18
18
|
class SortIndex {
|
|
19
19
|
constructor(buf, dbCtxExternal) {
|
|
@@ -31,12 +31,12 @@ export class DbServer extends DbShared {
|
|
|
31
31
|
migrating = null;
|
|
32
32
|
saveInProgress = false;
|
|
33
33
|
fileSystemPath;
|
|
34
|
-
|
|
34
|
+
verifTree;
|
|
35
35
|
dirtyRanges = new Set();
|
|
36
|
-
|
|
36
|
+
ioWorker;
|
|
37
37
|
workers = [];
|
|
38
38
|
availableWorkerIndex = -1;
|
|
39
|
-
processingQueries = 0;
|
|
39
|
+
processingQueries = 0; // semaphore for locking writes
|
|
40
40
|
modifyQueue = [];
|
|
41
41
|
queryQueue = new Map();
|
|
42
42
|
stopped; // = true does not work
|
|
@@ -60,11 +60,34 @@ export class DbServer extends DbShared {
|
|
|
60
60
|
save(opts) {
|
|
61
61
|
return save(this, false, opts?.forceFullDump ?? false);
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
async loadBlock(typeName, nodeId) {
|
|
64
|
+
const def = this.schemaTypesParsed[typeName];
|
|
65
|
+
if (!def) {
|
|
66
|
+
throw new Error('Type not found');
|
|
67
|
+
}
|
|
68
|
+
const typeId = def.id;
|
|
69
|
+
const key = makeTreeKeyFromNodeId(typeId, def.blockCapacity, nodeId);
|
|
70
|
+
const [, start] = destructureTreeKey(key);
|
|
71
|
+
const block = this.verifTree.getBlock(key);
|
|
72
|
+
if (!block) {
|
|
73
|
+
throw new Error('Block not found');
|
|
74
|
+
}
|
|
75
|
+
await loadBlock(this, def, start);
|
|
76
|
+
}
|
|
77
|
+
async unloadBlock(typeName, nodeId) {
|
|
78
|
+
const def = this.schemaTypesParsed[typeName];
|
|
79
|
+
if (!def) {
|
|
80
|
+
throw new Error('Type not found');
|
|
81
|
+
}
|
|
82
|
+
const typeId = def.id;
|
|
83
|
+
const key = makeTreeKeyFromNodeId(typeId, def.blockCapacity, nodeId);
|
|
84
|
+
const [, start] = destructureTreeKey(key);
|
|
85
|
+
const block = this.verifTree.getBlock(key);
|
|
86
|
+
if (!block) {
|
|
87
|
+
throw new Error('Block not found');
|
|
88
|
+
}
|
|
89
|
+
await unloadBlock(this, def, start);
|
|
90
|
+
}
|
|
68
91
|
sortIndexes;
|
|
69
92
|
cleanupTimer;
|
|
70
93
|
cleanup() {
|
|
@@ -299,7 +322,7 @@ export class DbServer extends DbShared {
|
|
|
299
322
|
while (typesSize--) {
|
|
300
323
|
const typeId = readUint16(buf, i);
|
|
301
324
|
const def = this.schemaTypesParsedById[typeId];
|
|
302
|
-
const key =
|
|
325
|
+
const key = makeTreeKeyFromNodeId(def.id, def.blockCapacity, def.lastId);
|
|
303
326
|
this.dirtyRanges.add(key);
|
|
304
327
|
i += 10;
|
|
305
328
|
}
|
|
@@ -307,7 +330,7 @@ export class DbServer extends DbShared {
|
|
|
307
330
|
while (i < end) {
|
|
308
331
|
// const key = view.getFloat64(i, true)
|
|
309
332
|
// These node ranges may not actually exist
|
|
310
|
-
//this.dirtyRanges.add(key)
|
|
333
|
+
// this.dirtyRanges.add(key)
|
|
311
334
|
i += 8;
|
|
312
335
|
}
|
|
313
336
|
resizeModifyDirtyRanges(this);
|
|
@@ -334,7 +357,6 @@ export class DbServer extends DbShared {
|
|
|
334
357
|
resolve(new Error('Query queue exceeded'));
|
|
335
358
|
return;
|
|
336
359
|
}
|
|
337
|
-
// TODO should we check here as well? Already will check in DbWorker
|
|
338
360
|
const schemaChecksum = readUint64(buf, buf.byteLength - 8);
|
|
339
361
|
if (schemaChecksum !== this.schema?.hash) {
|
|
340
362
|
return Promise.resolve(new Uint8Array(1));
|
|
@@ -389,7 +411,6 @@ export class DbServer extends DbShared {
|
|
|
389
411
|
}
|
|
390
412
|
}
|
|
391
413
|
onQueryEnd() {
|
|
392
|
-
this.processingQueries--;
|
|
393
414
|
if (this.processingQueries === 0) {
|
|
394
415
|
if (this.modifyQueue.length) {
|
|
395
416
|
const modifyQueue = this.modifyQueue;
|
|
@@ -432,7 +453,8 @@ export class DbServer extends DbShared {
|
|
|
432
453
|
if (!noSave) {
|
|
433
454
|
await this.save();
|
|
434
455
|
}
|
|
435
|
-
await
|
|
456
|
+
await this.ioWorker.terminate();
|
|
457
|
+
await Promise.all(this.workers.map((worker) => worker.terminate()));
|
|
436
458
|
this.workers = [];
|
|
437
459
|
native.stop(this.dbCtxExternal);
|
|
438
460
|
await setTimeout(100);
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { DbServer } from '../index.js';
|
|
2
2
|
import { DbSchema } from '../../schema.js';
|
|
3
|
+
export type MigrateRange = {
|
|
4
|
+
typeId: number;
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
};
|
|
3
8
|
type TransformFn = (node: Record<string, any>) => Record<string, any> | [string, Record<string, any>];
|
|
4
9
|
export type TransformFns = Record<string, TransformFn>;
|
|
5
10
|
export declare const migrate: (server: DbServer, fromSchema: DbSchema, toSchema: DbSchema, transform?: TransformFns) => Promise<void>;
|
|
@@ -2,7 +2,8 @@ import { BasedDb, save } from '../../index.js';
|
|
|
2
2
|
import { dirname, join } from 'path';
|
|
3
3
|
import { Worker, MessageChannel, receiveMessageOnPort, } from 'node:worker_threads';
|
|
4
4
|
import native from '../../native.js';
|
|
5
|
-
import {
|
|
5
|
+
import { destructureTreeKey } from '../tree.js';
|
|
6
|
+
import { foreachDirtyBlock } from '../blocks.js';
|
|
6
7
|
import { fileURLToPath } from 'url';
|
|
7
8
|
import { setNativeSchema, setSchemaOnServer, writeSchemaFile, } from '../schema.js';
|
|
8
9
|
import { setToAwake, waitUntilSleeping } from './utils.js';
|
|
@@ -90,11 +91,11 @@ export const migrate = async (server, fromSchema, toSchema, transform) => {
|
|
|
90
91
|
let i = 0;
|
|
91
92
|
let rangesToMigrate = [];
|
|
92
93
|
await save(server, false, false, true);
|
|
93
|
-
server.
|
|
94
|
-
const [
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
rangesToMigrate.push(
|
|
94
|
+
server.verifTree.foreachBlock((block) => {
|
|
95
|
+
const [typeId, start] = destructureTreeKey(block.key);
|
|
96
|
+
const def = server.schemaTypesParsedById[typeId];
|
|
97
|
+
const end = start + def.blockCapacity - 1;
|
|
98
|
+
rangesToMigrate.push({ typeId, start, end });
|
|
98
99
|
});
|
|
99
100
|
await waitUntilSleeping(workerState);
|
|
100
101
|
while (i < rangesToMigrate.length) {
|
|
@@ -108,12 +109,13 @@ export const migrate = async (server, fromSchema, toSchema, transform) => {
|
|
|
108
109
|
setToAwake(workerState, true);
|
|
109
110
|
await waitUntilSleeping(workerState);
|
|
110
111
|
// exec queued modifies
|
|
112
|
+
server.processingQueries--;
|
|
111
113
|
server.onQueryEnd();
|
|
112
114
|
if (i === rangesToMigrate.length) {
|
|
113
115
|
if (server.dirtyRanges.size) {
|
|
114
116
|
rangesToMigrate = [];
|
|
115
117
|
i = 0;
|
|
116
|
-
|
|
118
|
+
foreachDirtyBlock(server, (_mtKey, typeId, start, end) => {
|
|
117
119
|
rangesToMigrate.push({
|
|
118
120
|
typeId,
|
|
119
121
|
start,
|
|
@@ -140,6 +142,7 @@ export const migrate = async (server, fromSchema, toSchema, transform) => {
|
|
|
140
142
|
// pass last node IDS { type: lastId }
|
|
141
143
|
setSchemaOnServer(server, toSchema);
|
|
142
144
|
for (const key in schemaTypesParsed) {
|
|
145
|
+
// maybe only send the lastId
|
|
143
146
|
const def = server.schemaTypesParsed[key];
|
|
144
147
|
def.lastId = schemaTypesParsed[key].lastId;
|
|
145
148
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { DbServer } from './index.js';
|
|
2
|
+
type RangeDump = {
|
|
3
|
+
file: string;
|
|
4
|
+
hash: string;
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
};
|
|
2
8
|
export type Writelog = {
|
|
3
9
|
ts: number;
|
|
4
10
|
types: {
|
|
@@ -10,12 +16,8 @@ export type Writelog = {
|
|
|
10
16
|
hash: string;
|
|
11
17
|
commonDump: string;
|
|
12
18
|
rangeDumps: {
|
|
13
|
-
[t: number]:
|
|
14
|
-
file: string;
|
|
15
|
-
hash: string;
|
|
16
|
-
start: number;
|
|
17
|
-
end: number;
|
|
18
|
-
}[];
|
|
19
|
+
[t: number]: RangeDump[];
|
|
19
20
|
};
|
|
20
21
|
};
|
|
21
22
|
export declare function save<T extends boolean>(db: DbServer, sync?: T, forceFullDump?: boolean, skipMigrationCheck?: boolean): T extends true ? void : Promise<void>;
|
|
23
|
+
export {};
|
package/dist/src/server/save.js
CHANGED
|
@@ -2,30 +2,26 @@ import native from '../native.js';
|
|
|
2
2
|
import { isMainThread } from 'node:worker_threads';
|
|
3
3
|
import { writeFile } from 'node:fs/promises';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import {
|
|
5
|
+
import { VerifTree, destructureTreeKey, } from './tree.js';
|
|
6
|
+
import { saveBlock, foreachBlock, foreachDirtyBlock, } from './blocks.js';
|
|
6
7
|
import { writeFileSync } from 'node:fs';
|
|
7
8
|
import { bufToHex } from '@saulx/utils';
|
|
8
9
|
import { COMMON_SDB_FILE, WRITELOG_FILE } from '../types.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const err = native.saveBlock(path, typeId, start, db.dbCtxExternal, hashOut);
|
|
14
|
-
if (err == -8) {
|
|
15
|
-
// TODO ENOENT
|
|
16
|
-
return ''; // empty range
|
|
10
|
+
function hasPartialTypes(db) {
|
|
11
|
+
let res = false;
|
|
12
|
+
for (let id in db.schemaTypesParsedById) {
|
|
13
|
+
res = res || db.schemaTypesParsedById[id].partial;
|
|
17
14
|
}
|
|
18
|
-
|
|
19
|
-
// TODO print the error string
|
|
20
|
-
console.error(`Save ${typeId}:${start}-${end} failed: ${err}`);
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
return file;
|
|
15
|
+
return res;
|
|
24
16
|
}
|
|
25
17
|
export function save(db, sync = false, forceFullDump = false, skipMigrationCheck = false) {
|
|
26
18
|
if (!(isMainThread && (db.dirtyRanges.size || forceFullDump))) {
|
|
27
19
|
return;
|
|
28
20
|
}
|
|
21
|
+
if (forceFullDump && hasPartialTypes(db)) {
|
|
22
|
+
db.emit('error', 'forceFullDump is not allowed with partial types');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
29
25
|
if (db.migrating && !skipMigrationCheck) {
|
|
30
26
|
db.emit('info', 'Block save db is migrating');
|
|
31
27
|
return;
|
|
@@ -40,62 +36,21 @@ export function save(db, sync = false, forceFullDump = false, skipMigrationCheck
|
|
|
40
36
|
let err;
|
|
41
37
|
err = native.saveCommon(join(db.fileSystemPath, COMMON_SDB_FILE), db.dbCtxExternal);
|
|
42
38
|
if (err) {
|
|
43
|
-
|
|
39
|
+
db.emit('error', `Save common failed: ${err}`);
|
|
44
40
|
// Return ?
|
|
45
41
|
}
|
|
46
42
|
if (forceFullDump) {
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const hash = new Uint8Array(16);
|
|
55
|
-
const file = saveBlock(db, typeId, start, end, hash);
|
|
56
|
-
if (file === null) {
|
|
57
|
-
throw new Error('full dump failed');
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
const data = {
|
|
61
|
-
file,
|
|
62
|
-
typeId,
|
|
63
|
-
start,
|
|
64
|
-
end,
|
|
65
|
-
};
|
|
66
|
-
db.merkleTree.insert(mtKey, hash, data);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
43
|
+
// reset the state just in case
|
|
44
|
+
db.verifTree = new VerifTree(db.schemaTypesParsed);
|
|
45
|
+
// We use db.verifTree.types instead of db.schemaTypesParsed because it's
|
|
46
|
+
// ordered.
|
|
47
|
+
for (const { typeId } of db.verifTree.types()) {
|
|
48
|
+
const def = db.schemaTypesParsedById[typeId];
|
|
49
|
+
foreachBlock(db, def, (start, end, _hash) => saveBlock(db, def.id, start, end));
|
|
69
50
|
}
|
|
70
51
|
}
|
|
71
52
|
else {
|
|
72
|
-
|
|
73
|
-
const hash = new Uint8Array(16);
|
|
74
|
-
const file = saveBlock(db, typeId, start, end, hash);
|
|
75
|
-
if (file === null) {
|
|
76
|
-
// The previous state should remain in the merkle tree for
|
|
77
|
-
// load and sync purposes.
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
const oldLeaf = db.merkleTree.search(mtKey);
|
|
82
|
-
// If (file.length === 0) then the range is empty but that's fine,
|
|
83
|
-
// we'll keep them around for now to maintain the order of the tree.
|
|
84
|
-
if (oldLeaf) {
|
|
85
|
-
oldLeaf.data.file = file;
|
|
86
|
-
db.merkleTree.update(mtKey, hash);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
const data = {
|
|
90
|
-
file,
|
|
91
|
-
typeId,
|
|
92
|
-
start,
|
|
93
|
-
end,
|
|
94
|
-
};
|
|
95
|
-
db.merkleTree.insert(mtKey, hash, data);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
53
|
+
foreachDirtyBlock(db, (_mtKey, typeId, start, end) => saveBlock(db, typeId, start, end));
|
|
99
54
|
}
|
|
100
55
|
db.dirtyRanges.clear();
|
|
101
56
|
const types = {};
|
|
@@ -105,22 +60,24 @@ export function save(db, sync = false, forceFullDump = false, skipMigrationCheck
|
|
|
105
60
|
types[id] = { lastId, blockCapacity };
|
|
106
61
|
rangeDumps[id] = [];
|
|
107
62
|
}
|
|
108
|
-
db.
|
|
109
|
-
const [typeId, start] =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const data =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
63
|
+
db.verifTree.foreachBlock((block) => {
|
|
64
|
+
const [typeId, start] = destructureTreeKey(block.key);
|
|
65
|
+
const def = db.schemaTypesParsedById[typeId];
|
|
66
|
+
const end = start + def.blockCapacity - 1;
|
|
67
|
+
const data = {
|
|
68
|
+
file: db.verifTree.getBlockFile(block),
|
|
69
|
+
hash: bufToHex(block.hash),
|
|
70
|
+
start,
|
|
71
|
+
end,
|
|
72
|
+
};
|
|
73
|
+
rangeDumps[typeId].push(data);
|
|
117
74
|
});
|
|
118
75
|
const data = {
|
|
119
76
|
ts,
|
|
120
77
|
types,
|
|
121
78
|
commonDump: COMMON_SDB_FILE,
|
|
122
79
|
rangeDumps,
|
|
123
|
-
hash: bufToHex(db.
|
|
80
|
+
hash: bufToHex(db.verifTree.hash), // TODO `hash('hex')`
|
|
124
81
|
};
|
|
125
82
|
const filePath = join(db.fileSystemPath, WRITELOG_FILE);
|
|
126
83
|
const content = JSON.stringify(data);
|
|
@@ -137,8 +94,7 @@ export function save(db, sync = false, forceFullDump = false, skipMigrationCheck
|
|
|
137
94
|
resolve(v);
|
|
138
95
|
})
|
|
139
96
|
.catch((err) => {
|
|
140
|
-
|
|
141
|
-
db.emit('info', `Save: writing writeLog failed ${err.message}`);
|
|
97
|
+
db.emit('error', `Save: writing writeLog failed ${err.message}`);
|
|
142
98
|
db.saveInProgress = false;
|
|
143
99
|
reject(err);
|
|
144
100
|
});
|
|
@@ -146,7 +102,7 @@ export function save(db, sync = false, forceFullDump = false, skipMigrationCheck
|
|
|
146
102
|
}
|
|
147
103
|
}
|
|
148
104
|
catch (err) {
|
|
149
|
-
db.emit('
|
|
105
|
+
db.emit('error', `Save failed ${err.message}`);
|
|
150
106
|
db.saveInProgress = false;
|
|
151
107
|
throw err;
|
|
152
108
|
}
|
|
@@ -2,11 +2,11 @@ import { schemaToSelvaBuffer, updateTypeDefs } from '@based/schema/def';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { writeFile } from 'node:fs/promises';
|
|
4
4
|
import native from '../native.js';
|
|
5
|
-
import {
|
|
5
|
+
import { makeTreeKey } from './tree.js';
|
|
6
6
|
import { deepCopy, writeUint64 } from '@saulx/utils';
|
|
7
7
|
import { SCHEMA_FILE } from '../types.js';
|
|
8
8
|
import { hash } from '@saulx/hash';
|
|
9
|
-
import { getPropType } from '@based/schema';
|
|
9
|
+
import { getPropType, serialize } from '@based/schema';
|
|
10
10
|
export const setSchemaOnServer = (server, schema) => {
|
|
11
11
|
const { schemaTypesParsed, schemaTypesParsedById } = updateTypeDefs(schema);
|
|
12
12
|
server.schema = schema;
|
|
@@ -16,7 +16,7 @@ export const setSchemaOnServer = (server, schema) => {
|
|
|
16
16
|
export const writeSchemaFile = async (server, schema) => {
|
|
17
17
|
const schemaFilePath = join(server.fileSystemPath, SCHEMA_FILE);
|
|
18
18
|
try {
|
|
19
|
-
await writeFile(schemaFilePath,
|
|
19
|
+
await writeFile(schemaFilePath, serialize(schema));
|
|
20
20
|
}
|
|
21
21
|
catch (err) {
|
|
22
22
|
throw new Error(`Error writing schema to a file path ${schemaFilePath}}`);
|
|
@@ -38,7 +38,7 @@ export const setNativeSchema = (server, schema) => {
|
|
|
38
38
|
if (schema.types._root) {
|
|
39
39
|
// TODO fix server add it in schema at least
|
|
40
40
|
const data = [2, 1, 0, 0, 0, 1, 9, 1, 0, 0, 0, 7, 1, 0, 1];
|
|
41
|
-
const blockKey =
|
|
41
|
+
const blockKey = makeTreeKey(1, 1);
|
|
42
42
|
const buf = new Uint8Array(8 + data.length + 2 + 8 + 4);
|
|
43
43
|
const view = new DataView(buf.buffer, 0, buf.byteLength);
|
|
44
44
|
// set schema hash
|
|
@@ -52,8 +52,9 @@ export const setNativeSchema = (server, schema) => {
|
|
|
52
52
|
// add dataLen
|
|
53
53
|
view.setUint32(buf.length - 4, data.length, true);
|
|
54
54
|
server.modify(buf);
|
|
55
|
-
|
|
55
|
+
//server.verifTree = new VerifTree(.schemaTypesParsed)
|
|
56
56
|
}
|
|
57
|
+
server.verifTree.updateTypes(server.schemaTypesParsed);
|
|
57
58
|
};
|
|
58
59
|
export const strictSchemaToDbSchema = (schema) => {
|
|
59
60
|
// @ts-ignore
|