@based/db 0.0.64 → 0.0.67

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 (144) 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 +33 -4
  4. package/dist/lib/darwin_aarch64/include/selva/fields.h +37 -25
  5. package/dist/lib/darwin_aarch64/include/selva/hll.h +5 -3
  6. package/dist/lib/darwin_aarch64/include/selva/membar.h +23 -0
  7. package/dist/lib/darwin_aarch64/include/selva/types.h +8 -1
  8. package/dist/lib/darwin_aarch64/include/selva/worker_ctx.h +19 -3
  9. package/dist/lib/darwin_aarch64/libdeflate.dylib +0 -0
  10. package/dist/lib/darwin_aarch64/libjemalloc_selva.2.dylib +0 -0
  11. package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
  12. package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
  13. package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
  14. package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
  15. package/dist/lib/darwin_aarch64/libnode-v24.node +0 -0
  16. package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
  17. package/dist/lib/darwin_aarch64/libxxhash.dylib +0 -0
  18. package/dist/lib/linux_aarch64/include/selva/colvec.h +71 -0
  19. package/dist/lib/linux_aarch64/include/selva/db.h +33 -4
  20. package/dist/lib/linux_aarch64/include/selva/fields.h +37 -25
  21. package/dist/lib/linux_aarch64/include/selva/hll.h +5 -3
  22. package/dist/lib/linux_aarch64/include/selva/membar.h +23 -0
  23. package/dist/lib/linux_aarch64/include/selva/types.h +8 -1
  24. package/dist/lib/linux_aarch64/include/selva/worker_ctx.h +19 -3
  25. package/dist/lib/linux_aarch64/libjemalloc_selva.so.2 +0 -0
  26. package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
  27. package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
  28. package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
  29. package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
  30. package/dist/lib/linux_aarch64/libnode-v24.node +0 -0
  31. package/dist/lib/linux_aarch64/libselva.so +0 -0
  32. package/dist/lib/linux_x86_64/include/selva/colvec.h +71 -0
  33. package/dist/lib/linux_x86_64/include/selva/db.h +33 -4
  34. package/dist/lib/linux_x86_64/include/selva/fields.h +37 -25
  35. package/dist/lib/linux_x86_64/include/selva/hll.h +5 -3
  36. package/dist/lib/linux_x86_64/include/selva/membar.h +23 -0
  37. package/dist/lib/linux_x86_64/include/selva/types.h +8 -1
  38. package/dist/lib/linux_x86_64/include/selva/worker_ctx.h +19 -3
  39. package/dist/lib/linux_x86_64/libjemalloc_selva.so.2 +0 -0
  40. package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
  41. package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
  42. package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
  43. package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
  44. package/dist/lib/linux_x86_64/libnode-v24.node +0 -0
  45. package/dist/lib/linux_x86_64/libselva.so +0 -0
  46. package/dist/src/client/flushModify.d.ts +2 -1
  47. package/dist/src/client/flushModify.js +12 -4
  48. package/dist/src/client/modify/create.js +11 -0
  49. package/dist/src/client/modify/delete.js +3 -0
  50. package/dist/src/client/modify/fixed.js +1 -1
  51. package/dist/src/client/modify/modify.js +2 -2
  52. package/dist/src/client/modify/setCursor.d.ts +2 -1
  53. package/dist/src/client/query/BasedDbQuery.d.ts +10 -4
  54. package/dist/src/client/query/BasedDbQuery.js +114 -6
  55. package/dist/src/client/query/aggregates/aggregation.js +24 -11
  56. package/dist/src/client/query/aggregates/types.d.ts +22 -2
  57. package/dist/src/client/query/aggregates/types.js +34 -1
  58. package/dist/src/client/query/display.js +8 -2
  59. package/dist/src/client/query/filter/createVariableFilterBuffer.d.ts +2 -3
  60. package/dist/src/client/query/filter/createVariableFilterBuffer.js +20 -7
  61. package/dist/src/client/query/filter/filter.js +13 -3
  62. package/dist/src/client/query/filter/primitiveFilter.d.ts +1 -2
  63. package/dist/src/client/query/include/props.js +18 -2
  64. package/dist/src/client/query/include/toBuffer.js +11 -3
  65. package/dist/src/client/query/include/walk.js +5 -1
  66. package/dist/src/client/query/queryDef.js +4 -1
  67. package/dist/src/client/query/read/read.js +52 -22
  68. package/dist/src/client/query/registerQuery.js +1 -0
  69. package/dist/src/client/query/search/index.d.ts +1 -1
  70. package/dist/src/client/query/search/index.js +21 -7
  71. package/dist/src/client/query/sort.d.ts +1 -1
  72. package/dist/src/client/query/toByteCode/default.d.ts +1 -1
  73. package/dist/src/client/query/toByteCode/default.js +0 -2
  74. package/dist/src/client/query/toByteCode/toBuffer.js +0 -7
  75. package/dist/src/client/query/types.d.ts +16 -5
  76. package/dist/src/client/query/validation.d.ts +3 -0
  77. package/dist/src/client/query/validation.js +34 -2
  78. package/dist/src/client/xxHash64.d.ts +1 -1
  79. package/dist/src/index.d.ts +1 -2
  80. package/dist/src/index.js +0 -1
  81. package/dist/src/native.d.ts +7 -4
  82. package/dist/src/native.js +23 -13
  83. package/dist/src/server/IoWorker.d.ts +8 -0
  84. package/dist/src/server/IoWorker.js +39 -0
  85. package/dist/src/server/QueryWorker.d.ts +8 -0
  86. package/dist/src/server/QueryWorker.js +26 -0
  87. package/dist/src/server/blocks.d.ts +24 -0
  88. package/dist/src/server/blocks.js +112 -0
  89. package/dist/src/server/dbHash.d.ts +1 -1
  90. package/dist/src/server/index.d.ts +10 -16
  91. package/dist/src/server/index.js +39 -15
  92. package/dist/src/server/migrate/index.d.ts +5 -0
  93. package/dist/src/server/migrate/index.js +11 -7
  94. package/dist/src/server/migrate/worker.js +3 -0
  95. package/dist/src/server/save.d.ts +8 -6
  96. package/dist/src/server/save.js +34 -78
  97. package/dist/src/server/schema.js +6 -5
  98. package/dist/src/server/start.js +57 -60
  99. package/dist/src/server/tree.d.ts +24 -13
  100. package/dist/src/server/tree.js +95 -66
  101. package/dist/src/server/workers/DbWorker.d.ts +17 -0
  102. package/dist/src/server/{DbWorker.js → workers/DbWorker.js} +15 -17
  103. package/dist/src/server/workers/io_worker.js +39 -0
  104. package/dist/src/server/workers/io_worker_types.d.ts +12 -0
  105. package/dist/src/server/workers/io_worker_types.js +2 -0
  106. package/dist/src/server/workers/query_worker.d.ts +1 -0
  107. package/dist/src/server/workers/query_worker.js +4 -0
  108. package/dist/src/server/workers/worker.d.ts +1 -0
  109. package/dist/src/server/workers/worker.js +41 -0
  110. package/dist/src/shared/Emitter.d.ts +1 -0
  111. package/dist/src/types.d.ts +1 -1
  112. package/dist/src/types.js +1 -1
  113. package/package.json +3 -3
  114. package/dist/lib/darwin_aarch64/include/selva/find.h +0 -47
  115. package/dist/lib/darwin_aarch64/include/selva/history.h +0 -64
  116. package/dist/lib/darwin_aarch64/include/selva/queue_r.h +0 -190
  117. package/dist/lib/darwin_aarch64/include/selva/traverse.h +0 -65
  118. package/dist/lib/linux_aarch64/include/selva/find.h +0 -47
  119. package/dist/lib/linux_aarch64/include/selva/history.h +0 -64
  120. package/dist/lib/linux_aarch64/include/selva/queue_r.h +0 -190
  121. package/dist/lib/linux_aarch64/include/selva/traverse.h +0 -65
  122. package/dist/lib/linux_x86_64/include/selva/find.h +0 -47
  123. package/dist/lib/linux_x86_64/include/selva/history.h +0 -64
  124. package/dist/lib/linux_x86_64/include/selva/queue_r.h +0 -190
  125. package/dist/lib/linux_x86_64/include/selva/traverse.h +0 -65
  126. package/dist/src/client/query/serialize.d.ts +0 -4
  127. package/dist/src/client/query/serialize.js +0 -26
  128. package/dist/src/server/DbWorker.d.ts +0 -13
  129. package/dist/src/server/csmt/draw-dot.d.ts +0 -4
  130. package/dist/src/server/csmt/draw-dot.js +0 -38
  131. package/dist/src/server/csmt/index.d.ts +0 -4
  132. package/dist/src/server/csmt/index.js +0 -5
  133. package/dist/src/server/csmt/match.d.ts +0 -7
  134. package/dist/src/server/csmt/match.js +0 -10
  135. package/dist/src/server/csmt/memebership-proof.d.ts +0 -7
  136. package/dist/src/server/csmt/memebership-proof.js +0 -122
  137. package/dist/src/server/csmt/tree-utils.d.ts +0 -6
  138. package/dist/src/server/csmt/tree-utils.js +0 -33
  139. package/dist/src/server/csmt/tree.d.ts +0 -3
  140. package/dist/src/server/csmt/tree.js +0 -270
  141. package/dist/src/server/csmt/types.d.ts +0 -46
  142. package/dist/src/server/csmt/types.js +0 -2
  143. package/dist/src/server/worker.js +0 -33
  144. /package/dist/src/server/{worker.d.ts → workers/io_worker.d.ts} +0 -0
