@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.
Files changed (122) hide show
  1. package/README.md +2 -2
  2. package/dist/lib/darwin_aarch64/include/selva/colvec.h +71 -0
  3. package/dist/lib/darwin_aarch64/include/selva/db.h +3 -0
  4. package/dist/lib/darwin_aarch64/include/selva/fields.h +1 -1
  5. package/dist/lib/darwin_aarch64/include/selva/types.h +7 -0
  6. package/dist/lib/darwin_aarch64/libdeflate.dylib +0 -0
  7. package/dist/lib/darwin_aarch64/libjemalloc_selva.2.dylib +0 -0
  8. package/dist/lib/darwin_aarch64/libjemalloc_selva.so.2 +0 -0
  9. package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
  10. package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
  11. package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
  12. package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
  13. package/dist/lib/darwin_aarch64/libnode-v24.node +0 -0
  14. package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
  15. package/dist/lib/darwin_aarch64/libxxhash.dylib +0 -0
  16. package/dist/lib/linux_aarch64/include/selva/colvec.h +71 -0
  17. package/dist/lib/linux_aarch64/include/selva/db.h +3 -0
  18. package/dist/lib/linux_aarch64/include/selva/fields.h +1 -1
  19. package/dist/lib/linux_aarch64/include/selva/types.h +7 -0
  20. package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
  21. package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
  22. package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
  23. package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
  24. package/dist/lib/linux_aarch64/libnode-v24.node +0 -0
  25. package/dist/lib/linux_aarch64/libselva.so +0 -0
  26. package/dist/lib/linux_x86_64/include/selva/colvec.h +71 -0
  27. package/dist/lib/linux_x86_64/include/selva/db.h +3 -0
  28. package/dist/lib/linux_x86_64/include/selva/fields.h +1 -1
  29. package/dist/lib/linux_x86_64/include/selva/types.h +7 -0
  30. package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
  31. package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
  32. package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
  33. package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
  34. package/dist/lib/linux_x86_64/libnode-v24.node +0 -0
  35. package/dist/lib/linux_x86_64/libselva.so +0 -0
  36. package/dist/src/client/flushModify.d.ts +2 -1
  37. package/dist/src/client/flushModify.js +12 -4
  38. package/dist/src/client/modify/create.js +11 -0
  39. package/dist/src/client/modify/delete.js +3 -0
  40. package/dist/src/client/modify/modify.js +2 -2
  41. package/dist/src/client/modify/setCursor.d.ts +2 -1
  42. package/dist/src/client/query/BasedDbQuery.d.ts +7 -2
  43. package/dist/src/client/query/BasedDbQuery.js +111 -14
  44. package/dist/src/client/query/aggregates/aggregation.js +24 -11
  45. package/dist/src/client/query/aggregates/types.d.ts +21 -2
  46. package/dist/src/client/query/aggregates/types.js +34 -1
  47. package/dist/src/client/query/display.js +8 -2
  48. package/dist/src/client/query/filter/createVariableFilterBuffer.d.ts +2 -3
  49. package/dist/src/client/query/filter/createVariableFilterBuffer.js +20 -7
  50. package/dist/src/client/query/filter/filter.js +13 -3
  51. package/dist/src/client/query/filter/primitiveFilter.d.ts +1 -2
  52. package/dist/src/client/query/include/props.js +18 -2
  53. package/dist/src/client/query/include/toBuffer.js +11 -3
  54. package/dist/src/client/query/include/walk.js +5 -1
  55. package/dist/src/client/query/queryDef.js +4 -1
  56. package/dist/src/client/query/read/read.js +50 -23
  57. package/dist/src/client/query/registerQuery.js +1 -0
  58. package/dist/src/client/query/search/index.d.ts +1 -1
  59. package/dist/src/client/query/search/index.js +21 -7
  60. package/dist/src/client/query/sort.d.ts +1 -1
  61. package/dist/src/client/query/toByteCode/default.d.ts +1 -1
  62. package/dist/src/client/query/toByteCode/default.js +0 -2
  63. package/dist/src/client/query/toByteCode/toBuffer.js +0 -7
  64. package/dist/src/client/query/types.d.ts +16 -5
  65. package/dist/src/client/query/validation.js +25 -2
  66. package/dist/src/client/xxHash64.d.ts +1 -1
  67. package/dist/src/index.d.ts +0 -1
  68. package/dist/src/index.js +0 -1
  69. package/dist/src/native.d.ts +2 -2
  70. package/dist/src/native.js +7 -8
  71. package/dist/src/server/IoWorker.d.ts +8 -0
  72. package/dist/src/server/IoWorker.js +39 -0
  73. package/dist/src/server/QueryWorker.d.ts +8 -0
  74. package/dist/src/server/QueryWorker.js +26 -0
  75. package/dist/src/server/blocks.d.ts +24 -0
  76. package/dist/src/server/blocks.js +112 -0
  77. package/dist/src/server/dbHash.d.ts +1 -1
  78. package/dist/src/server/index.d.ts +9 -16
  79. package/dist/src/server/index.js +37 -15
  80. package/dist/src/server/migrate/index.d.ts +5 -0
  81. package/dist/src/server/migrate/index.js +10 -7
  82. package/dist/src/server/save.d.ts +8 -6
  83. package/dist/src/server/save.js +34 -78
  84. package/dist/src/server/schema.js +6 -5
  85. package/dist/src/server/start.js +57 -60
  86. package/dist/src/server/tree.d.ts +24 -13
  87. package/dist/src/server/tree.js +95 -66
  88. package/dist/src/server/workers/DbWorker.d.ts +17 -0
  89. package/dist/src/server/{DbWorker.js → workers/DbWorker.js} +15 -17
  90. package/dist/src/server/workers/io_worker.js +39 -0
  91. package/dist/src/server/workers/io_worker_types.d.ts +12 -0
  92. package/dist/src/server/workers/io_worker_types.js +2 -0
  93. package/dist/src/server/workers/query_worker.d.ts +1 -0
  94. package/dist/src/server/workers/query_worker.js +4 -0
  95. package/dist/src/server/workers/worker.d.ts +1 -0
  96. package/dist/src/server/workers/worker.js +41 -0
  97. package/dist/src/shared/Emitter.d.ts +1 -0
  98. package/dist/src/types.d.ts +1 -1
  99. package/dist/src/types.js +1 -1
  100. package/package.json +3 -3
  101. package/dist/lib/darwin_aarch64/include/selva/history.h +0 -64
  102. package/dist/lib/linux_aarch64/include/selva/history.h +0 -64
  103. package/dist/lib/linux_x86_64/include/selva/history.h +0 -64
  104. package/dist/src/client/query/serialize.d.ts +0 -4
  105. package/dist/src/client/query/serialize.js +0 -26
  106. package/dist/src/server/DbWorker.d.ts +0 -13
  107. package/dist/src/server/csmt/draw-dot.d.ts +0 -4
  108. package/dist/src/server/csmt/draw-dot.js +0 -38
  109. package/dist/src/server/csmt/index.d.ts +0 -4
  110. package/dist/src/server/csmt/index.js +0 -5
  111. package/dist/src/server/csmt/match.d.ts +0 -7
  112. package/dist/src/server/csmt/match.js +0 -10
  113. package/dist/src/server/csmt/memebership-proof.d.ts +0 -7
  114. package/dist/src/server/csmt/memebership-proof.js +0 -122
  115. package/dist/src/server/csmt/tree-utils.d.ts +0 -6
  116. package/dist/src/server/csmt/tree-utils.js +0 -33
  117. package/dist/src/server/csmt/tree.d.ts +0 -3
  118. package/dist/src/server/csmt/tree.js +0 -270
  119. package/dist/src/server/csmt/types.d.ts +0 -46
  120. package/dist/src/server/csmt/types.js +0 -2
  121. package/dist/src/server/worker.js +0 -33
  122. /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,5 +1,5 @@
