@based/db 0.0.24 → 0.0.25

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 (31) hide show
  1. package/dist/lib/darwin_aarch64/include/selva/selva_string.h +2 -2
  2. package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
  3. package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
  4. package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
  5. package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
  6. package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
  7. package/dist/lib/linux_aarch64/include/selva/selva_string.h +2 -2
  8. package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
  9. package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
  10. package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
  11. package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
  12. package/dist/lib/linux_aarch64/libselva.so +0 -0
  13. package/dist/lib/linux_x86_64/include/selva/selva_string.h +2 -2
  14. package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
  15. package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
  16. package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
  17. package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
  18. package/dist/lib/linux_x86_64/libselva.so +0 -0
  19. package/dist/src/client/modify/fixed.js +1 -1
  20. package/dist/src/client/modify/modify.js +6 -3
  21. package/dist/src/client/query/BasedIterable.d.ts +1 -1
  22. package/dist/src/client/query/BasedIterable.js +9 -3
  23. package/dist/src/client/query/read/read.js +1 -1
  24. package/dist/src/server/index.d.ts +5 -1
  25. package/dist/src/server/index.js +4 -3
  26. package/dist/src/server/save.d.ts +20 -1
  27. package/dist/src/server/save.js +58 -30
  28. package/dist/src/server/start.js +4 -7
  29. package/dist/src/server/tree.d.ts +1 -1
  30. package/dist/src/server/tree.js +1 -1
  31. package/package.json +1 -1