@@ -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 saveRange(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.saveRange(path, typeId, start, end, 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 = saveRange(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 = saveRange(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
@@ -1,15 +1,27 @@
1
- import { DbWorker } from './DbWorker.js';
1
+ import { availableParallelism } from 'node:os';
2
+ import { QueryWorker } from './QueryWorker.js';
3
+ import { IoWorker } from './IoWorker.js';
2
4
  import native from '../native.js';
3
5
  import { rm, mkdir, readFile } from 'node:fs/promises';
4
6
  import { join } from 'node:path';
5
- import { destructureCsmtKey, foreachBlock, initCsmt, makeCsmtKey, specialBlock, } from './tree.js';
6
- import { availableParallelism } from 'node:os';
7
+ import { VerifTree, makeTreeKey } from './tree.js';
8
+ import { foreachBlock } from './blocks.js';
7
9
  import exitHook from 'exit-hook';
8
10
  import { save } from './save.js';
9
- import { DEFAULT_BLOCK_CAPACITY } from '@based/schema/def';
10
- import { bufToHex, wait } from '@saulx/utils';
11
+ import { deSerialize } from '@based/schema';
12
+ import { BLOCK_CAPACITY_DEFAULT } from '@based/schema/def';
13
+ import { bufToHex, equals, hexToBuf, wait } from '@saulx/utils';
11
14
  import { SCHEMA_FILE, WRITELOG_FILE } from '../types.js';
12
15
  import { setSchemaOnServer } from './schema.js';
16
+ function startWorkers(db, opts) {
17
+ const queryThreads = opts?.queryThreads ?? availableParallelism();
18
+ const address = native.intFromExternal(db.dbCtxExternal);
19
+ db.workers = [];
20
+ for (let i = 0; i < queryThreads; i++) {
21
+ db.workers.push(new QueryWorker(address, db, i));
22
+ }
23
+ db.ioWorker = new IoWorker(address, db);
24
+ }
13
25
  export async function start(db, opts) {
14
26
  const path = db.fileSystemPath;
15
27
  const noop = () => { };
@@ -19,6 +31,7 @@ export async function start(db, opts) {
19
31
  await mkdir(path, { recursive: true }).catch(noop);
20
32
  db.dbCtxExternal = native.start();
21
33
  let writelog = null;
34
+ let partials = []; // Blocks that exists but were not loaded [key, hash]
22
35
  try {
23
36
  writelog = JSON.parse((await readFile(join(path, WRITELOG_FILE))).toString());
24
37
  // Load the common dump
@@ -29,81 +42,66 @@ export async function start(db, opts) {
29
42
  console.error(e.message);
30
43
  throw e;
31
44
  }
45
+ const schema = await readFile(join(path, SCHEMA_FILE));
46
+ if (schema) {
47
+ const s = deSerialize(schema);
48
+ setSchemaOnServer(db, s);
49
+ }
32
50
  // Load all range dumps
33
51
  for (const typeId in writelog.rangeDumps) {
34
52
  const dumps = writelog.rangeDumps[typeId];
53
+ const def = db.schemaTypesParsedById[typeId];
35
54
  for (const dump of dumps) {
36
55
  const fname = dump.file;
37
56
  if (fname?.length > 0) {
38
- try {
39
- native.loadRange(join(path, fname), db.dbCtxExternal);
57
+ if (!def.partial) {
58
+ try {
59
+ // Can't use loadBlock() yet because verifTree is not avail
60
+ native.loadBlock(join(path, fname), db.dbCtxExternal);
61
+ }
62
+ catch (e) {
63
+ console.error(e.message);
64
+ }
40
65
  }
41
- catch (e) {
42
- console.error(e.message);
66
+ else {
67
+ partials.push([
68
+ makeTreeKey(def.id, dump.start),
69
+ hexToBuf(dump.hash),
70
+ ]);
43
71
  }
44
72
  }
45
73
  }
46
74
  }
47
- const schema = await readFile(join(path, SCHEMA_FILE));
48
- if (schema) {
49
- const s = JSON.parse(schema.toString());
50
- setSchemaOnServer(db, s);
51
- }
52
75
  }
53
76
  catch (err) {
54
77
  // TODO In some cases we really should give up!
55
78
  }
56
- const csmtTypes = initCsmt(db);
57
- for (const key in csmtTypes) {
58
- const def = csmtTypes[key];
79
+ db.verifTree = new VerifTree(db.schemaTypesParsed);
80
+ for (const { typeId } of db.verifTree.types()) {
81
+ const def = db.schemaTypesParsedById[typeId];
59
82
  const [total, lastId] = native.getTypeInfo(def.id, db.dbCtxExternal);
60
83
  def.lastId = writelog?.types[def.id]?.lastId || lastId;
61
84
  def.blockCapacity =
62
- writelog?.types[def.id]?.blockCapacity || DEFAULT_BLOCK_CAPACITY;
63
- foreachBlock(db, def, (start, end, hash) => {
64
- const mtKey = makeCsmtKey(def.id, start);
65
- const file = writelog.rangeDumps[def.id]?.find((v) => v.start === start)?.file ||
66
- '';
67
- const data = {
68
- file,
69
- typeId: def.id,
70
- start,
71
- end,
72
- };
73
- db.merkleTree.insert(mtKey, hash, data);
74
- }, true);
85
+ writelog?.types[def.id]?.blockCapacity ||
86
+ def.blockCapacity ||
87
+ BLOCK_CAPACITY_DEFAULT;
88
+ foreachBlock(db, def, (start, _end, hash) => {
89
+ const mtKey = makeTreeKey(def.id, start);
90
+ db.verifTree.update(mtKey, hash);
91
+ });
92
+ }
93
+ // Insert partials to make the hash match
94
+ for (const [key, hash] of partials) {
95
+ db.verifTree.update(key, hash, false);
75
96
  }
76
97
  if (writelog?.hash) {
77
- // FIXME FDN-1301
78
- //const oldHash = hexToBuf(writelog.hash)
79
- //const newHash = db.merkleTree.getRoot()?.hash
80
- //if (!hashEq(oldHash, newHash)) {
81
- // console.error(
82
- // `WARN: CSMT hash mismatch. expected: ${writelog.hash} actual: ${bufToHex(newHash)}`,
83
- // )
84
- //}
85
- const oldHashSet = new Set();
86
- const newHashSet = new Set();
87
- for (let k in writelog.rangeDumps)
88
- writelog.rangeDumps[k].forEach(({ hash }) => oldHashSet.add(hash));
89
- db.merkleTree.visitLeafNodes(({ key, hash }) => {
90
- const [_typeId, start] = destructureCsmtKey(key);
91
- if (start == specialBlock)
92
- return; // skip the type specialBlock
93
- newHashSet.add(bufToHex(hash));
94
- });
95
- const setEq = (a, b) => a.size === b.size && [...a].every((value) => b.has(value));
96
- if (!setEq(oldHashSet, newHashSet)) {
97
- console.error(`WARN: CSMT hash mismatch.`);
98
+ const oldHash = hexToBuf(writelog.hash);
99
+ const newHash = db.verifTree.hash;
100
+ if (!equals(oldHash, newHash)) {
101
+ console.error(`WARN: DB hash mismatch. expected: ${writelog.hash} actual: ${bufToHex(newHash)}`);
98
102
  }
99
103
  }
100
- // start workers
101
- const queryThreads = opts?.queryThreads ?? availableParallelism();
102
- const address = native.intFromExternal(db.dbCtxExternal);
103
- db.workers = [];
104
- for (let i = 0; i < queryThreads; i++) {
105
- db.workers.push(new DbWorker(address, db, i));
106
- }
104
+ startWorkers(db, opts);
107
105
  if (!opts?.hosted) {
108
106
  db.unlistenExit = exitHook((signal) => {
109
107
  const blockSig = () => { };
@@ -118,9 +116,8 @@ export async function start(db, opts) {
118
116
  signals.forEach((sig) => process.off(sig, blockSig));
119
117
  });
120
118
  }
121
- const d = performance.now();
122
119
  await Promise.all(db.workers.map(({ readyPromise }) => readyPromise));
123
- db.emit('info', `Starting workers took ${d}ms`);
120
+ db.emit('info', 'All workers ready');
124
121
  // use timeout
125
122
  if (db.saveIntervalInSeconds > 0) {
126
123
  db.saveInterval ??= setInterval(() => {
@@ -1,15 +1,26 @@
1
- import { DbServer } from './index.js';
2
1
  import { SchemaTypeDef } from '@based/schema/def';
3
- export type CsmtNodeRange = {
4
- file: string;
5
- typeId: number;
6
- start: number;
7
- end: number;
2
+ export declare const destructureTreeKey: (key: number) => number[];
3
+ export declare const makeTreeKey: (typeId: number, start: number) => number;
4
+ export declare const nodeId2Start: (blockCapacity: number, nodeId: number) => number;
5
+ export declare const makeTreeKeyFromNodeId: (typeId: number, blockCapacity: number, nodeId: number) => number;
6
+ type Hash = Uint8Array;
7
+ export type VerifBlock = {
8
+ key: number;
9
+ hash: Hash;
10
+ inmem: boolean;
11
+ loadPromise: null | Promise<void>;
8
12
  };
9
- export declare const specialBlock = 2147483647;
10
- export declare const destructureCsmtKey: (key: number) => number[];
11
- export declare const makeCsmtKey: (typeId: number, start: number) => number;
12
- export declare const makeCsmtKeyFromNodeId: (typeId: number, blockCapacity: number, nodeId: number) => number;
13
- export declare function initCsmt(db: DbServer): {};
14
- export declare function foreachBlock(db: DbServer, def: SchemaTypeDef, cb: (start: number, end: number, hash: Uint8Array) => void, includeEmptyBlocks?: boolean): void;
15
- export declare function foreachDirtyBlock(db: DbServer, cb: (mtKey: number, typeId: number, start: number, end: number) => void): Promise<void>;
13
+ export declare class VerifTree {
14
+ #private;
15
+ constructor(schemaTypesParsed: Record<string, SchemaTypeDef>);
16
+ types(): Generator<any, void, unknown>;
17
+ foreachBlock(cb: (block: VerifBlock) => void): void;
18
+ get hash(): Uint8Array;
19
+ update(key: number, hash: Hash, inmem?: boolean): void;
20
+ remove(key: number): void;
21
+ static blockSdbFile(typeId: number, start: number, end: number): string;
22
+ getBlock(key: number): VerifBlock;
23
+ getBlockFile(block: VerifBlock): string;
24
+ updateTypes(schemaTypesParsed: Record<string, SchemaTypeDef>): void;
25
+ }
26
+ export {};
@@ -1,80 +1,109 @@
1
- import native from '../native.js';
2
- import { createTree } from './csmt/tree.js';
3
- // This is a special start id set for every type to somewhat lock the order of the csmt.
4
- // While the id is valid, it's never a true start id of a block.
5
- export const specialBlock = 2147483647;
6
- export const destructureCsmtKey = (key) => [
1
+ import createDbHash from './dbHash.js';
2
+ export const destructureTreeKey = (key) => [
7
3
  (key / 4294967296) | 0, // typeId
8
4
  (key >>> 31) * 2147483648 + (key & 0x7fffffff), // start_node_id
9
5
  ];
10
- export const makeCsmtKey = (typeId, start) => typeId * 4294967296 + start;
11
- export const makeCsmtKeyFromNodeId = (typeId, blockCapacity, nodeId) => {
6
+ export const makeTreeKey = (typeId, start) => typeId * 4294967296 + start;
7
+ export const nodeId2Start = (blockCapacity, nodeId) => ((nodeId - +!(nodeId % blockCapacity)) / blockCapacity) | 0;
8
+ const nodeId2BlockI = (nodeId, blockCapacity) => ((nodeId - 1) - ((nodeId - 1) % blockCapacity)) / blockCapacity;
9
+ export const makeTreeKeyFromNodeId = (typeId, blockCapacity, nodeId) => {
12
10
  const tmp = nodeId - +!(nodeId % blockCapacity);
13
11
  return typeId * 4294967296 + ((tmp / blockCapacity) | 0) * blockCapacity + 1;
14
12
  };
15
- export function initCsmt(db) {
16
- const types = Object.keys(db.schemaTypesParsed)
17
- .sort((a, b) => db.schemaTypesParsed[a].id - db.schemaTypesParsed[b].id)
18
- .reduce((obj, key) => {
19
- obj[key] = db.schemaTypesParsed[key];
20
- return obj;
21
- }, {});
22
- db.merkleTree = createTree(db.createCsmtHashFun);
23
- // Insert specialBlocks for types.
24
- // This should ensure that the insertion order of the actual node ranges is
25
- // always deterministic.
26
- for (const key in types) {
27
- const { id: typeId } = types[key];
28
- const data = {
29
- file: '',
30
- typeId: typeId,
31
- start: 0,
32
- end: 0,
33
- };
34
- try {
35
- db.merkleTree.insert(makeCsmtKey(typeId, specialBlock), db.merkleTree.emptyHash, data);
13
+ const HASH_SIZE = 16;
14
+ export class VerifTree {
15
+ #types;
16
+ #h = createDbHash();
17
+ constructor(schemaTypesParsed) {
18
+ this.#types = VerifTree.#makeTypes(schemaTypesParsed);
19
+ }
20
+ static #makeTypes(schemaTypesParsed) {
21
+ return Object.preventExtensions(Object.keys(schemaTypesParsed)
22
+ .sort((a, b) => schemaTypesParsed[a].id - schemaTypesParsed[b].id)
23
+ .reduce((obj, key) => {
24
+ const def = schemaTypesParsed[key];
25
+ const typeId = def.id;
26
+ obj[typeId] = {
27
+ typeId,
28
+ blockCapacity: def.blockCapacity,
29
+ hash: new Uint8Array(HASH_SIZE),
30
+ blocks: [],
31
+ };
32
+ return obj;
33
+ }, {}));
34
+ }
35
+ *types() {
36
+ for (const k of Object.keys(this.#types)) {
37
+ yield this.#types[k];
36
38
  }
37
- catch (_) { }
38
39
  }
39
- return types;
40
- }
41
- export function foreachBlock(db, def, cb, includeEmptyBlocks = false) {
42
- const step = def.blockCapacity;
43
- for (let start = 1; start <= def.lastId; start += step) {
44
- const end = start + step - 1;
45
- const hash = new Uint8Array(16);
46
- const res = native.getNodeRangeHash(def.id, start, end, hash, db.dbCtxExternal);
47
- if (res || includeEmptyBlocks) {
48
- cb(start, end, hash);
40
+ foreachBlock(cb) {
41
+ for (const k of Object.keys(this.#types)) {
42
+ const { blocks } = this.#types[k];
43
+ for (let block of blocks) {
44
+ if (block)
45
+ cb(block);
46
+ }
49
47
  }
50
48
  }
51
- }
52
- export async function foreachDirtyBlock(db, cb) {
53
- const typeIdMap = {};
54
- for (const typeName in db.schemaTypesParsed) {
55
- const type = db.schemaTypesParsed[typeName];
56
- const typeId = type.id;
57
- typeIdMap[typeId] = type;
49
+ get hash() {
50
+ this.#h.reset();
51
+ this.foreachBlock((block) => this.#h.update(block.hash));
52
+ return this.#h.digest();
53
+ }
54
+ update(key, hash, inmem = true) {
55
+ const [typeId, start] = destructureTreeKey(key);
56
+ const type = this.#types[typeId];
57
+ if (!type) {
58
+ throw new Error(`type ${typeId} not found`);
59
+ }
60
+ const blockI = nodeId2BlockI(start, type.blockCapacity);
61
+ const block = type.blocks[blockI] ?? (type.blocks[blockI] = Object.preventExtensions({ key, hash, inmem, loadPromise: null }));
62
+ block.hash = hash;
63
+ block.inmem = inmem;
64
+ }
65
+ remove(key) {
66
+ const [typeId, start] = destructureTreeKey(key);
67
+ const type = this.#types[typeId];
68
+ if (!type) {
69
+ throw new Error(`type ${typeId} not found`);
70
+ }
71
+ const blockI = nodeId2BlockI(start, type.blockCapacity);
72
+ delete type.blocks[blockI];
73
+ }
74
+ static blockSdbFile(typeId, start, end) {
75
+ return `${typeId}_${start}_${end}.sdb`;
76
+ }
77
+ getBlock(key) {
78
+ const [typeId, start] = destructureTreeKey(key);
79
+ const type = this.#types[typeId];
80
+ if (!type) {
81
+ throw new Error(`type ${typeId} not found`);
82
+ }
83
+ const blockI = nodeId2BlockI(start, type.blockCapacity);
84
+ return type.blocks[blockI];
58
85
  }
59
- // FDN-791 CSMT is unstable (not history independent)
60
- // For now we just sort the dirty data by type and start
61
- // to make the insertion order more deterministic.
62
- // This doesn't solve the issue completely because
63
- // we might mess the order later with updates.
64
- const dirty = [...db.dirtyRanges].sort((a, b) => {
65
- const [aTypeId, aStart] = destructureCsmtKey(a);
66
- const [bTypeId, bStart] = destructureCsmtKey(b);
67
- const aId = typeIdMap[aTypeId].id;
68
- const bId = typeIdMap[bTypeId].id;
69
- const td = aId - bId;
70
- if (td != 0)
71
- return td;
72
- return aStart - bStart;
73
- });
74
- for (const mtKey of dirty) {
75
- const [typeId, start] = destructureCsmtKey(mtKey);
76
- const end = start + typeIdMap[typeId].blockCapacity - 1;
77
- cb(mtKey, typeId, start, end);
86
+ getBlockFile(block) {
87
+ const [typeId, start] = destructureTreeKey(block.key);
88
+ const type = this.#types[typeId];
89
+ if (!type) {
90
+ throw new Error(`type ${typeId} not found`);
91
+ }
92
+ const end = start + type.blockCapacity - 1;
93
+ return VerifTree.blockSdbFile(typeId, start, end);
94
+ }
95
+ updateTypes(schemaTypesParsed) {
96
+ const oldTypes = this.#types;
97
+ const newTypes = VerifTree.#makeTypes(schemaTypesParsed);
98
+ for (const k of Object.keys(oldTypes)) {
99
+ const oldType = oldTypes[k];
100
+ const newType = newTypes[k];
101
+ if (newType) {
102
+ newType.hash = oldType.hash;
103
+ newType.blocks = oldType.blocks;
104
+ }
105
+ }
106
+ this.#types = newTypes;
78
107
  }
79
108
  }
80
109
  //# sourceMappingURL=tree.js.map
@@ -0,0 +1,17 @@
1
+ import { DbServer } from '../index.js';
2
+ export declare abstract class DbWorker {
3
+ constructor(address: BigInt, db: DbServer, onExit: (code: number) => void, workerName: string);
4
+ protected db: DbServer;
5
+ private channel;
6
+ private worker;
7
+ protected resolvers: ((x: any) => any)[];
8
+ readyPromise: Promise<true>;
9
+ terminate(): Promise<number>;
10
+ abstract handleMsg(buf: any): void;
11
+ protected callback: (resolve: (x: any) => any) => void;
12
+ /**
13
+ * Send msg to the worker thread and return a promise to the response.
14
+ */
15
+ protected call(msg: any): Promise<Uint8Array>;
16
+ updateCtx(address: BigInt): Promise<void>;
17
+ }