@aztec-labs/kv-store 6.0.0-nightly.20260829
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dest/bench/shared_map_bench.d.ts +19 -0
- package/dest/bench/shared_map_bench.d.ts.map +1 -0
- package/dest/bench/shared_map_bench.js +91 -0
- package/dest/deprecated/indexeddb/array.d.ts +22 -0
- package/dest/deprecated/indexeddb/array.d.ts.map +1 -0
- package/dest/deprecated/indexeddb/array.js +121 -0
- package/dest/deprecated/indexeddb/index.d.ts +7 -0
- package/dest/deprecated/indexeddb/index.d.ts.map +1 -0
- package/dest/deprecated/indexeddb/index.js +8 -0
- package/dest/deprecated/indexeddb/map.d.ts +38 -0
- package/dest/deprecated/indexeddb/map.d.ts.map +1 -0
- package/dest/deprecated/indexeddb/map.js +121 -0
- package/dest/deprecated/indexeddb/multi_map.d.ts +14 -0
- package/dest/deprecated/indexeddb/multi_map.d.ts.map +1 -0
- package/dest/deprecated/indexeddb/multi_map.js +108 -0
- package/dest/deprecated/indexeddb/set.d.ts +17 -0
- package/dest/deprecated/indexeddb/set.d.ts.map +1 -0
- package/dest/deprecated/indexeddb/set.js +24 -0
- package/dest/deprecated/indexeddb/singleton.d.ts +17 -0
- package/dest/deprecated/indexeddb/singleton.d.ts.map +1 -0
- package/dest/deprecated/indexeddb/singleton.js +41 -0
- package/dest/deprecated/indexeddb/store.d.ts +98 -0
- package/dest/deprecated/indexeddb/store.d.ts.map +1 -0
- package/dest/deprecated/indexeddb/store.js +171 -0
- package/dest/interfaces/array.d.ts +81 -0
- package/dest/interfaces/array.d.ts.map +1 -0
- package/dest/interfaces/array.js +1 -0
- package/dest/interfaces/array_test_suite.d.ts +3 -0
- package/dest/interfaces/array_test_suite.d.ts.map +1 -0
- package/dest/interfaces/array_test_suite.js +103 -0
- package/dest/interfaces/common.d.ts +25 -0
- package/dest/interfaces/common.d.ts.map +1 -0
- package/dest/interfaces/common.js +8 -0
- package/dest/interfaces/counter.d.ts +59 -0
- package/dest/interfaces/counter.d.ts.map +1 -0
- package/dest/interfaces/counter.js +1 -0
- package/dest/interfaces/index.d.ts +10 -0
- package/dest/interfaces/index.d.ts.map +1 -0
- package/dest/interfaces/index.js +8 -0
- package/dest/interfaces/map.d.ts +106 -0
- package/dest/interfaces/map.d.ts.map +1 -0
- package/dest/interfaces/map.js +3 -0
- package/dest/interfaces/map_test_suite.d.ts +3 -0
- package/dest/interfaces/map_test_suite.d.ts.map +1 -0
- package/dest/interfaces/map_test_suite.js +261 -0
- package/dest/interfaces/multi_map.d.ts +41 -0
- package/dest/interfaces/multi_map.d.ts.map +1 -0
- package/dest/interfaces/multi_map.js +3 -0
- package/dest/interfaces/multi_map_test_suite.d.ts +3 -0
- package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -0
- package/dest/interfaces/multi_map_test_suite.js +363 -0
- package/dest/interfaces/set.d.ts +44 -0
- package/dest/interfaces/set.d.ts.map +1 -0
- package/dest/interfaces/set.js +1 -0
- package/dest/interfaces/set_test_suite.d.ts +3 -0
- package/dest/interfaces/set_test_suite.d.ts.map +1 -0
- package/dest/interfaces/set_test_suite.js +95 -0
- package/dest/interfaces/singleton.d.ts +29 -0
- package/dest/interfaces/singleton.d.ts.map +1 -0
- package/dest/interfaces/singleton.js +4 -0
- package/dest/interfaces/singleton_test_suite.d.ts +3 -0
- package/dest/interfaces/singleton_test_suite.d.ts.map +1 -0
- package/dest/interfaces/singleton_test_suite.js +29 -0
- package/dest/interfaces/store.d.ts +120 -0
- package/dest/interfaces/store.d.ts.map +1 -0
- package/dest/interfaces/store.js +1 -0
- package/dest/interfaces/utils.d.ts +18 -0
- package/dest/interfaces/utils.d.ts.map +1 -0
- package/dest/interfaces/utils.js +18 -0
- package/dest/lmdb/array.d.ts +24 -0
- package/dest/lmdb/array.d.ts.map +1 -0
- package/dest/lmdb/array.js +119 -0
- package/dest/lmdb/counter.d.ts +19 -0
- package/dest/lmdb/counter.d.ts.map +1 -0
- package/dest/lmdb/counter.js +50 -0
- package/dest/lmdb/index.d.ts +12 -0
- package/dest/lmdb/index.d.ts.map +1 -0
- package/dest/lmdb/index.js +25 -0
- package/dest/lmdb/map.d.ts +40 -0
- package/dest/lmdb/map.d.ts.map +1 -0
- package/dest/lmdb/map.js +156 -0
- package/dest/lmdb/multi_map.d.ts +13 -0
- package/dest/lmdb/multi_map.d.ts.map +1 -0
- package/dest/lmdb/multi_map.js +44 -0
- package/dest/lmdb/set.d.ts +18 -0
- package/dest/lmdb/set.d.ts.map +1 -0
- package/dest/lmdb/set.js +32 -0
- package/dest/lmdb/singleton.d.ts +14 -0
- package/dest/lmdb/singleton.d.ts.map +1 -0
- package/dest/lmdb/singleton.js +26 -0
- package/dest/lmdb/store.d.ts +94 -0
- package/dest/lmdb/store.d.ts.map +1 -0
- package/dest/lmdb/store.js +200 -0
- package/dest/lmdb-v2/array.d.ts +19 -0
- package/dest/lmdb-v2/array.d.ts.map +1 -0
- package/dest/lmdb-v2/array.js +103 -0
- package/dest/lmdb-v2/factory.d.ts +35 -0
- package/dest/lmdb-v2/factory.d.ts.map +1 -0
- package/dest/lmdb-v2/factory.js +126 -0
- package/dest/lmdb-v2/index.d.ts +3 -0
- package/dest/lmdb-v2/index.d.ts.map +1 -0
- package/dest/lmdb-v2/index.js +2 -0
- package/dest/lmdb-v2/map.d.ts +53 -0
- package/dest/lmdb-v2/map.d.ts.map +1 -0
- package/dest/lmdb-v2/map.js +106 -0
- package/dest/lmdb-v2/message.d.ts +131 -0
- package/dest/lmdb-v2/message.d.ts.map +1 -0
- package/dest/lmdb-v2/message.js +20 -0
- package/dest/lmdb-v2/multi_map.d.ts +52 -0
- package/dest/lmdb-v2/multi_map.d.ts.map +1 -0
- package/dest/lmdb-v2/multi_map.js +123 -0
- package/dest/lmdb-v2/read_transaction.d.ts +16 -0
- package/dest/lmdb-v2/read_transaction.d.ts.map +1 -0
- package/dest/lmdb-v2/read_transaction.js +137 -0
- package/dest/lmdb-v2/set.d.ts +15 -0
- package/dest/lmdb-v2/set.d.ts.map +1 -0
- package/dest/lmdb-v2/set.js +23 -0
- package/dest/lmdb-v2/singleton.d.ts +12 -0
- package/dest/lmdb-v2/singleton.d.ts.map +1 -0
- package/dest/lmdb-v2/singleton.js +31 -0
- package/dest/lmdb-v2/store.d.ts +43 -0
- package/dest/lmdb-v2/store.d.ts.map +1 -0
- package/dest/lmdb-v2/store.js +171 -0
- package/dest/lmdb-v2/tx-helpers.d.ts +6 -0
- package/dest/lmdb-v2/tx-helpers.d.ts.map +1 -0
- package/dest/lmdb-v2/tx-helpers.js +21 -0
- package/dest/lmdb-v2/utils.d.ts +8 -0
- package/dest/lmdb-v2/utils.d.ts.map +1 -0
- package/dest/lmdb-v2/utils.js +32 -0
- package/dest/lmdb-v2/write_transaction.d.ts +17 -0
- package/dest/lmdb-v2/write_transaction.d.ts.map +1 -0
- package/dest/lmdb-v2/write_transaction.js +258 -0
- package/dest/sqlite-opfs/array.d.ts +21 -0
- package/dest/sqlite-opfs/array.d.ts.map +1 -0
- package/dest/sqlite-opfs/array.js +128 -0
- package/dest/sqlite-opfs/errors.d.ts +54 -0
- package/dest/sqlite-opfs/errors.d.ts.map +1 -0
- package/dest/sqlite-opfs/errors.js +77 -0
- package/dest/sqlite-opfs/index.d.ts +14 -0
- package/dest/sqlite-opfs/index.d.ts.map +1 -0
- package/dest/sqlite-opfs/index.js +16 -0
- package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts +32 -0
- package/dest/sqlite-opfs/internal/ordered-binary-browser.d.ts.map +1 -0
- package/dest/sqlite-opfs/internal/ordered-binary-browser.js +448 -0
- package/dest/sqlite-opfs/manage.d.ts +22 -0
- package/dest/sqlite-opfs/manage.d.ts.map +1 -0
- package/dest/sqlite-opfs/manage.js +38 -0
- package/dest/sqlite-opfs/map.d.ts +35 -0
- package/dest/sqlite-opfs/map.d.ts.map +1 -0
- package/dest/sqlite-opfs/map.js +163 -0
- package/dest/sqlite-opfs/messages.d.ts +66 -0
- package/dest/sqlite-opfs/messages.d.ts.map +1 -0
- package/dest/sqlite-opfs/messages.js +5 -0
- package/dest/sqlite-opfs/multi_map.d.ts +16 -0
- package/dest/sqlite-opfs/multi_map.d.ts.map +1 -0
- package/dest/sqlite-opfs/multi_map.js +67 -0
- package/dest/sqlite-opfs/pool_integrity.d.ts +20 -0
- package/dest/sqlite-opfs/pool_integrity.d.ts.map +1 -0
- package/dest/sqlite-opfs/pool_integrity.js +282 -0
- package/dest/sqlite-opfs/pool_lock.d.ts +8 -0
- package/dest/sqlite-opfs/pool_lock.d.ts.map +1 -0
- package/dest/sqlite-opfs/pool_lock.js +47 -0
- package/dest/sqlite-opfs/set.d.ts +13 -0
- package/dest/sqlite-opfs/set.d.ts.map +1 -0
- package/dest/sqlite-opfs/set.js +19 -0
- package/dest/sqlite-opfs/singleton.d.ts +13 -0
- package/dest/sqlite-opfs/singleton.d.ts.map +1 -0
- package/dest/sqlite-opfs/singleton.js +48 -0
- package/dest/sqlite-opfs/store.d.ts +84 -0
- package/dest/sqlite-opfs/store.d.ts.map +1 -0
- package/dest/sqlite-opfs/store.js +318 -0
- package/dest/sqlite-opfs/worker.d.ts +2 -0
- package/dest/sqlite-opfs/worker.d.ts.map +1 -0
- package/dest/sqlite-opfs/worker.js +275 -0
- package/dest/stores/index.d.ts +3 -0
- package/dest/stores/index.d.ts.map +1 -0
- package/dest/stores/index.js +2 -0
- package/dest/stores/l2_tips_store.d.ts +23 -0
- package/dest/stores/l2_tips_store.d.ts.map +1 -0
- package/dest/stores/l2_tips_store.js +64 -0
- package/dest/utils.d.ts +15 -0
- package/dest/utils.d.ts.map +1 -0
- package/dest/utils.js +59 -0
- package/package.json +108 -0
- package/src/bench/shared_map_bench.ts +111 -0
- package/src/deprecated/indexeddb/array.ts +122 -0
- package/src/deprecated/indexeddb/index.ts +12 -0
- package/src/deprecated/indexeddb/map.ts +137 -0
- package/src/deprecated/indexeddb/multi_map.ts +106 -0
- package/src/deprecated/indexeddb/set.ts +37 -0
- package/src/deprecated/indexeddb/singleton.ts +52 -0
- package/src/deprecated/indexeddb/store.ts +214 -0
- package/src/interfaces/array.ts +92 -0
- package/src/interfaces/array_test_suite.ts +128 -0
- package/src/interfaces/common.ts +31 -0
- package/src/interfaces/counter.ts +65 -0
- package/src/interfaces/index.ts +9 -0
- package/src/interfaces/map.ts +117 -0
- package/src/interfaces/map_test_suite.ts +198 -0
- package/src/interfaces/multi_map.ts +45 -0
- package/src/interfaces/multi_map_test_suite.ts +372 -0
- package/src/interfaces/set.ts +48 -0
- package/src/interfaces/set_test_suite.ts +79 -0
- package/src/interfaces/singleton.ts +29 -0
- package/src/interfaces/singleton_test_suite.ts +44 -0
- package/src/interfaces/store.ts +142 -0
- package/src/interfaces/utils.ts +23 -0
- package/src/lmdb/array.ts +140 -0
- package/src/lmdb/counter.ts +67 -0
- package/src/lmdb/index.ts +41 -0
- package/src/lmdb/map.ts +177 -0
- package/src/lmdb/multi_map.ts +51 -0
- package/src/lmdb/set.ts +45 -0
- package/src/lmdb/singleton.ts +35 -0
- package/src/lmdb/store.ts +242 -0
- package/src/lmdb-v2/array.ts +120 -0
- package/src/lmdb-v2/factory.ts +184 -0
- package/src/lmdb-v2/index.ts +2 -0
- package/src/lmdb-v2/map.ts +136 -0
- package/src/lmdb-v2/message.ts +169 -0
- package/src/lmdb-v2/multi_map.ts +150 -0
- package/src/lmdb-v2/read_transaction.ts +156 -0
- package/src/lmdb-v2/set.ts +33 -0
- package/src/lmdb-v2/singleton.ts +38 -0
- package/src/lmdb-v2/store.ts +220 -0
- package/src/lmdb-v2/tx-helpers.ts +29 -0
- package/src/lmdb-v2/utils.ts +32 -0
- package/src/lmdb-v2/write_transaction.ts +315 -0
- package/src/sqlite-opfs/array.ts +124 -0
- package/src/sqlite-opfs/errors.ts +94 -0
- package/src/sqlite-opfs/index.ts +27 -0
- package/src/sqlite-opfs/internal/ordered-binary-browser.js +465 -0
- package/src/sqlite-opfs/manage.ts +46 -0
- package/src/sqlite-opfs/map.ts +163 -0
- package/src/sqlite-opfs/messages.ts +39 -0
- package/src/sqlite-opfs/multi_map.ts +74 -0
- package/src/sqlite-opfs/pool_integrity.ts +327 -0
- package/src/sqlite-opfs/pool_lock.ts +62 -0
- package/src/sqlite-opfs/set.ts +29 -0
- package/src/sqlite-opfs/singleton.ts +48 -0
- package/src/sqlite-opfs/store.ts +331 -0
- package/src/sqlite-opfs/worker.ts +259 -0
- package/src/stores/index.ts +3 -0
- package/src/stores/l2_tips_store.ts +67 -0
- package/src/utils.ts +98 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { CURSOR_PAGE_SIZE, Database, type LMDBMessageChannel, LMDBMessageType } from './message.js';
|
|
2
|
+
|
|
3
|
+
export class ReadTransaction {
|
|
4
|
+
protected open = true;
|
|
5
|
+
|
|
6
|
+
constructor(protected channel: LMDBMessageChannel) {}
|
|
7
|
+
|
|
8
|
+
public close(): void {
|
|
9
|
+
if (!this.open) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
this.open = false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
protected assertIsOpen() {
|
|
16
|
+
if (!this.open) {
|
|
17
|
+
throw new Error('Transaction is closed');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async get(key: Uint8Array): Promise<Uint8Array | undefined> {
|
|
22
|
+
this.assertIsOpen();
|
|
23
|
+
const response = await this.channel.sendMessage(LMDBMessageType.GET, { keys: [key], db: Database.DATA });
|
|
24
|
+
return response.values[0]?.[0] ?? undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async getIndex(key: Uint8Array): Promise<Uint8Array[]> {
|
|
28
|
+
this.assertIsOpen();
|
|
29
|
+
const response = await this.channel.sendMessage(LMDBMessageType.GET, { keys: [key], db: Database.INDEX });
|
|
30
|
+
return response.values[0] ?? [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async *iterate(
|
|
34
|
+
startKey: Uint8Array,
|
|
35
|
+
endKey?: Uint8Array,
|
|
36
|
+
reverse = false,
|
|
37
|
+
limit?: number,
|
|
38
|
+
): AsyncIterable<[Uint8Array, Uint8Array]> {
|
|
39
|
+
yield* this.#iterate(Database.DATA, startKey, endKey, reverse, limit, vals => vals[0]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async *iterateIndex(
|
|
43
|
+
startKey: Uint8Array,
|
|
44
|
+
endKey?: Uint8Array,
|
|
45
|
+
reverse = false,
|
|
46
|
+
limit?: number,
|
|
47
|
+
): AsyncIterable<[Uint8Array, Uint8Array[]]> {
|
|
48
|
+
yield* this.#iterate(Database.INDEX, startKey, endKey, reverse, limit, vals => vals);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public countEntries(startKey: Uint8Array, endKey: Uint8Array, reverse: boolean): Promise<number> {
|
|
52
|
+
return this.#countEntries(Database.DATA, startKey, endKey, reverse);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public countEntriesIndex(startKey: Uint8Array, endKey: Uint8Array, reverse: boolean): Promise<number> {
|
|
56
|
+
return this.#countEntries(Database.INDEX, startKey, endKey, reverse);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async *#iterate<T>(
|
|
60
|
+
db: string,
|
|
61
|
+
startKey: Uint8Array,
|
|
62
|
+
endKey: Uint8Array | undefined,
|
|
63
|
+
reverse: boolean,
|
|
64
|
+
limit: number | undefined,
|
|
65
|
+
map: (val: Uint8Array[]) => T,
|
|
66
|
+
): AsyncIterable<[Uint8Array, T]> {
|
|
67
|
+
this.assertIsOpen();
|
|
68
|
+
|
|
69
|
+
let cursor: number | undefined;
|
|
70
|
+
try {
|
|
71
|
+
const response = await this.channel.sendMessage(LMDBMessageType.START_CURSOR, {
|
|
72
|
+
key: startKey,
|
|
73
|
+
reverse,
|
|
74
|
+
count: typeof limit === 'number' ? Math.min(limit, CURSOR_PAGE_SIZE) : CURSOR_PAGE_SIZE,
|
|
75
|
+
onePage: typeof limit === 'number' && limit < CURSOR_PAGE_SIZE,
|
|
76
|
+
db,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
cursor = response.cursor ?? undefined;
|
|
80
|
+
let entries = response.entries;
|
|
81
|
+
let done = typeof cursor !== 'number';
|
|
82
|
+
let count = 0;
|
|
83
|
+
// emit the first page and any subsequent pages in a while loop
|
|
84
|
+
// NB: end contition is in the middle of the while loop
|
|
85
|
+
while (entries.length > 0) {
|
|
86
|
+
for (const [key, values] of entries) {
|
|
87
|
+
if (typeof limit === 'number' && count >= limit) {
|
|
88
|
+
done = true;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (endKey) {
|
|
93
|
+
const cmp = Buffer.compare(key, endKey);
|
|
94
|
+
if ((!reverse && cmp >= 0) || (reverse && cmp <= 0)) {
|
|
95
|
+
done = true;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
count++;
|
|
101
|
+
yield [key, map(values)];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// cursor is null if DB returned everything in the first page
|
|
105
|
+
if (typeof cursor !== 'number' || done) {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const response = await this.channel.sendMessage(LMDBMessageType.ADVANCE_CURSOR, {
|
|
110
|
+
cursor,
|
|
111
|
+
count: CURSOR_PAGE_SIZE,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
done = response.done;
|
|
115
|
+
entries = response.entries;
|
|
116
|
+
}
|
|
117
|
+
} finally {
|
|
118
|
+
// we might not have anything to close
|
|
119
|
+
if (typeof cursor === 'number') {
|
|
120
|
+
await this.channel.sendMessage(LMDBMessageType.CLOSE_CURSOR, { cursor });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async #countEntries(db: string, startKey: Uint8Array, endKey: Uint8Array, reverse: boolean): Promise<number> {
|
|
126
|
+
this.assertIsOpen();
|
|
127
|
+
|
|
128
|
+
let cursor: number | undefined;
|
|
129
|
+
try {
|
|
130
|
+
const response = await this.channel.sendMessage(LMDBMessageType.START_CURSOR, {
|
|
131
|
+
key: startKey,
|
|
132
|
+
reverse,
|
|
133
|
+
count: 0,
|
|
134
|
+
onePage: false,
|
|
135
|
+
db,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
cursor = response.cursor ?? undefined;
|
|
139
|
+
if (!cursor) {
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const advanceResponse = await this.channel.sendMessage(LMDBMessageType.ADVANCE_CURSOR_COUNT, {
|
|
144
|
+
cursor,
|
|
145
|
+
endKey: endKey,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return advanceResponse.count;
|
|
149
|
+
} finally {
|
|
150
|
+
// we might not have anything to close
|
|
151
|
+
if (typeof cursor === 'number') {
|
|
152
|
+
await this.channel.sendMessage(LMDBMessageType.CLOSE_CURSOR, { cursor });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Key, Range } from '../interfaces/common.js';
|
|
2
|
+
import type { AztecAsyncSet } from '../interfaces/set.js';
|
|
3
|
+
import { LMDBMap } from './map.js';
|
|
4
|
+
import type { AztecLMDBStoreV2 } from './store.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A set backed by LMDB.
|
|
8
|
+
*/
|
|
9
|
+
export class LMDBSet<K extends Key> implements AztecAsyncSet<K> {
|
|
10
|
+
private map: LMDBMap<K, boolean>;
|
|
11
|
+
|
|
12
|
+
constructor(store: AztecLMDBStoreV2, mapName: string) {
|
|
13
|
+
this.map = new LMDBMap(store, mapName);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
hasAsync(key: K): Promise<boolean> {
|
|
17
|
+
return this.map.hasAsync(key);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
add(key: K): Promise<void> {
|
|
21
|
+
return this.map.set(key, true);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
delete(key: K): Promise<void> {
|
|
25
|
+
return this.map.delete(key);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async *entriesAsync(range: Range<K> = {}): AsyncIterableIterator<K> {
|
|
29
|
+
for await (const key of this.map.keysAsync(range)) {
|
|
30
|
+
yield key;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Encoder } from 'msgpackr';
|
|
2
|
+
|
|
3
|
+
import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
|
|
4
|
+
import type { AztecLMDBStoreV2 } from './store.js';
|
|
5
|
+
import { execInReadTx, execInWriteTx } from './tx-helpers.js';
|
|
6
|
+
import { serializeKey } from './utils.js';
|
|
7
|
+
|
|
8
|
+
export class LMDBSingleValue<T> implements AztecAsyncSingleton<T> {
|
|
9
|
+
private key: Uint8Array;
|
|
10
|
+
private encoder = new Encoder();
|
|
11
|
+
constructor(
|
|
12
|
+
private store: AztecLMDBStoreV2,
|
|
13
|
+
name: string,
|
|
14
|
+
) {
|
|
15
|
+
this.key = serializeKey(`singleton:${name}`, 'value');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getAsync(): Promise<T | undefined> {
|
|
19
|
+
return execInReadTx(this.store, async tx => {
|
|
20
|
+
const val = await tx.get(this.key);
|
|
21
|
+
return val ? this.encoder.unpack(val) : undefined;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
set(val: T): Promise<boolean> {
|
|
26
|
+
return execInWriteTx(this.store, async tx => {
|
|
27
|
+
await tx.set(this.key, this.encoder.pack(val));
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
delete(): Promise<boolean> {
|
|
33
|
+
return execInWriteTx(this.store, async tx => {
|
|
34
|
+
await tx.remove(this.key);
|
|
35
|
+
return true;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec-labs/foundation/log';
|
|
2
|
+
import { Semaphore, SerialQueue } from '@aztec-labs/foundation/queue';
|
|
3
|
+
import { MsgpackChannel, NativeLMDBStore } from '@aztec-labs/native';
|
|
4
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
5
|
+
import { mkdir, rm } from 'fs/promises';
|
|
6
|
+
|
|
7
|
+
import type { AztecAsyncArray } from '../interfaces/array.js';
|
|
8
|
+
import type { Key, StoreSize, Value } from '../interfaces/common.js';
|
|
9
|
+
import type { AztecAsyncCounter } from '../interfaces/counter.js';
|
|
10
|
+
import type { AztecAsyncMap } from '../interfaces/map.js';
|
|
11
|
+
import type { AztecAsyncMultiMap } from '../interfaces/multi_map.js';
|
|
12
|
+
import type { AztecAsyncSet } from '../interfaces/set.js';
|
|
13
|
+
import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
|
|
14
|
+
import type { AztecAsyncKVStore } from '../interfaces/store.js';
|
|
15
|
+
import { LMDBArray } from './array.js';
|
|
16
|
+
import { LMDBMap } from './map.js';
|
|
17
|
+
import {
|
|
18
|
+
Database,
|
|
19
|
+
type LMDBMessageChannel,
|
|
20
|
+
LMDBMessageType,
|
|
21
|
+
type LMDBRequestBody,
|
|
22
|
+
type LMDBResponseBody,
|
|
23
|
+
} from './message.js';
|
|
24
|
+
import { LMDBMultiMap } from './multi_map.js';
|
|
25
|
+
import { ReadTransaction } from './read_transaction.js';
|
|
26
|
+
import { LMDBSet } from './set.js';
|
|
27
|
+
import { LMDBSingleValue } from './singleton.js';
|
|
28
|
+
import { WriteTransaction } from './write_transaction.js';
|
|
29
|
+
|
|
30
|
+
export { execInReadTx, execInWriteTx } from './tx-helpers.js';
|
|
31
|
+
|
|
32
|
+
export class AztecLMDBStoreV2 implements AztecAsyncKVStore, LMDBMessageChannel {
|
|
33
|
+
private open = false;
|
|
34
|
+
private channel: MsgpackChannel<LMDBMessageType, LMDBRequestBody, LMDBResponseBody>;
|
|
35
|
+
private writerCtx = new AsyncLocalStorage<WriteTransaction>();
|
|
36
|
+
private writerQueue = new SerialQueue();
|
|
37
|
+
private availableCursors: Semaphore;
|
|
38
|
+
|
|
39
|
+
private constructor(
|
|
40
|
+
private dataDir: string,
|
|
41
|
+
mapSize: number,
|
|
42
|
+
maxReaders: number,
|
|
43
|
+
private log: Logger,
|
|
44
|
+
private cleanup?: () => Promise<void>,
|
|
45
|
+
ephemeral: boolean = false,
|
|
46
|
+
) {
|
|
47
|
+
this.log.info(`Starting data store with maxReaders ${maxReaders}`);
|
|
48
|
+
this.channel = new MsgpackChannel(new NativeLMDBStore(dataDir, mapSize, maxReaders, ephemeral));
|
|
49
|
+
// leave one reader to always be available for regular, atomic, reads
|
|
50
|
+
this.availableCursors = new Semaphore(maxReaders - 1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public get dataDirectory(): string {
|
|
54
|
+
return this.dataDir;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async start() {
|
|
58
|
+
this.writerQueue.start();
|
|
59
|
+
|
|
60
|
+
await this.channel.sendMessage(LMDBMessageType.OPEN_DATABASE, {
|
|
61
|
+
db: Database.DATA,
|
|
62
|
+
uniqueKeys: true,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await this.channel.sendMessage(LMDBMessageType.OPEN_DATABASE, {
|
|
66
|
+
db: Database.INDEX,
|
|
67
|
+
uniqueKeys: false,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
this.open = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static async new(
|
|
74
|
+
dataDir: string,
|
|
75
|
+
dbMapSizeKb: number = 10 * 1024 * 1024,
|
|
76
|
+
maxReaders: number = 16,
|
|
77
|
+
cleanup?: () => Promise<void>,
|
|
78
|
+
bindings?: LoggerBindings,
|
|
79
|
+
ephemeral: boolean = false,
|
|
80
|
+
) {
|
|
81
|
+
const log = createLogger('kv-store:lmdb-v2', bindings);
|
|
82
|
+
const db = new AztecLMDBStoreV2(dataDir, dbMapSizeKb, maxReaders, log, cleanup, ephemeral);
|
|
83
|
+
await db.start();
|
|
84
|
+
return db;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public async backupTo(dstPath: string, compact = true) {
|
|
88
|
+
await mkdir(dstPath, { recursive: true });
|
|
89
|
+
await this.channel.sendMessage(LMDBMessageType.COPY_STORE, { dstPath, compact });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public getReadTx(): ReadTransaction {
|
|
93
|
+
if (!this.open) {
|
|
94
|
+
throw new Error('Store is closed');
|
|
95
|
+
}
|
|
96
|
+
return new ReadTransaction(this);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public getCurrentWriteTx(): WriteTransaction | undefined {
|
|
100
|
+
if (!this.open) {
|
|
101
|
+
throw new Error('Store is closed');
|
|
102
|
+
}
|
|
103
|
+
const currentWrite = this.writerCtx.getStore();
|
|
104
|
+
return currentWrite;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
openMap<K extends Key, V extends Value>(name: string): AztecAsyncMap<K, V> {
|
|
108
|
+
return new LMDBMap(this, name);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
openMultiMap<K extends Key, V extends Value>(name: string): AztecAsyncMultiMap<K, V> {
|
|
112
|
+
return new LMDBMultiMap(this, name);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
openSingleton<T extends Value>(name: string): AztecAsyncSingleton<T> {
|
|
116
|
+
return new LMDBSingleValue(this, name);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
openArray<T extends Value>(name: string): AztecAsyncArray<T> {
|
|
120
|
+
return new LMDBArray(this, name);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
openSet<K extends Key>(name: string): AztecAsyncSet<K> {
|
|
124
|
+
return new LMDBSet(this, name);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
openCounter<K extends Key>(_name: string): AztecAsyncCounter<K> {
|
|
128
|
+
throw new Error('Not implemented');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async transactionAsync<T extends Exclude<any, Promise<any>>>(
|
|
132
|
+
callback: (tx: WriteTransaction) => Promise<T>,
|
|
133
|
+
): Promise<T> {
|
|
134
|
+
if (!this.open) {
|
|
135
|
+
throw new Error('Store is closed');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// transactionAsync might be called recursively
|
|
139
|
+
// send any writes to the parent tx, but don't close it
|
|
140
|
+
// if the callback throws then the parent tx will rollback automatically
|
|
141
|
+
const currentTx = this.getCurrentWriteTx();
|
|
142
|
+
if (currentTx) {
|
|
143
|
+
return await callback(currentTx);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this.writerQueue.put(async () => {
|
|
147
|
+
const tx = new WriteTransaction(this);
|
|
148
|
+
try {
|
|
149
|
+
const res = await this.writerCtx.run(tx, callback, tx);
|
|
150
|
+
await tx.commit();
|
|
151
|
+
return res;
|
|
152
|
+
} catch (err) {
|
|
153
|
+
this.log.error(`Failed to commit transaction`, err);
|
|
154
|
+
throw err;
|
|
155
|
+
} finally {
|
|
156
|
+
tx.close();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
clear(): Promise<void> {
|
|
162
|
+
return Promise.resolve();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async delete(): Promise<void> {
|
|
166
|
+
await this.close();
|
|
167
|
+
await rm(this.dataDir, { recursive: true, force: true, maxRetries: 3 });
|
|
168
|
+
this.log.verbose(`Deleted database files at ${this.dataDir}`);
|
|
169
|
+
await this.cleanup?.();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async close() {
|
|
173
|
+
if (!this.open) {
|
|
174
|
+
// already closed
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
this.open = false;
|
|
178
|
+
await this.writerQueue.cancel();
|
|
179
|
+
await this.channel.sendMessage(LMDBMessageType.CLOSE, undefined);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
public async sendMessage<T extends LMDBMessageType>(
|
|
183
|
+
msgType: T,
|
|
184
|
+
body: LMDBRequestBody[T],
|
|
185
|
+
): Promise<LMDBResponseBody[T]> {
|
|
186
|
+
if (!this.open) {
|
|
187
|
+
throw new Error('Store is closed');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (msgType === LMDBMessageType.START_CURSOR) {
|
|
191
|
+
await this.availableCursors.acquire();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let response: LMDBResponseBody[T] | undefined = undefined;
|
|
195
|
+
try {
|
|
196
|
+
({ response } = await this.channel.sendMessage(msgType, body));
|
|
197
|
+
return response;
|
|
198
|
+
} finally {
|
|
199
|
+
if (
|
|
200
|
+
(msgType === LMDBMessageType.START_CURSOR && response === undefined) ||
|
|
201
|
+
msgType === LMDBMessageType.CLOSE_CURSOR ||
|
|
202
|
+
// it's possible for a START_CURSOR command to not return a cursor (e.g. db is empty)
|
|
203
|
+
(msgType === LMDBMessageType.START_CURSOR &&
|
|
204
|
+
typeof (response as LMDBResponseBody[LMDBMessageType.START_CURSOR]).cursor !== 'number')
|
|
205
|
+
) {
|
|
206
|
+
this.availableCursors.release();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public async estimateSize(): Promise<StoreSize> {
|
|
212
|
+
const resp = await this.sendMessage(LMDBMessageType.STATS, undefined);
|
|
213
|
+
return {
|
|
214
|
+
mappingSize: Number(resp.dbMapSizeBytes),
|
|
215
|
+
physicalFileSize: Number(resp.dbPhysicalFileSizeBytes),
|
|
216
|
+
actualSize: resp.stats.reduce((s, db) => Number(db.totalUsedSize) + s, 0),
|
|
217
|
+
numItems: resp.stats.reduce((s, db) => Number(db.numDataItems) + s, 0),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReadTransaction } from './read_transaction.js';
|
|
2
|
+
import type { AztecLMDBStoreV2 } from './store.js';
|
|
3
|
+
import type { WriteTransaction } from './write_transaction.js';
|
|
4
|
+
|
|
5
|
+
export function execInWriteTx<T>(store: AztecLMDBStoreV2, fn: (tx: WriteTransaction) => Promise<T>): Promise<T> {
|
|
6
|
+
const currentWrite = store.getCurrentWriteTx();
|
|
7
|
+
if (currentWrite) {
|
|
8
|
+
return fn(currentWrite);
|
|
9
|
+
} else {
|
|
10
|
+
return store.transactionAsync(fn);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function execInReadTx<T>(
|
|
15
|
+
store: AztecLMDBStoreV2,
|
|
16
|
+
fn: (tx: ReadTransaction) => T | Promise<T>,
|
|
17
|
+
): Promise<T> {
|
|
18
|
+
const currentWrite = store.getCurrentWriteTx();
|
|
19
|
+
if (currentWrite) {
|
|
20
|
+
return await fn(currentWrite);
|
|
21
|
+
} else {
|
|
22
|
+
const tx = store.getReadTx();
|
|
23
|
+
try {
|
|
24
|
+
return await fn(tx);
|
|
25
|
+
} finally {
|
|
26
|
+
tx.close();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MAXIMUM_KEY, fromBufferKey, toBufferKey } from 'ordered-binary';
|
|
2
|
+
|
|
3
|
+
import type { Key } from '../interfaces/common.js';
|
|
4
|
+
|
|
5
|
+
export function keyCmp(a: [Uint8Array, Uint8Array[] | null], b: [Uint8Array, Uint8Array[] | null]): -1 | 0 | 1 {
|
|
6
|
+
return Buffer.compare(a[0], b[0]);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function singleKeyCmp(a: [Uint8Array, Uint8Array[] | null], b: Uint8Array): -1 | 0 | 1 {
|
|
10
|
+
return Buffer.compare(a[0], b);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function minKey(prefix: string) {
|
|
14
|
+
return toBufferKey([prefix]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function maxKey(prefix: string) {
|
|
18
|
+
return toBufferKey([prefix, MAXIMUM_KEY]);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function serializeKey(prefix: string, key: Key): Buffer {
|
|
22
|
+
return toBufferKey([prefix, key]);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function deserializeKey<K extends Key>(prefix: string, key: Uint8Array): K | false {
|
|
26
|
+
const buf = Buffer.from(key);
|
|
27
|
+
const parsed = fromBufferKey(buf);
|
|
28
|
+
if (!Array.isArray(parsed) || parsed[0] !== prefix) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return parsed[1] as K;
|
|
32
|
+
}
|