@@ -123,7 +123,7 @@ int selva_string_init_crc(struct selva_string *s, const char *str, size_t len, u
123
123
  [[nodiscard]]
124
124
  SELVA_EXPORT
125
125
  struct selva_string *selva_string_create(const char *str, size_t len, enum selva_string_flags flags)
126
- __attribute__((access(read_only, 1, 2)));
126
+ __attribute__((access(read_only, 1)));
127
127
 
128
128
  /**
129
129
  * Create a new string with a user provided CRC.
@@ -205,7 +205,7 @@ int selva_string_truncate(struct selva_string *s, size_t newlen)
205
205
  */
206
206
  SELVA_EXPORT
207
207
  int selva_string_append(struct selva_string *s, const char *str, size_t len)
208
- __attribute__((access(read_only, 2, 3)));
208
+ __attribute__((access(read_only, 2)));
209
209
 
210
210
  /**
211
211
  * Replace current value of the string s with str.
@@ -123,7 +123,7 @@ int selva_string_init_crc(struct selva_string *s, const char *str, size_t len, u
123
123
  [[nodiscard]]
124
124
  SELVA_EXPORT
125
125
  struct selva_string *selva_string_create(const char *str, size_t len, enum selva_string_flags flags)
126
- __attribute__((access(read_only, 1, 2)));
126
+ __attribute__((access(read_only, 1)));
127
127
 
128
128
  /**
129
129
  * Create a new string with a user provided CRC.
@@ -205,7 +205,7 @@ int selva_string_truncate(struct selva_string *s, size_t newlen)
205
205
  */
206
206
  SELVA_EXPORT
207
207
  int selva_string_append(struct selva_string *s, const char *str, size_t len)
208
- __attribute__((access(read_only, 2, 3)));
208
+ __attribute__((access(read_only, 2)));
209
209
 
210
210
  /**
211
211
  * Replace current value of the string s with str.
Binary file
@@ -123,7 +123,7 @@ int selva_string_init_crc(struct selva_string *s, const char *str, size_t len, u
123
123
  [[nodiscard]]
124
124
  SELVA_EXPORT
125
125
  struct selva_string *selva_string_create(const char *str, size_t len, enum selva_string_flags flags)
126
- __attribute__((access(read_only, 1, 2)));
126
+ __attribute__((access(read_only, 1)));
127
127
 
128
128
  /**
129
129
  * Create a new string with a user provided CRC.
@@ -205,7 +205,7 @@ int selva_string_truncate(struct selva_string *s, size_t newlen)
205
205
  */
206
206
  SELVA_EXPORT
207
207
  int selva_string_append(struct selva_string *s, const char *str, size_t len)
208
- __attribute__((access(read_only, 2, 3)));
208
+ __attribute__((access(read_only, 2)));
209
209
 
210
210
  /**
211
211
  * Replace current value of the string s with str.
Binary file
@@ -56,7 +56,7 @@ map[ENUM] = (ctx, val, def) => {
56
56
  return RANGE_ERR;
57
57
  }
58
58
  if (val === null) {
59
- ctx.buf[ctx.len++] = 1;
59
+ ctx.buf[ctx.len++] = 0;
60
60
  }
61
61
  else if (val in def.reverseEnum) {
62
62
  ctx.buf[ctx.len++] = def.reverseEnum[val] + 1;
@@ -24,10 +24,13 @@ function _modify(ctx, res, obj, schema, mod, tree, overwrite, unsafe) {
24
24
  }
25
25
  let err;
26
26
  if (isPropDef(def)) {
27
+ const val = obj[key];
28
+ if (val === undefined) {
29
+ continue;
30
+ }
27
31
  if (res.subMarkers) {
28
32
  checkSubscriptionMarkers(ctx.db, res.subMarkers, def);
29
33
  }
30
- const val = obj[key];
31
34
  const type = def.typeIndex;
32
35
  if (def.separate) {
33
36
  if (type === STRING) {
@@ -81,8 +84,8 @@ function _modify(ctx, res, obj, schema, mod, tree, overwrite, unsafe) {
81
84
  err = writeFixedValue(ctx, val, def, ctx.lastMain + def.start);
82
85
  }
83
86
  }
84
- else if (typeof val === 'object') {
85
- if (val !== null && 'increment' in val) {
87
+ else if (typeof val === 'object' && val !== null) {
88
+ if ('increment' in val) {
86
89
  let increment = val.increment;
87
90
  if (increment === 0) {
88
91
  continue;
@@ -14,7 +14,7 @@ export declare class BasedQueryResponse {
14
14
  [inspect.custom](depth: number): string;
15
15
  debug(): this;
16
16
  node(index?: number): any;
17
- [Symbol.iterator](): Generator<Item, void, unknown>;
17
+ [Symbol.iterator](): Generator<Partial<Item>, void, unknown>;
18
18
  inspect(depth?: number): this;
19
19
  forEach(fn: (item: any, key: number) => void): void;
20
20
  map(fn: (item: any, key: number) => any): any[];
@@ -59,9 +59,15 @@ export class BasedQueryResponse {
59
59
  while (i < result.byteLength - 4) {
60
60
  let id = readUint32(result, i);
61
61
  i += 4;
62
- const item = {
63
- id,
64
- };
62
+ let item;
63
+ if (this.def.aggregation == -999 /* AggFn.NONE */) {
64
+ item = {};
65
+ }
66
+ else {
67
+ item = {
68
+ id,
69
+ };
70
+ }
65
71
  if (this.def.search) {
66
72
  item.$searchScore = readFloatLE(result, i);
67
73
  i += 4;
@@ -396,7 +396,7 @@ export const resultToObject = (q, result, end, offset = 0) => {
396
396
  while (i < end) {
397
397
  const id = readUint32(result, i);
398
398
  i += 4;
399
- var item;
399
+ let item;
400
400
  if (q.aggregation == -999 /* AggFn.NONE */) {
401
401
  item = {};
402
402
  }
@@ -4,6 +4,8 @@ import { createTree } from './csmt/index.js';
4
4
  import { Worker, MessagePort } from 'node:worker_threads';
5
5
  import { TransformFns } from './migrate/index.js';
6
6
  import exitHook from 'exit-hook';
7
+ export declare const SCHEMA_FILE = "schema.json";
8
+ export declare const WRITELOG_FILE = "writelog.json";
7
9
  declare class SortIndex {
8
10
  constructor(buf: Uint8Array, dbCtxExternal: any);
9
11
  buf: Uint8Array;
@@ -57,7 +59,9 @@ export declare class DbServer {
57
59
  clean?: boolean;
58
60
  hosted?: boolean;
59
61
  }): Promise<void>;
60
- save(): void | Promise<void>;
62
+ save(opts?: {
63
+ forceFullDump?: boolean;
64
+ }): Promise<void>;
61
65
  createCsmtHashFun: () => {
62
66
  update: (buf: Uint8Array) => any;
63
67
  digest: (encoding?: "hex") => Uint8Array | string;
@@ -12,7 +12,8 @@ import { Worker, MessageChannel } from 'node:worker_threads';
12
12
  import { fileURLToPath } from 'node:url';
13
13
  import { setTimeout } from 'node:timers/promises';
14
14
  import { migrate } from './migrate/index.js';
15
- const SCHEMA_FILE = 'schema.json';
15
+ export const SCHEMA_FILE = 'schema.json';
16
+ export const WRITELOG_FILE = 'writelog.json';
16
17
  const __filename = fileURLToPath(import.meta.url);
17
18
  const __dirname = dirname(__filename);
18
19
  const workerPath = join(__dirname, 'worker.js');
@@ -98,8 +99,8 @@ export class DbServer {
98
99
  start(opts) {
99
100
  return start(this, opts);
100
101
  }
101
- save() {
102
- return save(this);
102
+ save(opts) {
103
+ return save(this, false, opts?.forceFullDump ?? false);
103
104
  }
104
105
  createCsmtHashFun = () => {
105
106
  // We can just reuse it as long as we only have one tree.
@@ -1,2 +1,21 @@
1
1
  import { DbServer } from './index.js';
2
- export declare function save<T extends boolean>(db: DbServer, sync?: T): T extends true ? void : Promise<void>;
2
+ export type Writelog = {
3
+ ts: number;
4
+ types: {
5
+ [t: number]: {
6
+ lastId: number;
7
+ blockCapacity: number;
8
+ };
9
+ };
10
+ hash: string;
11
+ commonDump: string;
12
+ rangeDumps: {
13
+ [t: number]: {
14
+ file: string;
15
+ hash: string;
16
+ start: number;
17
+ end: number;
18
+ }[];
19
+ };
20
+ };
21
+ export declare function save<T extends boolean>(db: DbServer, sync?: T, forceFullDump?: boolean): T extends true ? void : Promise<void>;
@@ -1,14 +1,16 @@
1
1
  import native from '../native.js';
2
+ import { isMainThread } from 'node:worker_threads';
2
3
  import { writeFile } from 'node:fs/promises';
3
4
  import { join } from 'node:path';
4
- import { destructureCsmtKey, foreachDirtyBlock } from './tree.js';
5
+ import { destructureCsmtKey, foreachBlock, foreachDirtyBlock, makeCsmtKey } from './tree.js';
6
+ import { WRITELOG_FILE } from './index.js';
5
7
  import { writeFileSync } from 'node:fs';
6
8
  import { bufToHex } from '../utils.js';
7
- const WRITELOG_FILE = 'writelog.json';
9
+ import { createTree } from './csmt/tree.js';
8
10
  const COMMON_SDB_FILE = 'common.sdb';
9
11
  const block_sdb_file = (typeId, start, end) => `${typeId}_${start}_${end}.sdb`;
10
- export function save(db, sync = false) {
11
- if (!db.dirtyRanges.size) {
12
+ export function save(db, sync = false, forceFullDump = false) {
13
+ if (!(isMainThread && (db.dirtyRanges.size || forceFullDump))) {
12
14
  return;
13
15
  }
14
16
  let err;
@@ -17,29 +19,58 @@ export function save(db, sync = false) {
17
19
  if (err) {
18
20
  console.error(`Save common failed: ${err}`);
19
21
  }
20
- foreachDirtyBlock(db, (mtKey, typeId, start, end) => {
21
- const file = block_sdb_file(typeId, start, end);
22
- const path = join(db.fileSystemPath, file);
23
- const hash = new Uint8Array(16);
24
- err = native.saveRange(path, typeId, start, end, db.dbCtxExternal, hash);
25
- if (err) {
26
- console.error(`Save ${typeId}:${start}-${end} failed: ${err}`);
27
- return; // TODO What to do with the merkle tree in db situation?
22
+ if (forceFullDump) {
23
+ // We just rebuild the whole tree
24
+ db.merkleTree = createTree(db.createCsmtHashFun); // TODO This could be somewhere else.
25
+ for (const key in db.schemaTypesParsed) {
26
+ const def = db.schemaTypesParsed[key];
27
+ foreachBlock(db, def, (start, end, _hash) => {
28
+ const typeId = def.id;
29
+ const file = block_sdb_file(typeId, start, end);
30
+ const hash = new Uint8Array(16); // TODO One is unnecessary, probably the arg of this cb
31
+ err = native.saveRange(join(db.fileSystemPath, file), typeId, start, end, db.dbCtxExternal, hash);
32
+ if (err) {
33
+ console.error(`Save ${typeId}:${start}-${end} failed: ${err}`);
34
+ return; // TODO What to do with the merkle tree in db situation?
35
+ }
36
+ const mtKey = makeCsmtKey(typeId, start);
37
+ const data = {
38
+ file,
39
+ typeId,
40
+ start,
41
+ end,
42
+ };
43
+ db.merkleTree.insert(mtKey, hash, data);
44
+ });
28
45
  }
29
- const data = {
30
- file,
31
- typeId,
32
- start,
33
- end,
34
- };
35
- try {
36
- db.merkleTree.delete(mtKey);
37
- }
38
- catch (err) {
39
- // console.error({ err })
40
- }
41
- db.merkleTree.insert(mtKey, hash, data);
42
- });
46
+ }
47
+ else {
48
+ foreachDirtyBlock(db, (mtKey, typeId, start, end) => {
49
+ const file = block_sdb_file(typeId, start, end);
50
+ const path = join(db.fileSystemPath, file);
51
+ const hash = new Uint8Array(16);
52
+ err = native.saveRange(path, typeId, start, end, db.dbCtxExternal, hash);
53
+ if (err) {
54
+ console.error(`Save ${typeId}:${start}-${end} failed: ${err}`);
55
+ // The previous state should remain in the merkle tree for
56
+ // load and sync purposes.
57
+ return;
58
+ }
59
+ const data = {
60
+ file,
61
+ typeId,
62
+ start,
63
+ end,
64
+ };
65
+ try {
66
+ db.merkleTree.delete(mtKey);
67
+ }
68
+ catch (err) {
69
+ // console.error({ err })
70
+ }
71
+ db.merkleTree.insert(mtKey, hash, data);
72
+ });
73
+ }
43
74
  db.dirtyRanges.clear();
44
75
  const types = {};
45
76
  const rangeDumps = {};
@@ -61,11 +92,8 @@ export function save(db, sync = false) {
61
92
  types,
62
93
  commonDump: COMMON_SDB_FILE,
63
94
  rangeDumps,
95
+ hash: bufToHex(db.merkleTree.getRoot()?.hash ?? new Uint8Array(0)),
64
96
  };
65
- const mtRoot = db.merkleTree.getRoot();
66
- if (mtRoot) {
67
- data.hash = bufToHex(mtRoot.hash);
68
- }
69
97
  const filePath = join(db.fileSystemPath, WRITELOG_FILE);
70
98
  const content = JSON.stringify(data);
71
99
  return sync ? writeFileSync(filePath, content) : writeFile(filePath, content);
@@ -1,19 +1,16 @@
1
1
  import { stringHash } from '@saulx/hash';
2
- import { DbWorker } from './index.js';
2
+ import { DbWorker, SCHEMA_FILE, WRITELOG_FILE } from './index.js';
3
3
  import native from '../native.js';
4
4
  import { rm, mkdir, readFile } from 'node:fs/promises';
5
5
  import { join } from 'node:path';
6
6
  import { createTree, hashEq } from './csmt/index.js';
7
- import { foreachBlock } from './tree.js';
7
+ import { foreachBlock, makeCsmtKey } from './tree.js';
8
8
  import { availableParallelism } from 'node:os';
9
9
  import exitHook from 'exit-hook';
10
10
  import './worker.js';
11
11
  import { save } from './save.js';
12
12
  import { DEFAULT_BLOCK_CAPACITY } from '@based/schema/def';
13
13
  import { bufToHex, hexToBuf } from '../utils.js';
14
- const SCHEMA_FILE = 'schema.json';
15
- const WRITELOG_FILE = 'writelog.json';
16
- const makeCsmtKey = (typeId, start) => typeId * 4294967296 + start;
17
14
  export async function start(db, opts) {
18
15
  const path = db.fileSystemPath;
19
16
  const id = stringHash(path) >>> 0;
@@ -94,7 +91,7 @@ export async function start(db, opts) {
94
91
  const oldHash = hexToBuf(writelog.hash);
95
92
  const newHash = db.merkleTree.getRoot()?.hash;
96
93
  if (!hashEq(oldHash, newHash)) {
97
- console.error(`WARN: CSMT hash mismatch: ${writelog.hash} != ${bufToHex(newHash)}`);
94
+ console.error(`WARN: CSMT hash mismatch. expected: ${writelog.hash} actual: ${bufToHex(newHash)}`);
98
95
  }
99
96
  }
100
97
  // start workers
@@ -105,7 +102,7 @@ export async function start(db, opts) {
105
102
  db.workers[i] = new DbWorker(address, db);
106
103
  }
107
104
  if (!opts?.hosted) {
108
- db.unlistenExit = exitHook(async (signal) => {
105
+ db.unlistenExit = exitHook((signal) => {
109
106
  const blockSig = () => { };
110
107
  const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
111
108
  // A really dumb way to block signals temporarily while saving.
@@ -9,5 +9,5 @@ export type CsmtNodeRange = {
9
9
  export declare const destructureCsmtKey: (key: number) => number[];
10
10
  export declare const makeCsmtKey: (typeId: number, start: number) => number;
11
11
  export declare const makeCsmtKeyFromNodeId: (typeId: number, blockCapacity: number, nodeId: number) => number;
12
- export declare function foreachBlock(db: DbServer, def: SchemaTypeDef, cb: (start: number, end: number, hash: Uint8Array) => void): Promise<void>;
12
+ export declare function foreachBlock(db: DbServer, def: SchemaTypeDef, cb: (start: number, end: number, hash: Uint8Array) => void): void;
13
13
  export declare function foreachDirtyBlock(db: DbServer, cb: (mtKey: number, typeId: number, start: number, end: number) => void): Promise<void>;
@@ -8,7 +8,7 @@ export const makeCsmtKeyFromNodeId = (typeId, blockCapacity, nodeId) => {
8
8
  const tmp = nodeId - +!(nodeId % blockCapacity);
9
9
  return typeId * 4294967296 + ((tmp / blockCapacity) | 0) * blockCapacity + 1;
10
10
  };
11
- export async function foreachBlock(db, def, cb) {
11
+ export function foreachBlock(db, def, cb) {
12
12
  const step = def.blockCapacity;
13
13
  for (let start = 1; start <= def.lastId; start += step) {
14
14
  const end = start + step - 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@based/db",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",