@aztec/kv-store 0.86.0 → 0.87.0
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/dest/indexeddb/multi_map.d.ts.map +1 -1
- package/dest/indexeddb/multi_map.js +20 -6
- package/dest/indexeddb/store.d.ts +1 -1
- package/dest/indexeddb/store.d.ts.map +1 -1
- package/dest/indexeddb/store.js +9 -6
- package/dest/interfaces/common.d.ts +9 -8
- package/dest/interfaces/common.d.ts.map +1 -1
- package/dest/interfaces/common.js +8 -3
- package/dest/interfaces/index.d.ts +2 -1
- package/dest/interfaces/index.d.ts.map +1 -1
- package/dest/interfaces/index.js +1 -0
- package/dest/interfaces/map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.js +107 -52
- package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/multi_map_test_suite.js +66 -2
- package/dest/lmdb/map.d.ts +1 -3
- package/dest/lmdb/map.d.ts.map +1 -1
- package/dest/lmdb/store.d.ts.map +1 -1
- package/dest/lmdb/store.js +5 -0
- package/dest/lmdb-v2/array.d.ts.map +1 -1
- package/dest/lmdb-v2/map.d.ts.map +1 -1
- package/dest/lmdb-v2/map.js +3 -3
- package/dest/lmdb-v2/message.d.ts +1 -0
- package/dest/lmdb-v2/message.d.ts.map +1 -1
- package/dest/lmdb-v2/multi_map.d.ts.map +1 -1
- package/dest/lmdb-v2/singleton.d.ts.map +1 -1
- package/dest/lmdb-v2/store.d.ts.map +1 -1
- package/dest/lmdb-v2/store.js +1 -0
- package/dest/lmdb-v2/utils.d.ts +2 -4
- package/dest/lmdb-v2/utils.d.ts.map +1 -1
- package/dest/lmdb-v2/write_transaction.d.ts +2 -4
- package/dest/lmdb-v2/write_transaction.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/indexeddb/multi_map.ts +17 -3
- package/src/indexeddb/store.ts +13 -7
- package/src/interfaces/common.ts +18 -9
- package/src/interfaces/index.ts +2 -1
- package/src/interfaces/map_test_suite.ts +25 -14
- package/src/interfaces/multi_map_test_suite.ts +63 -2
- package/src/lmdb/map.ts +5 -5
- package/src/lmdb/store.ts +10 -1
- package/src/lmdb-v2/array.ts +4 -1
- package/src/lmdb-v2/map.ts +9 -4
- package/src/lmdb-v2/message.ts +1 -0
- package/src/lmdb-v2/multi_map.ts +4 -1
- package/src/lmdb-v2/singleton.ts +4 -1
- package/src/lmdb-v2/store.ts +1 -0
- package/src/lmdb-v2/write_transaction.ts +2 -2
package/dest/lmdb-v2/store.js
CHANGED
|
@@ -163,6 +163,7 @@ export class AztecLMDBStoreV2 {
|
|
|
163
163
|
const resp = await this.sendMessage(LMDBMessageType.STATS, undefined);
|
|
164
164
|
return {
|
|
165
165
|
mappingSize: Number(resp.dbMapSizeBytes),
|
|
166
|
+
physicalFileSize: Number(resp.dbPhysicalFileSizeBytes),
|
|
166
167
|
actualSize: resp.stats.reduce((s, db)=>Number(db.totalUsedSize) + s, 0),
|
|
167
168
|
numItems: resp.stats.reduce((s, db)=>Number(db.numDataItems) + s, 0)
|
|
168
169
|
};
|
package/dest/lmdb-v2/utils.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
3
1
|
import type { Key } from '../interfaces/common.js';
|
|
4
2
|
type Cmp<T> = (a: T, b: T) => -1 | 0 | 1;
|
|
5
3
|
export declare function dedupeSortedArray<T>(arr: T[], cmp: Cmp<T>): void;
|
|
@@ -11,8 +9,8 @@ export declare function removeFromSortedArray<T, N>(arr: T[], val: N, cmp: (a: T
|
|
|
11
9
|
export declare function merge<T>(arr: T[], toInsert: T[], cmp: (a: T, b: T) => -1 | 0 | 1): void;
|
|
12
10
|
export declare function keyCmp(a: [Uint8Array, Uint8Array[] | null], b: [Uint8Array, Uint8Array[] | null]): -1 | 0 | 1;
|
|
13
11
|
export declare function singleKeyCmp(a: [Uint8Array, Uint8Array[] | null], b: Uint8Array): -1 | 0 | 1;
|
|
14
|
-
export declare function minKey(prefix: string): Buffer
|
|
15
|
-
export declare function maxKey(prefix: string): Buffer
|
|
12
|
+
export declare function minKey(prefix: string): Buffer<ArrayBufferLike>;
|
|
13
|
+
export declare function maxKey(prefix: string): Buffer<ArrayBufferLike>;
|
|
16
14
|
export declare function serializeKey(prefix: string, key: Key): Buffer;
|
|
17
15
|
export declare function deserializeKey<K extends Key>(prefix: string, key: Uint8Array): K | false;
|
|
18
16
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnD,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEzC,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAkBhE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAgB7F;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAiBxG;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,CAAC,GAAG,SAAS,CAG1G;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAsB5F;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,QAK5F;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAqBvF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE7G;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAE5F;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,2BAEpC;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,2BAEpC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAE7D;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,CAAC,GAAG,KAAK,CAOxF"}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
3
1
|
import { type Batch } from './message.js';
|
|
4
2
|
import { ReadTransaction } from './read_transaction.js';
|
|
5
3
|
export declare class WriteTransaction extends ReadTransaction {
|
|
@@ -12,8 +10,8 @@ export declare class WriteTransaction extends ReadTransaction {
|
|
|
12
10
|
setIndex(key: Buffer, ...values: Buffer[]): Promise<void>;
|
|
13
11
|
removeIndex(key: Buffer, ...values: Buffer[]): Promise<void>;
|
|
14
12
|
getIndex(key: Buffer): Promise<Uint8Array[]>;
|
|
15
|
-
iterate(startKey: Uint8Array, endKey?: Uint8Array
|
|
16
|
-
iterateIndex(startKey: Uint8Array, endKey?: Uint8Array
|
|
13
|
+
iterate(startKey: Uint8Array, endKey?: Uint8Array, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array]>;
|
|
14
|
+
iterateIndex(startKey: Uint8Array, endKey?: Uint8Array, reverse?: boolean, limit?: number): AsyncIterable<[Uint8Array, Uint8Array[]]>;
|
|
17
15
|
commit(): Promise<void>;
|
|
18
16
|
}
|
|
19
17
|
//# sourceMappingURL=write_transaction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write_transaction.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/write_transaction.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"write_transaction.d.ts","sourceRoot":"","sources":["../../src/lmdb-v2/write_transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAA6B,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,qBAAa,gBAAiB,SAAQ,eAAe;;IAEnD,SAAgB,SAAS,EAAE,KAAK,CAG9B;IACF,SAAgB,UAAU,EAAE,KAAK,CAG/B;IAEF,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAchB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAevE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BzD,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCtC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAuB3C,OAAO,CAC5B,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAanB,YAAY,CACjC,QAAQ,EAAE,UAAU,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,aAAa,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;IAqH/B,MAAM;CAUpB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/kv-store",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.87.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/interfaces/index.js",
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
17
17
|
"test:node": "NODE_NO_WARNINGS=1 mocha --config ./.mocharc.json",
|
|
18
18
|
"test:browser": "wtr --config ./web-test-runner.config.mjs",
|
|
19
|
-
"test": "yarn test:node
|
|
19
|
+
"test": "yarn test:node"
|
|
20
20
|
},
|
|
21
21
|
"inherits": [
|
|
22
22
|
"../package.common.json",
|
|
23
23
|
"./package.local.json"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@aztec/ethereum": "0.
|
|
27
|
-
"@aztec/foundation": "0.
|
|
28
|
-
"@aztec/native": "0.
|
|
29
|
-
"@aztec/stdlib": "0.
|
|
26
|
+
"@aztec/ethereum": "0.87.0",
|
|
27
|
+
"@aztec/foundation": "0.87.0",
|
|
28
|
+
"@aztec/native": "0.87.0",
|
|
29
|
+
"@aztec/stdlib": "0.87.0",
|
|
30
30
|
"idb": "^8.0.0",
|
|
31
31
|
"lmdb": "^3.2.0",
|
|
32
32
|
"msgpackr": "^1.11.2",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@types/jest": "^29.5.0",
|
|
41
41
|
"@types/mocha": "^10.0.10",
|
|
42
42
|
"@types/mocha-each": "^2.0.4",
|
|
43
|
-
"@types/node": "^
|
|
43
|
+
"@types/node": "^22.15.17",
|
|
44
44
|
"@types/sinon": "^17.0.3",
|
|
45
45
|
"@web/dev-server-esbuild": "^1.0.3",
|
|
46
46
|
"@web/test-runner": "^0.19.0",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"mocha-each": "^2.0.1",
|
|
53
53
|
"sinon": "^19.0.2",
|
|
54
54
|
"ts-node": "^10.9.1",
|
|
55
|
-
"typescript": "^5.
|
|
55
|
+
"typescript": "^5.3.3"
|
|
56
56
|
},
|
|
57
57
|
"files": [
|
|
58
58
|
"dest",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"!*.test.*"
|
|
61
61
|
],
|
|
62
62
|
"engines": {
|
|
63
|
-
"node": ">=
|
|
63
|
+
"node": ">=20.10"
|
|
64
64
|
},
|
|
65
65
|
"jest": {
|
|
66
66
|
"extensionsToTreatAsEsm": [
|
|
@@ -12,6 +12,7 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
12
12
|
implements AztecAsyncMultiMap<K, V>
|
|
13
13
|
{
|
|
14
14
|
override async set(key: K, val: V): Promise<void> {
|
|
15
|
+
// Inserting repeated values is a no-op
|
|
15
16
|
const exists = !!(await this.db
|
|
16
17
|
.index('hash')
|
|
17
18
|
.get(
|
|
@@ -23,9 +24,19 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
23
24
|
if (exists) {
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// Get the maximum keyCount for the given key
|
|
28
|
+
// In order to support sparse multimaps, we cannot rely
|
|
29
|
+
// on just counting the number of entries for the key, since we would repeat slots
|
|
30
|
+
// if we delete an entry
|
|
31
|
+
// set -> container:key:0 (keyCount = 1)
|
|
32
|
+
// set -> container:key:1 (keyCount = 2)
|
|
33
|
+
// delete -> container:key:0 (keyCount = 1)
|
|
34
|
+
// set -> container:key:1 <--- already exists!
|
|
35
|
+
// Instead, we iterate in reverse order to get the last inserted entry
|
|
36
|
+
const index = this.db.index('keyCount');
|
|
37
|
+
const rangeQuery = IDBKeyRange.upperBound([this.container, this.normalizeKey(key), Number.MAX_SAFE_INTEGER]);
|
|
38
|
+
const maxEntry = (await index.iterate(rangeQuery, 'prevunique').next()).value;
|
|
39
|
+
const count = maxEntry?.value?.keyCount ?? 0;
|
|
29
40
|
await this.db.put({
|
|
30
41
|
value: val,
|
|
31
42
|
hash: hash(val),
|
|
@@ -37,6 +48,7 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
async *getValuesAsync(key: K): AsyncIterableIterator<V> {
|
|
51
|
+
// Iterate over the whole range of keyCount for the given key
|
|
40
52
|
const index = this.db.index('keyCount');
|
|
41
53
|
const rangeQuery = IDBKeyRange.bound(
|
|
42
54
|
[this.container, this.normalizeKey(key), 0],
|
|
@@ -50,6 +62,8 @@ export class IndexedDBAztecMultiMap<K extends Key, V extends Value>
|
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
async deleteValue(key: K, val: V): Promise<void> {
|
|
65
|
+
// Since we know the value, we can hash it and directly query the "hash" index
|
|
66
|
+
// to avoid having to iterate over all the values
|
|
53
67
|
const fullKey = await this.db
|
|
54
68
|
.index('hash')
|
|
55
69
|
.getKey(
|
package/src/indexeddb/store.ts
CHANGED
|
@@ -38,7 +38,6 @@ export interface AztecIDBSchema extends DBSchema {
|
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
40
|
export class AztecIndexedDBStore implements AztecAsyncKVStore {
|
|
41
|
-
#log: Logger;
|
|
42
41
|
#rootDB: IDBPDatabase<AztecIDBSchema>;
|
|
43
42
|
#name: string;
|
|
44
43
|
|
|
@@ -50,9 +49,12 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
|
|
|
50
49
|
| IndexedDBAztecSingleton<any>
|
|
51
50
|
>();
|
|
52
51
|
|
|
53
|
-
constructor(
|
|
52
|
+
constructor(
|
|
53
|
+
rootDB: IDBPDatabase<AztecIDBSchema>,
|
|
54
|
+
public readonly isEphemeral: boolean,
|
|
55
|
+
name: string,
|
|
56
|
+
) {
|
|
54
57
|
this.#rootDB = rootDB;
|
|
55
|
-
this.#log = log;
|
|
56
58
|
this.#name = name;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
@@ -66,19 +68,23 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
|
|
|
66
68
|
* @returns The store
|
|
67
69
|
*/
|
|
68
70
|
static async open(log: Logger, name?: string, ephemeral: boolean = false): Promise<AztecIndexedDBStore> {
|
|
69
|
-
name = name && !ephemeral ? name :
|
|
71
|
+
name = name && !ephemeral ? name : globalThis.crypto.getRandomValues(new Uint8Array(16)).join('');
|
|
70
72
|
log.debug(`Opening IndexedDB ${ephemeral ? 'temp ' : ''}database with name ${name}`);
|
|
71
73
|
const rootDB = await openDB<AztecIDBSchema>(name, 1, {
|
|
72
74
|
upgrade(db) {
|
|
73
75
|
const objectStore = db.createObjectStore('data', { keyPath: 'slot' });
|
|
74
76
|
|
|
75
77
|
objectStore.createIndex('key', ['container', 'key'], { unique: false });
|
|
76
|
-
|
|
78
|
+
// Keep count of the maximum number of keys ever inserted in the container
|
|
79
|
+
// This allows unique slots for repeated keys, which is useful for multi-maps
|
|
80
|
+
objectStore.createIndex('keyCount', ['container', 'key', 'keyCount'], { unique: true });
|
|
81
|
+
// Keep an index on the pair key-hash for a given container, allowing us to efficiently
|
|
82
|
+
// delete unique values from multi-maps
|
|
77
83
|
objectStore.createIndex('hash', ['container', 'key', 'hash'], { unique: true });
|
|
78
84
|
},
|
|
79
85
|
});
|
|
80
86
|
|
|
81
|
-
const kvStore = new AztecIndexedDBStore(rootDB, ephemeral,
|
|
87
|
+
const kvStore = new AztecIndexedDBStore(rootDB, ephemeral, name);
|
|
82
88
|
return kvStore;
|
|
83
89
|
}
|
|
84
90
|
|
|
@@ -179,7 +185,7 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore {
|
|
|
179
185
|
}
|
|
180
186
|
|
|
181
187
|
estimateSize(): Promise<StoreSize> {
|
|
182
|
-
return Promise.resolve({ mappingSize: 0, actualSize: 0, numItems: 0 });
|
|
188
|
+
return Promise.resolve({ mappingSize: 0, physicalFileSize: 0, actualSize: 0, numItems: 0 });
|
|
183
189
|
}
|
|
184
190
|
|
|
185
191
|
close(): Promise<void> {
|
package/src/interfaces/common.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*/
|
|
4
|
-
export type Key = string | number | Array<string | number>;
|
|
1
|
+
/** The key type for use with the kv-store */
|
|
2
|
+
export type Key = string | number | Uint8Array | Array<string | number>;
|
|
5
3
|
|
|
6
4
|
export type Value = NonNullable<any>;
|
|
7
5
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
*/
|
|
11
|
-
export type Range<K extends Key = Key> = {
|
|
6
|
+
/** A range of keys of arbitrary type. */
|
|
7
|
+
export type CustomRange<K> = {
|
|
12
8
|
/** The key of the first item to include */
|
|
13
9
|
start?: K;
|
|
14
10
|
/** The key of the last item to include */
|
|
@@ -19,4 +15,17 @@ export type Range<K extends Key = Key> = {
|
|
|
19
15
|
limit?: number;
|
|
20
16
|
};
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
/** Maps a custom range into a range of valid key types to iterate over. */
|
|
19
|
+
export function mapRange<CK, K extends Key = Key>(range: CustomRange<CK>, mapFn: (key: CK) => K): Range<K> {
|
|
20
|
+
return {
|
|
21
|
+
start: range.start ? mapFn(range.start) : undefined,
|
|
22
|
+
end: range.end ? mapFn(range.end) : undefined,
|
|
23
|
+
reverse: range.reverse,
|
|
24
|
+
limit: range.limit,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A range of keys to iterate over. */
|
|
29
|
+
export type Range<K extends Key = Key> = CustomRange<K>;
|
|
30
|
+
|
|
31
|
+
export type StoreSize = { mappingSize: number; physicalFileSize: number; actualSize: number; numItems: number };
|
package/src/interfaces/index.ts
CHANGED
|
@@ -5,4 +5,5 @@ export * from './singleton.js';
|
|
|
5
5
|
export * from './store.js';
|
|
6
6
|
export * from './set.js';
|
|
7
7
|
export * from './multi_map.js';
|
|
8
|
-
export
|
|
8
|
+
export { mapRange } from './common.js';
|
|
9
|
+
export type { CustomRange, Range, StoreSize } from './common.js';
|
|
@@ -110,19 +110,30 @@ export function describeAztecMap(
|
|
|
110
110
|
expect(await keys()).to.deep.equal(['baz', 'foo']);
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
113
|
+
for (const [name, data] of [
|
|
114
|
+
['chars', ['a', 'b', 'c', 'd']],
|
|
115
|
+
['numbers', [1, 2, 3, 4]],
|
|
116
|
+
['negative numbers', [-4, -3, -2, -1]],
|
|
117
|
+
['strings', ['aaa', 'bbb', 'ccc', 'ddd']],
|
|
118
|
+
['zero-based numbers', [0, 1, 2, 3]],
|
|
119
|
+
]) {
|
|
120
|
+
it(`supports range queries over ${name} keys`, async () => {
|
|
121
|
+
const [a, b, c, d] = data;
|
|
122
|
+
|
|
123
|
+
await map.set(a, 'a');
|
|
124
|
+
await map.set(b, 'b');
|
|
125
|
+
await map.set(c, 'c');
|
|
126
|
+
await map.set(d, 'd');
|
|
127
|
+
|
|
128
|
+
expect(await keys()).to.deep.equal([a, b, c, d]);
|
|
129
|
+
expect(await keys({ start: b, end: c })).to.deep.equal([b]);
|
|
130
|
+
expect(await keys({ start: b })).to.deep.equal([b, c, d]);
|
|
131
|
+
expect(await keys({ end: c })).to.deep.equal([a, b]);
|
|
132
|
+
expect(await keys({ start: b, end: c, reverse: true })).to.deep.equal([c]);
|
|
133
|
+
expect(await keys({ start: b, limit: 1 })).to.deep.equal([b]);
|
|
134
|
+
expect(await keys({ start: b, reverse: true })).to.deep.equal([d, c]);
|
|
135
|
+
expect(await keys({ end: b, reverse: true })).to.deep.equal([b, a]);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
127
138
|
});
|
|
128
139
|
}
|
|
@@ -124,12 +124,73 @@ export function describeAztecMultiMap(
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
it('should be able to delete individual values for a single key', async () => {
|
|
127
|
+
await multiMap.set('foo', '1');
|
|
128
|
+
await multiMap.set('foo', '2');
|
|
129
|
+
await multiMap.set('foo', '3');
|
|
130
|
+
|
|
131
|
+
await multiMap.deleteValue('foo', '2');
|
|
132
|
+
|
|
133
|
+
expect(await getValues('foo')).to.deep.equal(['1', '3']);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should be able to delete the last and first values for a key', async () => {
|
|
137
|
+
await multiMap.set('foo', '1');
|
|
138
|
+
await multiMap.set('foo', '2');
|
|
139
|
+
await multiMap.set('foo', '3');
|
|
140
|
+
|
|
141
|
+
await multiMap.deleteValue('foo', '1');
|
|
142
|
+
|
|
143
|
+
expect(await getValues('foo')).to.deep.equal(['2', '3']);
|
|
144
|
+
|
|
145
|
+
await multiMap.deleteValue('foo', '3');
|
|
146
|
+
|
|
147
|
+
expect(await getValues('foo')).to.deep.equal(['2']);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should be able to fully clear a key', async () => {
|
|
151
|
+
await multiMap.set('foo', '1');
|
|
152
|
+
await multiMap.set('foo', '2');
|
|
153
|
+
await multiMap.set('foo', '3');
|
|
154
|
+
|
|
155
|
+
await multiMap.deleteValue('foo', '1');
|
|
156
|
+
await multiMap.deleteValue('foo', '3');
|
|
157
|
+
await multiMap.deleteValue('foo', '2');
|
|
158
|
+
|
|
159
|
+
expect(await getValues('foo')).to.deep.equal([]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should be able to insert after deletion', async () => {
|
|
163
|
+
await multiMap.set('foo', '1');
|
|
164
|
+
await multiMap.set('foo', '2');
|
|
165
|
+
await multiMap.set('foo', '3');
|
|
166
|
+
|
|
167
|
+
await multiMap.deleteValue('foo', '2');
|
|
127
168
|
await multiMap.set('foo', 'bar');
|
|
128
|
-
await multiMap.set('foo', 'baz');
|
|
129
169
|
|
|
170
|
+
expect(await getValues('foo')).to.deep.equal(['1', '3', 'bar']);
|
|
171
|
+
|
|
172
|
+
// Delete the just-added entry
|
|
130
173
|
await multiMap.deleteValue('foo', 'bar');
|
|
131
174
|
|
|
132
|
-
expect(await getValues('foo')).to.deep.equal(['
|
|
175
|
+
expect(await getValues('foo')).to.deep.equal(['1', '3']);
|
|
176
|
+
|
|
177
|
+
// Reinsert the initially deleted key
|
|
178
|
+
await multiMap.set('foo', '2');
|
|
179
|
+
|
|
180
|
+
// LMDB and IndexedDB behave differently here, the former ordering by value and the latter by insertion. This is
|
|
181
|
+
// fine because there is no expectation for values in a multimap to be ordered.
|
|
182
|
+
const values = (await getValues('foo')).sort((a, b) => a.localeCompare(b));
|
|
183
|
+
expect(values).to.deep.equal(['1', '2', '3']);
|
|
184
|
+
|
|
185
|
+
// Fully clear the key
|
|
186
|
+
await multiMap.deleteValue('foo', '1');
|
|
187
|
+
await multiMap.deleteValue('foo', '3');
|
|
188
|
+
await multiMap.deleteValue('foo', '2');
|
|
189
|
+
|
|
190
|
+
// Insert some more
|
|
191
|
+
await multiMap.set('foo', 'baz');
|
|
192
|
+
await multiMap.set('foo', 'qux');
|
|
193
|
+
expect(await getValues('foo')).to.deep.equal(['baz', 'qux']);
|
|
133
194
|
});
|
|
134
195
|
|
|
135
196
|
it('supports range queries', async () => {
|
package/src/lmdb/map.ts
CHANGED
|
@@ -82,16 +82,16 @@ export class LmdbAztecMap<K extends Key, V extends Value> implements AztecMap<K,
|
|
|
82
82
|
? this.slot(range.end)
|
|
83
83
|
: this.endSentinel
|
|
84
84
|
: range.start
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
? this.slot(range.start)
|
|
86
|
+
: this.startSentinel;
|
|
87
87
|
|
|
88
88
|
const end = reverse
|
|
89
89
|
? range.start
|
|
90
90
|
? this.slot(range.start)
|
|
91
91
|
: this.startSentinel
|
|
92
92
|
: range.end
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
? this.slot(range.end)
|
|
94
|
+
: this.endSentinel;
|
|
95
95
|
|
|
96
96
|
const lmdbRange: RangeOptions = {
|
|
97
97
|
start,
|
|
@@ -113,7 +113,7 @@ export class LmdbAztecMap<K extends Key, V extends Value> implements AztecMap<K,
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
async *entriesAsync(range?: Range<K>
|
|
116
|
+
async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
|
|
117
117
|
for (const entry of this.entries(range)) {
|
|
118
118
|
yield entry;
|
|
119
119
|
}
|
package/src/lmdb/store.ts
CHANGED
|
@@ -32,7 +32,11 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
32
32
|
#multiMapData: Database<unknown, Key>;
|
|
33
33
|
#log = createLogger('kv-store:lmdb');
|
|
34
34
|
|
|
35
|
-
constructor(
|
|
35
|
+
constructor(
|
|
36
|
+
rootDb: RootDatabase,
|
|
37
|
+
public readonly isEphemeral: boolean,
|
|
38
|
+
private path: string,
|
|
39
|
+
) {
|
|
36
40
|
this.#rootDb = rootDb;
|
|
37
41
|
|
|
38
42
|
// big bucket to store all the data
|
|
@@ -187,10 +191,15 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
187
191
|
if ('mapSize' in stats && typeof stats.mapSize === 'number') {
|
|
188
192
|
mapSize = stats.mapSize;
|
|
189
193
|
}
|
|
194
|
+
let physicalFileSize = 0;
|
|
195
|
+
if ('physicalFileSize' in stats && typeof stats.physicalFileSize === 'number') {
|
|
196
|
+
physicalFileSize = stats.physicalFileSize;
|
|
197
|
+
}
|
|
190
198
|
const dataResult = this.estimateSubDBSize(this.#data);
|
|
191
199
|
const multiResult = this.estimateSubDBSize(this.#multiMapData);
|
|
192
200
|
return Promise.resolve({
|
|
193
201
|
mappingSize: mapSize,
|
|
202
|
+
physicalFileSize: physicalFileSize,
|
|
194
203
|
actualSize: dataResult.actualSize + multiResult.actualSize,
|
|
195
204
|
numItems: dataResult.numItems + multiResult.numItems,
|
|
196
205
|
});
|
package/src/lmdb-v2/array.ts
CHANGED
|
@@ -13,7 +13,10 @@ export class LMDBArray<T extends Value> implements AztecAsyncArray<T> {
|
|
|
13
13
|
private encoder = new Encoder();
|
|
14
14
|
private prefix: string;
|
|
15
15
|
|
|
16
|
-
constructor(
|
|
16
|
+
constructor(
|
|
17
|
+
private store: AztecLMDBStoreV2,
|
|
18
|
+
name: string,
|
|
19
|
+
) {
|
|
17
20
|
this.length = store.openSingleton(name + ':length');
|
|
18
21
|
this.prefix = `array:${name}`;
|
|
19
22
|
}
|
package/src/lmdb-v2/map.ts
CHANGED
|
@@ -11,7 +11,10 @@ export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K,
|
|
|
11
11
|
private prefix: string;
|
|
12
12
|
private encoder = new Encoder();
|
|
13
13
|
|
|
14
|
-
constructor(
|
|
14
|
+
constructor(
|
|
15
|
+
private store: AztecLMDBStoreV2,
|
|
16
|
+
name: string,
|
|
17
|
+
) {
|
|
15
18
|
this.prefix = `map:${name}`;
|
|
16
19
|
}
|
|
17
20
|
/**
|
|
@@ -65,9 +68,11 @@ export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K,
|
|
|
65
68
|
*/
|
|
66
69
|
async *entriesAsync(range?: Range<K>): AsyncIterableIterator<[K, V]> {
|
|
67
70
|
const reverse = range?.reverse ?? false;
|
|
68
|
-
const startKey = range?.start ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
|
|
69
71
|
|
|
70
|
-
const
|
|
72
|
+
const startKey = range?.start !== undefined ? serializeKey(this.prefix, range.start) : minKey(this.prefix);
|
|
73
|
+
|
|
74
|
+
const endKey =
|
|
75
|
+
range?.end !== undefined ? serializeKey(this.prefix, range.end) : reverse ? maxKey(this.prefix) : undefined;
|
|
71
76
|
|
|
72
77
|
let tx: ReadTransaction | undefined = this.store.getCurrentWriteTx();
|
|
73
78
|
const shouldClose = !tx;
|
|
@@ -81,7 +86,7 @@ export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K,
|
|
|
81
86
|
range?.limit,
|
|
82
87
|
)) {
|
|
83
88
|
const deserializedKey = deserializeKey<K>(this.prefix, key);
|
|
84
|
-
if (
|
|
89
|
+
if (deserializedKey === false) {
|
|
85
90
|
break;
|
|
86
91
|
}
|
|
87
92
|
yield [deserializedKey, this.encoder.unpack(val)];
|
package/src/lmdb-v2/message.ts
CHANGED
package/src/lmdb-v2/multi_map.ts
CHANGED
|
@@ -9,7 +9,10 @@ import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
|
|
|
9
9
|
export class LMDBMultiMap<K extends Key, V extends Value> implements AztecAsyncMultiMap<K, V> {
|
|
10
10
|
private prefix: string;
|
|
11
11
|
private encoder = new Encoder();
|
|
12
|
-
constructor(
|
|
12
|
+
constructor(
|
|
13
|
+
private store: AztecLMDBStoreV2,
|
|
14
|
+
name: string,
|
|
15
|
+
) {
|
|
13
16
|
this.prefix = `multimap:${name}`;
|
|
14
17
|
}
|
|
15
18
|
|
package/src/lmdb-v2/singleton.ts
CHANGED
|
@@ -8,7 +8,10 @@ import { serializeKey } from './utils.js';
|
|
|
8
8
|
export class LMDBSingleValue<T> implements AztecAsyncSingleton<T> {
|
|
9
9
|
private key: Uint8Array;
|
|
10
10
|
private encoder = new Encoder();
|
|
11
|
-
constructor(
|
|
11
|
+
constructor(
|
|
12
|
+
private store: AztecLMDBStoreV2,
|
|
13
|
+
name: string,
|
|
14
|
+
) {
|
|
12
15
|
this.key = serializeKey(`singleton:${name}`, 'value');
|
|
13
16
|
}
|
|
14
17
|
|
package/src/lmdb-v2/store.ts
CHANGED
|
@@ -210,6 +210,7 @@ export class AztecLMDBStoreV2 implements AztecAsyncKVStore, LMDBMessageChannel {
|
|
|
210
210
|
const resp = await this.sendMessage(LMDBMessageType.STATS, undefined);
|
|
211
211
|
return {
|
|
212
212
|
mappingSize: Number(resp.dbMapSizeBytes),
|
|
213
|
+
physicalFileSize: Number(resp.dbPhysicalFileSizeBytes),
|
|
213
214
|
actualSize: resp.stats.reduce((s, db) => Number(db.totalUsedSize) + s, 0),
|
|
214
215
|
numItems: resp.stats.reduce((s, db) => Number(db.numDataItems) + s, 0),
|
|
215
216
|
};
|
|
@@ -163,7 +163,7 @@ export class WriteTransaction extends ReadTransaction {
|
|
|
163
163
|
|
|
164
164
|
public override async *iterate(
|
|
165
165
|
startKey: Uint8Array,
|
|
166
|
-
endKey?: Uint8Array
|
|
166
|
+
endKey?: Uint8Array,
|
|
167
167
|
reverse?: boolean,
|
|
168
168
|
limit?: number,
|
|
169
169
|
): AsyncIterable<[Uint8Array, Uint8Array]> {
|
|
@@ -181,7 +181,7 @@ export class WriteTransaction extends ReadTransaction {
|
|
|
181
181
|
|
|
182
182
|
public override async *iterateIndex(
|
|
183
183
|
startKey: Uint8Array,
|
|
184
|
-
endKey?: Uint8Array
|
|
184
|
+
endKey?: Uint8Array,
|
|
185
185
|
reverse?: boolean,
|
|
186
186
|
limit?: number,
|
|
187
187
|
): AsyncIterable<[Uint8Array, Uint8Array[]]> {
|