1
1
  export default function createHash(): {
2
- update: (buf: Uint8Array) => any;
2
+ update: (buf: Uint8Array) => /*elided*/ any;
3
3
  digest: (encoding?: "hex") => Uint8Array | string;
4
4
  reset: () => void;
5
5
  };
@@ -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 { CsmtNodeRange } from './tree.js';
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 { DbWorker } from './DbWorker.js';
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
- merkleTree: ReturnType<typeof createTree<CsmtNodeRange>>;
24
+ verifTree: VerifTree;
25
25
  dirtyRanges: Set<number>;
26
- csmtHashFun: {
27
- update: (buf: Uint8Array) => any;
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
- createCsmtHashFun: () => {
51
- update: (buf: Uint8Array) => any;
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>;
@@ -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 { makeCsmtKeyFromNodeId } from './tree.js';
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
- merkleTree;
34
+ verifTree;
35
35
  dirtyRanges = new Set();
36
- csmtHashFun = createDbHash();
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
- createCsmtHashFun = () => {
64
- // We can just reuse it as long as we only have one tree.
65
- this.csmtHashFun.reset();
66
- return this.csmtHashFun;
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 = makeCsmtKeyFromNodeId(def.id, def.blockCapacity, def.lastId);
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 Promise.all(this.workers.map(({ worker }) => worker.terminate()));
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 { destructureCsmtKey, foreachDirtyBlock, specialBlock } from '../tree.js';
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.merkleTree.visitLeafNodes((leaf) => {
94
- const [_typeId, start] = destructureCsmtKey(leaf.key);
95
- if (start == specialBlock)
96
- return; // skip the type specialBlock
97
- rangesToMigrate.push(leaf.data);
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
- void foreachDirtyBlock(server, (_mtKey, typeId, start, end) => {
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 {};
@@ -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 { destructureCsmtKey, foreachBlock, foreachDirtyBlock, initCsmt, makeCsmtKey, specialBlock, } from './tree.js';
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
- const block_sdb_file = (typeId, start, end) => `${typeId}_${start}_${end}.sdb`;
10
- function saveBlock(db, typeId, start, end, hashOut) {
11
- const file = block_sdb_file(typeId, start, end);
12
- const path = join(db.fileSystemPath, file);
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
- else if (err) {
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
- console.error(`Save common failed: ${err}`);
39
+ db.emit('error', `Save common failed: ${err}`);
44
40
  // Return ?
45
41
  }
46
42
  if (forceFullDump) {
47
- // We just rebuild the whole tree
48
- initCsmt(db);
49
- for (const key in db.schemaTypesParsed) {
50
- const def = db.schemaTypesParsed[key];
51
- foreachBlock(db, def, (start, end, _hash) => {
52
- const typeId = def.id;
53
- const mtKey = makeCsmtKey(typeId, start);
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
- void foreachDirtyBlock(db, (mtKey, typeId, start, end) => {
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.merkleTree.visitLeafNodes((leaf) => {
109
- const [typeId, start] = destructureCsmtKey(leaf.key);
110
- if (start == specialBlock)
111
- return; // skip the type specialBlock
112
- const data = leaf.data;
113
- if (start != data.start) {
114
- console.error(`csmtKey start and range start mismatch: ${start} != ${data.start}`);
115
- }
116
- rangeDumps[typeId].push({ ...data, hash: bufToHex(leaf.hash) });
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.merkleTree.getRoot()?.hash ?? new Uint8Array(0)),
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
- console.error('Save: writing writeLog failed');
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('info', `Save failed ${err.message}`);
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 { initCsmt, makeCsmtKey } from './tree.js';
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, JSON.stringify(schema));
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 = makeCsmtKey(1, 1);
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
- initCsmt(server);
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