@aztec/kv-store 0.0.1-commit.b655e406 → 0.0.1-commit.c0b82b2
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/config.d.ts +1 -1
- package/dest/indexeddb/array.d.ts +1 -1
- package/dest/indexeddb/array.d.ts.map +1 -1
- package/dest/indexeddb/array.js +21 -7
- package/dest/indexeddb/index.d.ts +2 -2
- package/dest/indexeddb/index.d.ts.map +1 -1
- package/dest/indexeddb/index.js +3 -6
- package/dest/indexeddb/map.d.ts +8 -2
- package/dest/indexeddb/map.d.ts.map +1 -1
- package/dest/indexeddb/map.js +23 -13
- package/dest/indexeddb/multi_map.d.ts +3 -1
- package/dest/indexeddb/multi_map.d.ts.map +1 -1
- package/dest/indexeddb/multi_map.js +30 -1
- package/dest/indexeddb/set.d.ts +1 -1
- package/dest/indexeddb/set.d.ts.map +1 -1
- package/dest/indexeddb/singleton.d.ts +1 -1
- package/dest/indexeddb/singleton.d.ts.map +1 -1
- package/dest/indexeddb/singleton.js +3 -1
- package/dest/indexeddb/store.d.ts +3 -3
- package/dest/indexeddb/store.d.ts.map +1 -1
- package/dest/indexeddb/store.js +6 -4
- package/dest/interfaces/array.d.ts +1 -1
- package/dest/interfaces/array_test_suite.d.ts +1 -1
- package/dest/interfaces/common.d.ts +1 -1
- package/dest/interfaces/counter.d.ts +1 -1
- package/dest/interfaces/index.d.ts +1 -1
- package/dest/interfaces/map.d.ts +1 -1
- package/dest/interfaces/map_test_suite.d.ts +1 -1
- package/dest/interfaces/map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.js +48 -2
- package/dest/interfaces/multi_map.d.ts +7 -1
- package/dest/interfaces/multi_map.d.ts.map +1 -1
- package/dest/interfaces/multi_map_test_suite.d.ts +1 -1
- package/dest/interfaces/multi_map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/multi_map_test_suite.js +119 -0
- package/dest/interfaces/set.d.ts +1 -1
- package/dest/interfaces/set_test_suite.d.ts +1 -1
- package/dest/interfaces/singleton.d.ts +1 -1
- package/dest/interfaces/singleton_test_suite.d.ts +1 -1
- package/dest/interfaces/store.d.ts +1 -1
- package/dest/interfaces/utils.d.ts +2 -1
- package/dest/interfaces/utils.d.ts.map +1 -1
- package/dest/interfaces/utils.js +2 -1
- package/dest/lmdb/array.d.ts +1 -1
- package/dest/lmdb/array.d.ts.map +1 -1
- package/dest/lmdb/array.js +4 -2
- package/dest/lmdb/counter.d.ts +1 -1
- package/dest/lmdb/counter.d.ts.map +1 -1
- package/dest/lmdb/index.d.ts +2 -2
- package/dest/lmdb/index.d.ts.map +1 -1
- package/dest/lmdb/index.js +3 -3
- package/dest/lmdb/map.d.ts +1 -1
- package/dest/lmdb/map.d.ts.map +1 -1
- package/dest/lmdb/multi_map.d.ts +2 -1
- package/dest/lmdb/multi_map.d.ts.map +1 -1
- package/dest/lmdb/multi_map.js +15 -0
- package/dest/lmdb/set.d.ts +1 -1
- package/dest/lmdb/set.d.ts.map +1 -1
- package/dest/lmdb/singleton.d.ts +1 -1
- package/dest/lmdb/singleton.d.ts.map +1 -1
- package/dest/lmdb/store.d.ts +2 -3
- package/dest/lmdb/store.d.ts.map +1 -1
- package/dest/lmdb/store.js +1 -1
- package/dest/lmdb-v2/array.d.ts +2 -2
- package/dest/lmdb-v2/array.d.ts.map +1 -1
- package/dest/lmdb-v2/array.js +4 -3
- package/dest/lmdb-v2/factory.d.ts +6 -9
- package/dest/lmdb-v2/factory.d.ts.map +1 -1
- package/dest/lmdb-v2/factory.js +14 -10
- package/dest/lmdb-v2/index.d.ts +1 -1
- package/dest/lmdb-v2/map.d.ts +2 -2
- package/dest/lmdb-v2/map.d.ts.map +1 -1
- package/dest/lmdb-v2/map.js +1 -2
- package/dest/lmdb-v2/message.d.ts +1 -1
- package/dest/lmdb-v2/multi_map.d.ts +3 -2
- package/dest/lmdb-v2/multi_map.d.ts.map +1 -1
- package/dest/lmdb-v2/multi_map.js +11 -1
- package/dest/lmdb-v2/read_transaction.d.ts +1 -1
- package/dest/lmdb-v2/read_transaction.d.ts.map +1 -1
- package/dest/lmdb-v2/set.d.ts +1 -1
- package/dest/lmdb-v2/set.d.ts.map +1 -1
- package/dest/lmdb-v2/singleton.d.ts +2 -2
- package/dest/lmdb-v2/singleton.d.ts.map +1 -1
- package/dest/lmdb-v2/singleton.js +1 -2
- package/dest/lmdb-v2/store.d.ts +4 -5
- package/dest/lmdb-v2/store.d.ts.map +1 -1
- package/dest/lmdb-v2/store.js +3 -25
- 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 +1 -10
- package/dest/lmdb-v2/utils.d.ts.map +1 -1
- package/dest/lmdb-v2/utils.js +0 -94
- package/dest/lmdb-v2/write_transaction.d.ts +1 -1
- package/dest/lmdb-v2/write_transaction.d.ts.map +1 -1
- package/dest/lmdb-v2/write_transaction.js +9 -5
- package/dest/stores/index.d.ts +1 -1
- package/dest/stores/l2_tips_store.d.ts +24 -9
- package/dest/stores/l2_tips_store.d.ts.map +1 -1
- package/dest/stores/l2_tips_store.js +61 -52
- package/dest/utils.d.ts +9 -6
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +51 -16
- package/package.json +18 -13
- package/src/indexeddb/array.ts +4 -4
- package/src/indexeddb/index.ts +8 -6
- package/src/indexeddb/map.ts +24 -11
- package/src/indexeddb/multi_map.ts +27 -1
- package/src/indexeddb/singleton.ts +1 -1
- package/src/indexeddb/store.ts +13 -6
- package/src/interfaces/map_test_suite.ts +30 -2
- package/src/interfaces/multi_map.ts +7 -0
- package/src/interfaces/multi_map_test_suite.ts +132 -0
- package/src/interfaces/utils.ts +1 -0
- package/src/lmdb/index.ts +8 -3
- package/src/lmdb/multi_map.ts +16 -0
- package/src/lmdb/store.ts +1 -1
- package/src/lmdb-v2/array.ts +2 -2
- package/src/lmdb-v2/factory.ts +15 -11
- package/src/lmdb-v2/map.ts +2 -2
- package/src/lmdb-v2/multi_map.ts +10 -1
- package/src/lmdb-v2/singleton.ts +2 -2
- package/src/lmdb-v2/store.ts +5 -31
- package/src/lmdb-v2/tx-helpers.ts +29 -0
- package/src/lmdb-v2/utils.ts +0 -118
- package/src/lmdb-v2/write_transaction.ts +9 -8
- package/src/stores/l2_tips_store.ts +65 -56
- package/src/utils.ts +79 -21
|
@@ -107,6 +107,33 @@ export function describeAztecMap(
|
|
|
107
107
|
expect(await size()).to.equal(1);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
+
it('returns 0 for empty map size', async () => {
|
|
111
|
+
expect(await size()).to.equal(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('calculates size correctly across multiple operations', async () => {
|
|
115
|
+
expect(await size()).to.equal(0);
|
|
116
|
+
|
|
117
|
+
// Add items
|
|
118
|
+
await map.set('a', 'value1');
|
|
119
|
+
await map.set('b', 'value2');
|
|
120
|
+
await map.set('c', 'value3');
|
|
121
|
+
expect(await size()).to.equal(3);
|
|
122
|
+
|
|
123
|
+
// Update existing (size should not change)
|
|
124
|
+
await map.set('b', 'updated');
|
|
125
|
+
expect(await size()).to.equal(3);
|
|
126
|
+
|
|
127
|
+
// Delete some
|
|
128
|
+
await map.delete('a');
|
|
129
|
+
expect(await size()).to.equal(2);
|
|
130
|
+
|
|
131
|
+
// Delete all
|
|
132
|
+
await map.delete('b');
|
|
133
|
+
await map.delete('c');
|
|
134
|
+
expect(await size()).to.equal(0);
|
|
135
|
+
});
|
|
136
|
+
|
|
110
137
|
it('should be able to iterate over entries when there are no keys', async () => {
|
|
111
138
|
expect(await entries()).to.deep.equal([]);
|
|
112
139
|
});
|
|
@@ -145,10 +172,11 @@ export function describeAztecMap(
|
|
|
145
172
|
for (const [name, data] of [
|
|
146
173
|
['chars', ['a', 'b', 'c', 'd']],
|
|
147
174
|
['numbers', [1, 2, 3, 4]],
|
|
148
|
-
|
|
149
|
-
// ['negative numbers', [-4, -3, -2, -1]],
|
|
175
|
+
['negative numbers', [-4, -3, -2, -1]],
|
|
150
176
|
['strings', ['aaa', 'bbb', 'ccc', 'ddd']],
|
|
151
177
|
['zero-based numbers', [0, 1, 2, 3]],
|
|
178
|
+
['large numbers', [100, 999, 1000, 1001]],
|
|
179
|
+
['mixed negative and positive', [-1000, -1, 1, 1000]],
|
|
152
180
|
]) {
|
|
153
181
|
it(`supports range queries over ${name} keys`, async () => {
|
|
154
182
|
const [a, b, c, d] = data;
|
|
@@ -29,6 +29,13 @@ export interface AztecAsyncMultiMap<K extends Key, V extends Value> extends Azte
|
|
|
29
29
|
*/
|
|
30
30
|
getValuesAsync(key: K): AsyncIterableIterator<V>;
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Gets the number of values at the given key.
|
|
34
|
+
* @param key - The key to get the number of values from
|
|
35
|
+
* @returns The number of values at the given key
|
|
36
|
+
*/
|
|
37
|
+
getValueCountAsync(key: K): Promise<number>;
|
|
38
|
+
|
|
32
39
|
/**
|
|
33
40
|
* Deletes a specific value at the given key.
|
|
34
41
|
* @param key - The key to delete the value at
|
|
@@ -61,6 +61,13 @@ export function describeAztecMultiMap(
|
|
|
61
61
|
: await toArray((multiMap as AztecAsyncMultiMap<any, any>).getValuesAsync(key));
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
async function getValueCount(
|
|
65
|
+
key: Key,
|
|
66
|
+
sut: AztecAsyncMultiMap<any, any> | AztecMultiMap<any, any> = multiMap,
|
|
67
|
+
): Promise<number> {
|
|
68
|
+
return await (sut as AztecAsyncMultiMap<any, any>).getValueCountAsync(key);
|
|
69
|
+
}
|
|
70
|
+
|
|
64
71
|
it('should be able to set and get values', async () => {
|
|
65
72
|
await multiMap.set('foo', 'bar');
|
|
66
73
|
await multiMap.set('baz', 'qux');
|
|
@@ -97,6 +104,38 @@ export function describeAztecMultiMap(
|
|
|
97
104
|
expect(await size()).to.equal(1);
|
|
98
105
|
});
|
|
99
106
|
|
|
107
|
+
it('returns 0 for empty multimap size', async () => {
|
|
108
|
+
expect(await size()).to.equal(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('calculates size correctly with multiple values per key', async () => {
|
|
112
|
+
expect(await size()).to.equal(0);
|
|
113
|
+
|
|
114
|
+
// Add multiple values for same key
|
|
115
|
+
await multiMap.set('key1', 'value1');
|
|
116
|
+
expect(await size()).to.equal(1);
|
|
117
|
+
await multiMap.set('key1', 'value2');
|
|
118
|
+
expect(await size()).to.equal(2);
|
|
119
|
+
await multiMap.set('key1', 'value3');
|
|
120
|
+
expect(await size()).to.equal(3);
|
|
121
|
+
|
|
122
|
+
// Add values for different key
|
|
123
|
+
await multiMap.set('key2', 'value4');
|
|
124
|
+
expect(await size()).to.equal(4);
|
|
125
|
+
|
|
126
|
+
// Delete one value from key1
|
|
127
|
+
await multiMap.deleteValue('key1', 'value2');
|
|
128
|
+
expect(await size()).to.equal(3);
|
|
129
|
+
|
|
130
|
+
// Delete entire key
|
|
131
|
+
await multiMap.delete('key1');
|
|
132
|
+
expect(await size()).to.equal(1);
|
|
133
|
+
|
|
134
|
+
// Delete last key
|
|
135
|
+
await multiMap.delete('key2');
|
|
136
|
+
expect(await size()).to.equal(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
100
139
|
it('should be able to iterate over entries when there are no keys', async () => {
|
|
101
140
|
expect(await entries()).to.deep.equal([]);
|
|
102
141
|
});
|
|
@@ -238,5 +277,98 @@ export function describeAztecMultiMap(
|
|
|
238
277
|
expect(await keys({ start: 'b', reverse: true })).to.deep.equal(['d', 'c']);
|
|
239
278
|
expect(await keys({ end: 'b', reverse: true })).to.deep.equal(['b', 'a']);
|
|
240
279
|
});
|
|
280
|
+
|
|
281
|
+
it('returns 0 for missing key', async () => {
|
|
282
|
+
expect(await getValueCount('missing')).to.equal(0);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('counts a single value', async () => {
|
|
286
|
+
await multiMap.set('foo', 'bar');
|
|
287
|
+
expect(await getValueCount('foo')).to.equal(1);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('counts multiple distinct values for same key', async () => {
|
|
291
|
+
await multiMap.set('foo', 'bar');
|
|
292
|
+
await multiMap.set('foo', 'baz');
|
|
293
|
+
await multiMap.set('foo', 'qux');
|
|
294
|
+
expect(await getValueCount('foo')).to.equal(3);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('does not increase count for duplicate inserts', async () => {
|
|
298
|
+
await multiMap.set('foo', 'bar');
|
|
299
|
+
await multiMap.set('foo', 'bar');
|
|
300
|
+
await multiMap.set('foo', 'baz');
|
|
301
|
+
await multiMap.set('foo', 'baz');
|
|
302
|
+
expect(await getValueCount('foo')).to.equal(2);
|
|
303
|
+
expect(await getValues('foo')).to.have.members(['bar', 'baz']);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('decrements when deleting a single value', async () => {
|
|
307
|
+
await multiMap.set('foo', '1');
|
|
308
|
+
await multiMap.set('foo', '2');
|
|
309
|
+
await multiMap.set('foo', '3');
|
|
310
|
+
expect(await getValueCount('foo')).to.equal(3);
|
|
311
|
+
await multiMap.deleteValue('foo', '2');
|
|
312
|
+
expect(await getValueCount('foo')).to.equal(2);
|
|
313
|
+
expect(await getValues('foo')).to.have.members(['1', '3']);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('does not change count when deleting a non-existent value', async () => {
|
|
317
|
+
await multiMap.set('foo', '1');
|
|
318
|
+
await multiMap.set('foo', '3');
|
|
319
|
+
expect(await getValueCount('foo')).to.equal(2);
|
|
320
|
+
await multiMap.deleteValue('foo', '2');
|
|
321
|
+
expect(await getValueCount('foo')).to.equal(2);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('clears all values when deleting a key', async () => {
|
|
325
|
+
await multiMap.set('foo', 'bar');
|
|
326
|
+
await multiMap.set('foo', 'baz');
|
|
327
|
+
expect(await getValueCount('foo')).to.equal(2);
|
|
328
|
+
await multiMap.delete('foo');
|
|
329
|
+
expect(await getValueCount('foo')).to.equal(0);
|
|
330
|
+
expect(await getValues('foo')).to.deep.equal([]);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('count equals enumerated values length', async () => {
|
|
334
|
+
await multiMap.set('foo', '1');
|
|
335
|
+
await multiMap.set('foo', '2');
|
|
336
|
+
const vals = await getValues('foo');
|
|
337
|
+
expect(await getValueCount('foo')).to.equal(vals.length);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('sum of per-key counts equals total size', async () => {
|
|
341
|
+
await multiMap.set('foo', '1');
|
|
342
|
+
await multiMap.set('foo', '2');
|
|
343
|
+
await multiMap.set('bar', '3');
|
|
344
|
+
await multiMap.set('bar', '4');
|
|
345
|
+
await multiMap.set('baz', '5');
|
|
346
|
+
|
|
347
|
+
const allKeys = await keys();
|
|
348
|
+
const uniqueKeys = Array.from(new Set(allKeys));
|
|
349
|
+
const counts = await Promise.all(uniqueKeys.map(k => getValueCount(k)));
|
|
350
|
+
const sum = counts.reduce((s, n) => s + n, 0);
|
|
351
|
+
expect(sum).to.equal(await size());
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('supports sparse slots: delete middle, reinsert new, count remains correct', async () => {
|
|
355
|
+
await multiMap.set('foo', '1');
|
|
356
|
+
await multiMap.set('foo', '2');
|
|
357
|
+
await multiMap.set('foo', '3');
|
|
358
|
+
expect(await getValueCount('foo')).to.equal(3);
|
|
359
|
+
await multiMap.deleteValue('foo', '2');
|
|
360
|
+
expect(await getValueCount('foo')).to.equal(2);
|
|
361
|
+
await multiMap.set('foo', '4');
|
|
362
|
+
expect(await getValueCount('foo')).to.equal(3);
|
|
363
|
+
expect(await getValues('foo')).to.have.members(['1', '3', '4']);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('multiple keys are independent', async () => {
|
|
367
|
+
await multiMap.set('foo', '1');
|
|
368
|
+
await multiMap.set('foo', '2');
|
|
369
|
+
await multiMap.set('bar', '3');
|
|
370
|
+
expect(await getValueCount('foo')).to.equal(2);
|
|
371
|
+
expect(await getValueCount('bar')).to.equal(1);
|
|
372
|
+
});
|
|
241
373
|
});
|
|
242
374
|
}
|
package/src/interfaces/utils.ts
CHANGED
package/src/lmdb/index.ts
CHANGED
|
@@ -3,12 +3,17 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
|
|
5
5
|
import type { DataStoreConfig } from '../config.js';
|
|
6
|
-
import {
|
|
6
|
+
import { initStoreForRollupAndSchemaVersion } from '../utils.js';
|
|
7
7
|
import { AztecLmdbStore } from './store.js';
|
|
8
8
|
|
|
9
9
|
export { AztecLmdbStore } from './store.js';
|
|
10
10
|
|
|
11
|
-
export function createStore(
|
|
11
|
+
export function createStore(
|
|
12
|
+
name: string,
|
|
13
|
+
config: DataStoreConfig,
|
|
14
|
+
schemaVersion: number | undefined = undefined,
|
|
15
|
+
log: Logger = createLogger('kv-store'),
|
|
16
|
+
) {
|
|
12
17
|
let { dataDirectory } = config;
|
|
13
18
|
if (typeof dataDirectory !== 'undefined') {
|
|
14
19
|
dataDirectory = join(dataDirectory, name);
|
|
@@ -22,7 +27,7 @@ export function createStore(name: string, config: DataStoreConfig, log: Logger =
|
|
|
22
27
|
|
|
23
28
|
const store = AztecLmdbStore.open(dataDirectory, config.dataStoreMapSizeKb, false);
|
|
24
29
|
if (config.l1Contracts?.rollupAddress) {
|
|
25
|
-
return
|
|
30
|
+
return initStoreForRollupAndSchemaVersion(store, schemaVersion, config.l1Contracts.rollupAddress, log);
|
|
26
31
|
}
|
|
27
32
|
return store;
|
|
28
33
|
}
|
package/src/lmdb/multi_map.ts
CHANGED
|
@@ -29,6 +29,22 @@ export class LmdbAztecMultiMap<K extends Key, V extends Value>
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
getValueCountAsync(key: K): Promise<number> {
|
|
33
|
+
const transaction = this.db.useReadTransaction();
|
|
34
|
+
try {
|
|
35
|
+
const values = this.db.getValues(this.slot(key), {
|
|
36
|
+
transaction,
|
|
37
|
+
});
|
|
38
|
+
let count = 0;
|
|
39
|
+
for (const _ of values) {
|
|
40
|
+
count++;
|
|
41
|
+
}
|
|
42
|
+
return Promise.resolve(count);
|
|
43
|
+
} finally {
|
|
44
|
+
transaction.done();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
async deleteValue(key: K, val: V): Promise<void> {
|
|
33
49
|
await this.db.remove(this.slot(key), [key, val]);
|
|
34
50
|
}
|
package/src/lmdb/store.ts
CHANGED
package/src/lmdb-v2/array.ts
CHANGED
|
@@ -4,8 +4,8 @@ import type { AztecAsyncArray } from '../interfaces/array.js';
|
|
|
4
4
|
import type { Value } from '../interfaces/common.js';
|
|
5
5
|
import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
|
|
6
6
|
import type { ReadTransaction } from './read_transaction.js';
|
|
7
|
-
|
|
8
|
-
import {
|
|
7
|
+
import type { AztecLMDBStoreV2 } from './store.js';
|
|
8
|
+
import { execInReadTx, execInWriteTx } from './tx-helpers.js';
|
|
9
9
|
import { deserializeKey, serializeKey } from './utils.js';
|
|
10
10
|
|
|
11
11
|
export class LMDBArray<T extends Value> implements AztecAsyncArray<T> {
|
package/src/lmdb-v2/factory.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
-
import { type
|
|
3
|
-
import { DatabaseVersionManager } from '@aztec/stdlib/database-version';
|
|
2
|
+
import { type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { DatabaseVersionManager } from '@aztec/stdlib/database-version/manager';
|
|
4
4
|
|
|
5
5
|
import { mkdir, mkdtemp, rm } from 'fs/promises';
|
|
6
6
|
import { tmpdir } from 'os';
|
|
@@ -15,8 +15,9 @@ export async function createStore(
|
|
|
15
15
|
name: string,
|
|
16
16
|
schemaVersion: number,
|
|
17
17
|
config: DataStoreConfig,
|
|
18
|
-
|
|
18
|
+
bindings?: LoggerBindings,
|
|
19
19
|
): Promise<AztecLMDBStoreV2> {
|
|
20
|
+
const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
|
|
20
21
|
const { dataDirectory, l1Contracts } = config;
|
|
21
22
|
|
|
22
23
|
let store: AztecLMDBStoreV2;
|
|
@@ -33,7 +34,7 @@ export async function createStore(
|
|
|
33
34
|
rollupAddress,
|
|
34
35
|
dataDirectory: subDir,
|
|
35
36
|
onOpen: dbDirectory =>
|
|
36
|
-
AztecLMDBStoreV2.new(dbDirectory, config.dataStoreMapSizeKb, MAX_READERS, () => Promise.resolve(),
|
|
37
|
+
AztecLMDBStoreV2.new(dbDirectory, config.dataStoreMapSizeKb, MAX_READERS, () => Promise.resolve(), bindings),
|
|
37
38
|
});
|
|
38
39
|
|
|
39
40
|
log.info(
|
|
@@ -41,7 +42,7 @@ export async function createStore(
|
|
|
41
42
|
);
|
|
42
43
|
[store] = await versionManager.open();
|
|
43
44
|
} else {
|
|
44
|
-
store = await openTmpStore(name, true, config.dataStoreMapSizeKb, MAX_READERS,
|
|
45
|
+
store = await openTmpStore(name, true, config.dataStoreMapSizeKb, MAX_READERS, bindings);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
return store;
|
|
@@ -52,8 +53,9 @@ export async function openTmpStore(
|
|
|
52
53
|
ephemeral: boolean = true,
|
|
53
54
|
dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
|
|
54
55
|
maxReaders = MAX_READERS,
|
|
55
|
-
|
|
56
|
+
bindings?: LoggerBindings,
|
|
56
57
|
): Promise<AztecLMDBStoreV2> {
|
|
58
|
+
const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
|
|
57
59
|
const dataDir = await mkdtemp(join(tmpdir(), name + '-'));
|
|
58
60
|
log.debug(`Created temporary data store at: ${dataDir} with size: ${dbMapSizeKb} KB (LMDB v2)`);
|
|
59
61
|
|
|
@@ -73,17 +75,18 @@ export async function openTmpStore(
|
|
|
73
75
|
|
|
74
76
|
// For temporary stores, we don't need to worry about versioning
|
|
75
77
|
// as they are ephemeral and get cleaned up after use
|
|
76
|
-
return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup,
|
|
78
|
+
return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, bindings);
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
export async function openStoreAt(
|
|
80
82
|
dataDir: string,
|
|
81
83
|
dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
|
|
82
84
|
maxReaders = MAX_READERS,
|
|
83
|
-
|
|
85
|
+
bindings?: LoggerBindings,
|
|
84
86
|
): Promise<AztecLMDBStoreV2> {
|
|
87
|
+
const log = createLogger('kv-store:lmdb-v2', bindings);
|
|
85
88
|
log.debug(`Opening data store at: ${dataDir} with size: ${dbMapSizeKb} KB (LMDB v2)`);
|
|
86
|
-
return await AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, undefined,
|
|
89
|
+
return await AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, undefined, bindings);
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
export async function openVersionedStoreAt(
|
|
@@ -92,14 +95,15 @@ export async function openVersionedStoreAt(
|
|
|
92
95
|
rollupAddress: EthAddress,
|
|
93
96
|
dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
|
|
94
97
|
maxReaders = MAX_READERS,
|
|
95
|
-
|
|
98
|
+
bindings?: LoggerBindings,
|
|
96
99
|
): Promise<AztecLMDBStoreV2> {
|
|
100
|
+
const log = createLogger('kv-store:lmdb-v2', bindings);
|
|
97
101
|
log.debug(`Opening data store at: ${dataDirectory} with size: ${dbMapSizeKb} KB (LMDB v2)`);
|
|
98
102
|
const [store] = await new DatabaseVersionManager({
|
|
99
103
|
schemaVersion,
|
|
100
104
|
rollupAddress,
|
|
101
105
|
dataDirectory,
|
|
102
|
-
onOpen: dataDir => AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, undefined,
|
|
106
|
+
onOpen: dataDir => AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, undefined, bindings),
|
|
103
107
|
}).open();
|
|
104
108
|
return store;
|
|
105
109
|
}
|
package/src/lmdb-v2/map.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { Encoder } from 'msgpackr';
|
|
|
3
3
|
import type { Key, Range, Value } from '../interfaces/common.js';
|
|
4
4
|
import type { AztecAsyncMap } from '../interfaces/map.js';
|
|
5
5
|
import type { ReadTransaction } from './read_transaction.js';
|
|
6
|
-
|
|
7
|
-
import {
|
|
6
|
+
import type { AztecLMDBStoreV2 } from './store.js';
|
|
7
|
+
import { execInReadTx, execInWriteTx } from './tx-helpers.js';
|
|
8
8
|
import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
|
|
9
9
|
|
|
10
10
|
export class LMDBMap<K extends Key, V extends Value> implements AztecAsyncMap<K, V> {
|
package/src/lmdb-v2/multi_map.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Encoder } from 'msgpackr/pack';
|
|
2
|
+
import { MAXIMUM_KEY, toBufferKey } from 'ordered-binary';
|
|
2
3
|
|
|
3
4
|
import type { Key, Range, Value } from '../interfaces/common.js';
|
|
4
5
|
import type { AztecAsyncMultiMap } from '../interfaces/multi_map.js';
|
|
5
6
|
import type { ReadTransaction } from './read_transaction.js';
|
|
6
|
-
import {
|
|
7
|
+
import type { AztecLMDBStoreV2 } from './store.js';
|
|
8
|
+
import { execInReadTx, execInWriteTx } from './tx-helpers.js';
|
|
7
9
|
import { deserializeKey, maxKey, minKey, serializeKey } from './utils.js';
|
|
8
10
|
|
|
9
11
|
export class LMDBMultiMap<K extends Key, V extends Value> implements AztecAsyncMultiMap<K, V> {
|
|
@@ -138,4 +140,11 @@ export class LMDBMultiMap<K extends Key, V extends Value> implements AztecAsyncM
|
|
|
138
140
|
yield this.encoder.unpack(value);
|
|
139
141
|
}
|
|
140
142
|
}
|
|
143
|
+
|
|
144
|
+
getValueCountAsync(key: K): Promise<number> {
|
|
145
|
+
const startKey = serializeKey(this.prefix, key);
|
|
146
|
+
const endKey = toBufferKey([this.prefix, key, MAXIMUM_KEY]);
|
|
147
|
+
|
|
148
|
+
return execInReadTx(this.store, tx => tx.countEntriesIndex(startKey, endKey, false));
|
|
149
|
+
}
|
|
141
150
|
}
|
package/src/lmdb-v2/singleton.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Encoder } from 'msgpackr';
|
|
2
2
|
|
|
3
3
|
import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
|
|
4
|
-
|
|
5
|
-
import {
|
|
4
|
+
import type { AztecLMDBStoreV2 } from './store.js';
|
|
5
|
+
import { execInReadTx, execInWriteTx } from './tx-helpers.js';
|
|
6
6
|
import { serializeKey } from './utils.js';
|
|
7
7
|
|
|
8
8
|
export class LMDBSingleValue<T> implements AztecAsyncSingleton<T> {
|
package/src/lmdb-v2/store.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
1
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { Semaphore, SerialQueue } from '@aztec/foundation/queue';
|
|
3
3
|
import { MsgpackChannel, NativeLMDBStore } from '@aztec/native';
|
|
4
4
|
|
|
@@ -13,9 +13,7 @@ import type { AztecAsyncMultiMap } from '../interfaces/multi_map.js';
|
|
|
13
13
|
import type { AztecAsyncSet } from '../interfaces/set.js';
|
|
14
14
|
import type { AztecAsyncSingleton } from '../interfaces/singleton.js';
|
|
15
15
|
import type { AztecAsyncKVStore } from '../interfaces/store.js';
|
|
16
|
-
// eslint-disable-next-line import/no-cycle
|
|
17
16
|
import { LMDBArray } from './array.js';
|
|
18
|
-
// eslint-disable-next-line import/no-cycle
|
|
19
17
|
import { LMDBMap } from './map.js';
|
|
20
18
|
import {
|
|
21
19
|
Database,
|
|
@@ -27,10 +25,11 @@ import {
|
|
|
27
25
|
import { LMDBMultiMap } from './multi_map.js';
|
|
28
26
|
import { ReadTransaction } from './read_transaction.js';
|
|
29
27
|
import { LMDBSet } from './set.js';
|
|
30
|
-
// eslint-disable-next-line import/no-cycle
|
|
31
28
|
import { LMDBSingleValue } from './singleton.js';
|
|
32
29
|
import { WriteTransaction } from './write_transaction.js';
|
|
33
30
|
|
|
31
|
+
export { execInReadTx, execInWriteTx } from './tx-helpers.js';
|
|
32
|
+
|
|
34
33
|
export class AztecLMDBStoreV2 implements AztecAsyncKVStore, LMDBMessageChannel {
|
|
35
34
|
private open = false;
|
|
36
35
|
private channel: MsgpackChannel<LMDBMessageType, LMDBRequestBody, LMDBResponseBody>;
|
|
@@ -76,8 +75,9 @@ export class AztecLMDBStoreV2 implements AztecAsyncKVStore, LMDBMessageChannel {
|
|
|
76
75
|
dbMapSizeKb: number = 10 * 1024 * 1024,
|
|
77
76
|
maxReaders: number = 16,
|
|
78
77
|
cleanup?: () => Promise<void>,
|
|
79
|
-
|
|
78
|
+
bindings?: LoggerBindings,
|
|
80
79
|
) {
|
|
80
|
+
const log = createLogger('kv-store:lmdb-v2', bindings);
|
|
81
81
|
const db = new AztecLMDBStoreV2(dataDir, dbMapSizeKb, maxReaders, log, cleanup);
|
|
82
82
|
await db.start();
|
|
83
83
|
return db;
|
|
@@ -217,29 +217,3 @@ export class AztecLMDBStoreV2 implements AztecAsyncKVStore, LMDBMessageChannel {
|
|
|
217
217
|
};
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
|
|
221
|
-
export function execInWriteTx<T>(store: AztecLMDBStoreV2, fn: (tx: WriteTransaction) => Promise<T>): Promise<T> {
|
|
222
|
-
const currentWrite = store.getCurrentWriteTx();
|
|
223
|
-
if (currentWrite) {
|
|
224
|
-
return fn(currentWrite);
|
|
225
|
-
} else {
|
|
226
|
-
return store.transactionAsync(fn);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export async function execInReadTx<T>(
|
|
231
|
-
store: AztecLMDBStoreV2,
|
|
232
|
-
fn: (tx: ReadTransaction) => T | Promise<T>,
|
|
233
|
-
): Promise<T> {
|
|
234
|
-
const currentWrite = store.getCurrentWriteTx();
|
|
235
|
-
if (currentWrite) {
|
|
236
|
-
return await fn(currentWrite);
|
|
237
|
-
} else {
|
|
238
|
-
const tx = store.getReadTx();
|
|
239
|
-
try {
|
|
240
|
-
return await fn(tx);
|
|
241
|
-
} finally {
|
|
242
|
-
tx.close();
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
@@ -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
|
+
}
|
package/src/lmdb-v2/utils.ts
CHANGED
|
@@ -2,124 +2,6 @@ import { MAXIMUM_KEY, fromBufferKey, toBufferKey } from 'ordered-binary';
|
|
|
2
2
|
|
|
3
3
|
import type { Key } from '../interfaces/common.js';
|
|
4
4
|
|
|
5
|
-
type Cmp<T> = (a: T, b: T) => -1 | 0 | 1;
|
|
6
|
-
|
|
7
|
-
export function dedupeSortedArray<T>(arr: T[], cmp: Cmp<T>): void {
|
|
8
|
-
for (let i = 0; i < arr.length; i++) {
|
|
9
|
-
let j = i + 1;
|
|
10
|
-
for (; j < arr.length; j++) {
|
|
11
|
-
const res = cmp(arr[i], arr[j]);
|
|
12
|
-
if (res === 0) {
|
|
13
|
-
continue;
|
|
14
|
-
} else if (res < 0) {
|
|
15
|
-
break;
|
|
16
|
-
} else {
|
|
17
|
-
throw new Error('Array not sorted');
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (j - i > 1) {
|
|
22
|
-
arr.splice(i + 1, j - i - 1);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function insertIntoSortedArray<T>(arr: T[], item: T, cmp: (a: T, b: T) => number): void {
|
|
28
|
-
let left = 0;
|
|
29
|
-
let right = arr.length;
|
|
30
|
-
|
|
31
|
-
while (left < right) {
|
|
32
|
-
const mid = (left + right) >> 1;
|
|
33
|
-
const comparison = cmp(arr[mid], item);
|
|
34
|
-
|
|
35
|
-
if (comparison < 0) {
|
|
36
|
-
left = mid + 1;
|
|
37
|
-
} else {
|
|
38
|
-
right = mid;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
arr.splice(left, 0, item);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function findIndexInSortedArray<T, N>(values: T[], needle: N, cmp: (a: T, b: N) => number): number {
|
|
46
|
-
let start = 0;
|
|
47
|
-
let end = values.length - 1;
|
|
48
|
-
|
|
49
|
-
while (start <= end) {
|
|
50
|
-
const mid = start + (((end - start) / 2) | 0);
|
|
51
|
-
const res = cmp(values[mid], needle);
|
|
52
|
-
if (res === 0) {
|
|
53
|
-
return mid;
|
|
54
|
-
} else if (res > 0) {
|
|
55
|
-
end = mid - 1;
|
|
56
|
-
} else {
|
|
57
|
-
start = mid + 1;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return -1;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function findInSortedArray<T, N>(values: T[], needle: N, cmp: (a: T, b: N) => number): T | undefined {
|
|
65
|
-
const idx = findIndexInSortedArray(values, needle, cmp);
|
|
66
|
-
return idx > -1 ? values[idx] : undefined;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function removeAnyOf<T, N>(arr: T[], vals: N[], cmp: (a: T, b: N) => -1 | 0 | 1): void {
|
|
70
|
-
let writeIdx = 0;
|
|
71
|
-
let readIdx = 0;
|
|
72
|
-
let valIdx = 0;
|
|
73
|
-
|
|
74
|
-
while (readIdx < arr.length && valIdx < vals.length) {
|
|
75
|
-
const comparison = cmp(arr[readIdx], vals[valIdx]);
|
|
76
|
-
|
|
77
|
-
if (comparison < 0) {
|
|
78
|
-
arr[writeIdx++] = arr[readIdx++];
|
|
79
|
-
} else if (comparison > 0) {
|
|
80
|
-
valIdx++;
|
|
81
|
-
} else {
|
|
82
|
-
readIdx++;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
while (readIdx < arr.length) {
|
|
87
|
-
arr[writeIdx++] = arr[readIdx++];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
arr.length = writeIdx;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function removeFromSortedArray<T, N>(arr: T[], val: N, cmp: (a: T, b: N) => -1 | 0 | 1) {
|
|
94
|
-
const idx = findIndexInSortedArray(arr, val, cmp);
|
|
95
|
-
if (idx > -1) {
|
|
96
|
-
arr.splice(idx, 1);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function merge<T>(arr: T[], toInsert: T[], cmp: (a: T, b: T) => -1 | 0 | 1): void {
|
|
101
|
-
const result = new Array<T>(arr.length + toInsert.length);
|
|
102
|
-
let i = 0,
|
|
103
|
-
j = 0,
|
|
104
|
-
k = 0;
|
|
105
|
-
|
|
106
|
-
while (i < arr.length && j < toInsert.length) {
|
|
107
|
-
result[k++] = cmp(arr[i], toInsert[j]) <= 0 ? arr[i++] : toInsert[j++];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
while (i < arr.length) {
|
|
111
|
-
result[k++] = arr[i++];
|
|
112
|
-
}
|
|
113
|
-
while (j < toInsert.length) {
|
|
114
|
-
result[k++] = toInsert[j++];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
for (i = 0; i < result.length; i++) {
|
|
118
|
-
arr[i] = result[i];
|
|
119
|
-
}
|
|
120
|
-
arr.length = result.length;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
5
|
export function keyCmp(a: [Uint8Array, Uint8Array[] | null], b: [Uint8Array, Uint8Array[] | null]): -1 | 0 | 1 {
|
|
124
6
|
return Buffer.compare(a[0], b[0]);
|
|
125
7
|
}
|