@aztec/kv-store 0.66.0 → 0.67.1-devnet
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/array.d.ts +21 -0
- package/dest/indexeddb/array.d.ts.map +1 -0
- package/dest/indexeddb/array.js +96 -0
- package/dest/indexeddb/index.d.ts +7 -0
- package/dest/indexeddb/index.d.ts.map +1 -0
- package/dest/indexeddb/index.js +22 -0
- package/dest/indexeddb/map.d.ts +26 -0
- package/dest/indexeddb/map.d.ts.map +1 -0
- package/dest/indexeddb/map.js +104 -0
- package/dest/indexeddb/set.d.ts +17 -0
- package/dest/indexeddb/set.d.ts.map +1 -0
- package/dest/indexeddb/set.js +25 -0
- package/dest/indexeddb/singleton.d.ts +16 -0
- package/dest/indexeddb/singleton.d.ts.map +1 -0
- package/dest/indexeddb/singleton.js +42 -0
- package/dest/indexeddb/store.d.ts +100 -0
- package/dest/indexeddb/store.d.ts.map +1 -0
- package/dest/indexeddb/store.js +157 -0
- package/dest/interfaces/array.d.ts +43 -11
- package/dest/interfaces/array.d.ts.map +1 -1
- 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 +100 -0
- package/dest/interfaces/counter.d.ts +21 -1
- package/dest/interfaces/counter.d.ts.map +1 -1
- package/dest/interfaces/map.d.ts +80 -12
- package/dest/interfaces/map.d.ts.map +1 -1
- 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 +117 -0
- package/dest/interfaces/set.d.ts +23 -7
- package/dest/interfaces/set.d.ts.map +1 -1
- 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 +59 -0
- package/dest/interfaces/singleton.d.ts +14 -5
- package/dest/interfaces/singleton.d.ts.map +1 -1
- 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 +33 -0
- package/dest/interfaces/store.d.ts +83 -8
- package/dest/interfaces/store.d.ts.map +1 -1
- package/dest/interfaces/store_test_suite.d.ts +3 -0
- package/dest/interfaces/store_test_suite.d.ts.map +1 -0
- package/dest/interfaces/store_test_suite.js +40 -0
- package/dest/interfaces/utils.d.ts +16 -0
- package/dest/interfaces/utils.d.ts.map +1 -0
- package/dest/interfaces/utils.js +19 -0
- package/dest/lmdb/array.d.ts +7 -2
- package/dest/lmdb/array.d.ts.map +1 -1
- package/dest/lmdb/array.js +20 -1
- package/dest/lmdb/counter.d.ts +5 -2
- package/dest/lmdb/counter.d.ts.map +1 -1
- package/dest/lmdb/counter.js +10 -1
- package/dest/lmdb/index.d.ts +10 -0
- package/dest/lmdb/index.d.ts.map +1 -1
- package/dest/lmdb/index.js +28 -1
- package/dest/lmdb/map.d.ts +25 -3
- package/dest/lmdb/map.d.ts.map +1 -1
- package/dest/lmdb/map.js +112 -26
- package/dest/lmdb/set.d.ts +4 -2
- package/dest/lmdb/set.d.ts.map +1 -1
- package/dest/lmdb/set.js +9 -1
- package/dest/lmdb/singleton.d.ts +3 -2
- package/dest/lmdb/singleton.d.ts.map +1 -1
- package/dest/lmdb/singleton.js +4 -1
- package/dest/lmdb/store.d.ts +35 -15
- package/dest/lmdb/store.d.ts.map +1 -1
- package/dest/lmdb/store.js +47 -27
- package/dest/stores/l2_tips_store.d.ts +2 -2
- package/dest/stores/l2_tips_store.d.ts.map +1 -1
- package/dest/stores/l2_tips_store.js +13 -13
- package/dest/utils.d.ts +8 -7
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +6 -29
- package/package.json +54 -37
- package/src/indexeddb/array.ts +118 -0
- package/src/indexeddb/index.ts +29 -0
- package/src/indexeddb/map.ts +142 -0
- package/src/indexeddb/set.ts +37 -0
- package/src/indexeddb/singleton.ts +49 -0
- package/src/indexeddb/store.ts +193 -0
- package/src/interfaces/array.ts +48 -12
- package/src/interfaces/array_test_suite.ts +130 -0
- package/src/interfaces/counter.ts +23 -1
- package/src/interfaces/map.ts +90 -14
- package/src/interfaces/map_test_suite.ts +158 -0
- package/src/interfaces/set.ts +25 -8
- package/src/interfaces/set_test_suite.ts +81 -0
- package/src/interfaces/singleton.ts +14 -6
- package/src/interfaces/singleton_test_suite.ts +46 -0
- package/src/interfaces/store.ts +99 -8
- package/src/interfaces/store_test_suite.ts +56 -0
- package/src/interfaces/utils.ts +21 -0
- package/src/lmdb/array.ts +26 -2
- package/src/lmdb/counter.ts +14 -2
- package/src/lmdb/index.ts +36 -0
- package/src/lmdb/map.ts +130 -23
- package/src/lmdb/set.ts +12 -2
- package/src/lmdb/singleton.ts +6 -2
- package/src/lmdb/store.ts +73 -43
- package/src/stores/l2_tips_store.ts +17 -17
- package/src/utils.ts +8 -37
package/src/interfaces/store.ts
CHANGED
|
@@ -1,32 +1,54 @@
|
|
|
1
|
-
import { type AztecArray } from './array.js';
|
|
1
|
+
import { type AztecArray, type AztecAsyncArray } from './array.js';
|
|
2
2
|
import { type Key } from './common.js';
|
|
3
|
-
import { type AztecCounter } from './counter.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import { type AztecAsyncCounter, type AztecCounter } from './counter.js';
|
|
4
|
+
import {
|
|
5
|
+
type AztecAsyncMap,
|
|
6
|
+
type AztecAsyncMultiMap,
|
|
7
|
+
type AztecMap,
|
|
8
|
+
type AztecMapWithSize,
|
|
9
|
+
type AztecMultiMap,
|
|
10
|
+
type AztecMultiMapWithSize,
|
|
11
|
+
} from './map.js';
|
|
12
|
+
import { type AztecAsyncSet, type AztecSet } from './set.js';
|
|
13
|
+
import { type AztecAsyncSingleton, type AztecSingleton } from './singleton.js';
|
|
7
14
|
|
|
8
15
|
/** A key-value store */
|
|
9
16
|
export interface AztecKVStore {
|
|
17
|
+
syncGetters: true;
|
|
10
18
|
/**
|
|
11
19
|
* Creates a new map.
|
|
12
20
|
* @param name - The name of the map
|
|
13
21
|
* @returns The map
|
|
14
22
|
*/
|
|
15
|
-
openMap<K extends
|
|
23
|
+
openMap<K extends Key, V>(name: string): AztecMap<K, V>;
|
|
16
24
|
|
|
17
25
|
/**
|
|
18
26
|
* Creates a new set.
|
|
19
27
|
* @param name - The name of the set
|
|
20
28
|
* @returns The set
|
|
21
29
|
*/
|
|
22
|
-
openSet<K extends
|
|
30
|
+
openSet<K extends Key>(name: string): AztecSet<K>;
|
|
23
31
|
|
|
24
32
|
/**
|
|
25
33
|
* Creates a new multi-map.
|
|
26
34
|
* @param name - The name of the multi-map
|
|
27
35
|
* @returns The multi-map
|
|
28
36
|
*/
|
|
29
|
-
openMultiMap<K extends
|
|
37
|
+
openMultiMap<K extends Key, V>(name: string): AztecMultiMap<K, V>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates a new multi-map with size.
|
|
41
|
+
* @param name - The name of the multi-map
|
|
42
|
+
* @returns The multi-map
|
|
43
|
+
*/
|
|
44
|
+
openMultiMapWithSize<K extends Key, V>(name: string): AztecMultiMapWithSize<K, V>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new map with size.
|
|
48
|
+
* @param name - The name of the map
|
|
49
|
+
* @returns The map
|
|
50
|
+
*/
|
|
51
|
+
openMapWithSize<K extends Key, V>(name: string): AztecMapWithSize<K, V>;
|
|
30
52
|
|
|
31
53
|
/**
|
|
32
54
|
* Creates a new array.
|
|
@@ -74,3 +96,72 @@ export interface AztecKVStore {
|
|
|
74
96
|
*/
|
|
75
97
|
estimateSize(): { mappingSize: number; actualSize: number; numItems: number };
|
|
76
98
|
}
|
|
99
|
+
|
|
100
|
+
export interface AztecAsyncKVStore {
|
|
101
|
+
/**
|
|
102
|
+
* Creates a new map.
|
|
103
|
+
* @param name - The name of the map
|
|
104
|
+
* @returns The map
|
|
105
|
+
*/
|
|
106
|
+
openMap<K extends Key, V>(name: string): AztecAsyncMap<K, V>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Creates a new set.
|
|
110
|
+
* @param name - The name of the set
|
|
111
|
+
* @returns The set
|
|
112
|
+
*/
|
|
113
|
+
openSet<K extends Key>(name: string): AztecAsyncSet<K>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Creates a new multi-map.
|
|
117
|
+
* @param name - The name of the multi-map
|
|
118
|
+
* @returns The multi-map
|
|
119
|
+
*/
|
|
120
|
+
openMultiMap<K extends Key, V>(name: string): AztecAsyncMultiMap<K, V>;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Creates a new array.
|
|
124
|
+
* @param name - The name of the array
|
|
125
|
+
* @returns The array
|
|
126
|
+
*/
|
|
127
|
+
openArray<T>(name: string): AztecAsyncArray<T>;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Creates a new singleton.
|
|
131
|
+
* @param name - The name of the singleton
|
|
132
|
+
* @returns The singleton
|
|
133
|
+
*/
|
|
134
|
+
openSingleton<T>(name: string): AztecAsyncSingleton<T>;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Creates a new count map.
|
|
138
|
+
* @param name - name of the counter
|
|
139
|
+
*/
|
|
140
|
+
openCounter<K extends Key>(name: string): AztecAsyncCounter<K>;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Starts a transaction. All calls to read/write data while in a transaction are queued and executed atomically.
|
|
144
|
+
* @param callback - The callback to execute in a transaction
|
|
145
|
+
*/
|
|
146
|
+
transactionAsync<T extends Exclude<any, Promise<any>>>(callback: () => Promise<T>): Promise<T>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Clears all entries in the store
|
|
150
|
+
*/
|
|
151
|
+
clear(): Promise<void>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Forks the store.
|
|
155
|
+
*/
|
|
156
|
+
fork(): Promise<AztecAsyncKVStore>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Deletes the store
|
|
160
|
+
*/
|
|
161
|
+
delete(): Promise<void>;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Estimates the size of the store in bytes.
|
|
165
|
+
*/
|
|
166
|
+
estimateSize(): { mappingSize: number; actualSize: number; numItems: number };
|
|
167
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
|
|
3
|
+
import { type AztecAsyncSingleton, type AztecSingleton } from './singleton.js';
|
|
4
|
+
import { type AztecAsyncKVStore, type AztecKVStore } from './store.js';
|
|
5
|
+
import { isSyncStore } from './utils.js';
|
|
6
|
+
|
|
7
|
+
export function describeAztecStore(
|
|
8
|
+
testName: string,
|
|
9
|
+
getPersistentStore: () => Promise<AztecKVStore | AztecAsyncKVStore>,
|
|
10
|
+
getPersistentNoPathStore: () => Promise<AztecKVStore | AztecAsyncKVStore>,
|
|
11
|
+
getEphemeralStore: () => Promise<AztecKVStore | AztecAsyncKVStore>,
|
|
12
|
+
) {
|
|
13
|
+
describe(testName, () => {
|
|
14
|
+
async function get(
|
|
15
|
+
store: AztecKVStore | AztecAsyncKVStore,
|
|
16
|
+
singleton: AztecSingleton<string> | AztecAsyncSingleton<string>,
|
|
17
|
+
) {
|
|
18
|
+
return isSyncStore(store)
|
|
19
|
+
? (singleton as AztecSingleton<string>).get()
|
|
20
|
+
: await (singleton as AztecAsyncSingleton<string>).getAsync();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const itForks = async (store: AztecKVStore | AztecAsyncKVStore) => {
|
|
24
|
+
const singleton = store.openSingleton<string>('singleton');
|
|
25
|
+
await singleton.set('foo');
|
|
26
|
+
|
|
27
|
+
const forkedStore = await store.fork();
|
|
28
|
+
const forkedSingleton = forkedStore.openSingleton<string>('singleton');
|
|
29
|
+
expect(await get(store, singleton)).to.equal('foo');
|
|
30
|
+
await forkedSingleton.set('bar');
|
|
31
|
+
expect(await get(store, singleton)).to.equal('foo');
|
|
32
|
+
expect(await get(forkedStore, forkedSingleton)).to.equal('bar');
|
|
33
|
+
await forkedSingleton.delete();
|
|
34
|
+
expect(await get(store, singleton)).to.equal('foo');
|
|
35
|
+
await forkedStore.delete();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
it('forks a persistent store', async () => {
|
|
39
|
+
const store = await getPersistentStore();
|
|
40
|
+
await itForks(store);
|
|
41
|
+
await store.delete();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('forks a persistent store with no path', async () => {
|
|
45
|
+
const store = await getPersistentNoPathStore();
|
|
46
|
+
await itForks(store);
|
|
47
|
+
await store.delete();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('forks an ephemeral store', async () => {
|
|
51
|
+
const store = await getEphemeralStore();
|
|
52
|
+
await itForks(store);
|
|
53
|
+
await store.delete();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AztecAsyncKVStore, type AztecKVStore } from './store.js';
|
|
2
|
+
|
|
3
|
+
/* eslint-disable no-console */
|
|
4
|
+
export const mockLogger = {
|
|
5
|
+
debug: (msg: string, data: any) => console.log(msg, data),
|
|
6
|
+
info: (msg: string, data: any) => console.log(msg, data),
|
|
7
|
+
warn: (msg: string, data: any) => console.log(msg, data),
|
|
8
|
+
error: (msg: string, data: any) => console.error(msg, data),
|
|
9
|
+
fatal: (msg: string, data: any) => console.error(msg, data),
|
|
10
|
+
silent: (_msg: string, _data: any) => {},
|
|
11
|
+
verbose: (msg: string, data: any) => console.log(msg, data),
|
|
12
|
+
trace: (msg: string, data: any) => console.log(msg, data),
|
|
13
|
+
level: 'trace' as const,
|
|
14
|
+
isLevelEnabled: (_level: string) => true,
|
|
15
|
+
module: 'kv-store:mock-logger',
|
|
16
|
+
};
|
|
17
|
+
/* eslint-enable no-console */
|
|
18
|
+
|
|
19
|
+
export function isSyncStore(store: AztecKVStore | AztecAsyncKVStore): store is AztecAsyncKVStore {
|
|
20
|
+
return (store as AztecKVStore).syncGetters === true;
|
|
21
|
+
}
|
package/src/lmdb/array.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Database, type Key } from 'lmdb';
|
|
2
2
|
|
|
3
|
-
import { type AztecArray } from '../interfaces/array.js';
|
|
3
|
+
import { type AztecArray, type AztecAsyncArray } from '../interfaces/array.js';
|
|
4
4
|
import { LmdbAztecSingleton } from './singleton.js';
|
|
5
5
|
|
|
6
6
|
/** The shape of a key that stores a value in an array */
|
|
@@ -9,7 +9,7 @@ type ArrayIndexSlot = ['array', string, 'slot', number];
|
|
|
9
9
|
/**
|
|
10
10
|
* An persistent array backed by LMDB.
|
|
11
11
|
*/
|
|
12
|
-
export class LmdbAztecArray<T> implements AztecArray<T> {
|
|
12
|
+
export class LmdbAztecArray<T> implements AztecArray<T>, AztecAsyncArray<T> {
|
|
13
13
|
#db: Database<T, ArrayIndexSlot>;
|
|
14
14
|
#name: string;
|
|
15
15
|
#length: LmdbAztecSingleton<number>;
|
|
@@ -24,6 +24,10 @@ export class LmdbAztecArray<T> implements AztecArray<T> {
|
|
|
24
24
|
return this.#length.get() ?? 0;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
lengthAsync(): Promise<number> {
|
|
28
|
+
return Promise.resolve(this.length);
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
push(...vals: T[]): Promise<number> {
|
|
28
32
|
return this.#db.childTransaction(() => {
|
|
29
33
|
let length = this.length;
|
|
@@ -69,6 +73,10 @@ export class LmdbAztecArray<T> implements AztecArray<T> {
|
|
|
69
73
|
return this.#db.get(this.#slot(index));
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
atAsync(index: number): Promise<T | undefined> {
|
|
77
|
+
return Promise.resolve(this.at(index));
|
|
78
|
+
}
|
|
79
|
+
|
|
72
80
|
setAt(index: number, val: T): Promise<boolean> {
|
|
73
81
|
if (index < 0) {
|
|
74
82
|
index = this.length + index;
|
|
@@ -93,16 +101,32 @@ export class LmdbAztecArray<T> implements AztecArray<T> {
|
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
103
|
|
|
104
|
+
async *entriesAsync(): AsyncIterableIterator<[number, T]> {
|
|
105
|
+
for (const [key, value] of this.entries()) {
|
|
106
|
+
yield [key, value];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
96
110
|
*values(): IterableIterator<T> {
|
|
97
111
|
for (const [_, value] of this.entries()) {
|
|
98
112
|
yield value;
|
|
99
113
|
}
|
|
100
114
|
}
|
|
101
115
|
|
|
116
|
+
async *valuesAsync(): AsyncIterableIterator<T> {
|
|
117
|
+
for (const [_, value] of this.entries()) {
|
|
118
|
+
yield value;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
102
122
|
[Symbol.iterator](): IterableIterator<T> {
|
|
103
123
|
return this.values();
|
|
104
124
|
}
|
|
105
125
|
|
|
126
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<T> {
|
|
127
|
+
return this.valuesAsync();
|
|
128
|
+
}
|
|
129
|
+
|
|
106
130
|
#slot(index: number): ArrayIndexSlot {
|
|
107
131
|
return ['array', this.#name, 'slot', index];
|
|
108
132
|
}
|
package/src/lmdb/counter.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { type Key as BaseKey, type Database } from 'lmdb';
|
|
2
2
|
|
|
3
3
|
import { type Key, type Range } from '../interfaces/common.js';
|
|
4
|
-
import { type AztecCounter } from '../interfaces/counter.js';
|
|
4
|
+
import { type AztecAsyncCounter, type AztecCounter } from '../interfaces/counter.js';
|
|
5
5
|
import { LmdbAztecMap } from './map.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* A counter implementation backed by LMDB
|
|
9
9
|
*/
|
|
10
|
-
export class LmdbAztecCounter<K extends Key> implements AztecCounter<K> {
|
|
10
|
+
export class LmdbAztecCounter<K extends Key> implements AztecCounter<K>, AztecAsyncCounter<K> {
|
|
11
11
|
#db: Database;
|
|
12
12
|
#name: string;
|
|
13
13
|
#map: LmdbAztecMap<K, number>;
|
|
@@ -45,11 +45,23 @@ export class LmdbAztecCounter<K extends Key> implements AztecCounter<K> {
|
|
|
45
45
|
return this.#map.get(key) ?? 0;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
getAsync(key: K): Promise<number> {
|
|
49
|
+
return Promise.resolve(this.get(key));
|
|
50
|
+
}
|
|
51
|
+
|
|
48
52
|
entries(range: Range<K> = {}): IterableIterator<[K, number]> {
|
|
49
53
|
return this.#map.entries(range);
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
async *entriesAsync(range: Range<K> = {}): AsyncIterableIterator<[K, number]> {
|
|
57
|
+
yield* this.entries(range);
|
|
58
|
+
}
|
|
59
|
+
|
|
52
60
|
keys(range: Range<K> = {}): IterableIterator<K> {
|
|
53
61
|
return this.#map.keys(range);
|
|
54
62
|
}
|
|
63
|
+
|
|
64
|
+
async *keysAsync(range: Range<K> = {}): AsyncIterableIterator<K> {
|
|
65
|
+
yield* this.keys(range);
|
|
66
|
+
}
|
|
55
67
|
}
|
package/src/lmdb/index.ts
CHANGED
|
@@ -1 +1,37 @@
|
|
|
1
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
2
|
+
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
import { type DataStoreConfig } from '../config.js';
|
|
6
|
+
import { initStoreForRollup } from '../utils.js';
|
|
7
|
+
import { AztecLmdbStore } from './store.js';
|
|
8
|
+
|
|
1
9
|
export { AztecLmdbStore } from './store.js';
|
|
10
|
+
|
|
11
|
+
export function createStore(name: string, config: DataStoreConfig, log: Logger = createLogger('kv-store')) {
|
|
12
|
+
let { dataDirectory } = config;
|
|
13
|
+
if (typeof dataDirectory !== 'undefined') {
|
|
14
|
+
dataDirectory = join(dataDirectory, name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
log.info(
|
|
18
|
+
dataDirectory
|
|
19
|
+
? `Creating ${name} data store at directory ${dataDirectory} with map size ${config.dataStoreMapSizeKB} KB`
|
|
20
|
+
: `Creating ${name} ephemeral data store with map size ${config.dataStoreMapSizeKB} KB`,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const store = AztecLmdbStore.open(dataDirectory, config.dataStoreMapSizeKB, false);
|
|
24
|
+
if (config.l1Contracts?.rollupAddress) {
|
|
25
|
+
return initStoreForRollup(store, config.l1Contracts.rollupAddress, log);
|
|
26
|
+
}
|
|
27
|
+
return store;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Opens a temporary store for testing purposes.
|
|
31
|
+
* @param ephemeral - true if the store should only exist in memory and not automatically be flushed to disk. Optional
|
|
32
|
+
* @returns A new store
|
|
33
|
+
*/
|
|
34
|
+
export function openTmpStore(ephemeral: boolean = false): AztecLmdbStore {
|
|
35
|
+
const mapSize = 1024 * 1024 * 10; // 10 GB map size
|
|
36
|
+
return AztecLmdbStore.open(undefined, mapSize, ephemeral);
|
|
37
|
+
}
|
package/src/lmdb/map.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Database, type RangeOptions } from 'lmdb';
|
|
2
2
|
|
|
3
3
|
import { type Key, type Range } from '../interfaces/common.js';
|
|
4
|
-
import { type AztecMultiMap } from '../interfaces/map.js';
|
|
4
|
+
import { type AztecAsyncMultiMap, type AztecMapWithSize, type AztecMultiMap } from '../interfaces/map.js';
|
|
5
5
|
|
|
6
6
|
/** The slot where a key-value entry would be stored */
|
|
7
7
|
type MapValueSlot<K extends Key | Buffer> = ['map', string, 'slot', K];
|
|
@@ -9,12 +9,12 @@ type MapValueSlot<K extends Key | Buffer> = ['map', string, 'slot', K];
|
|
|
9
9
|
/**
|
|
10
10
|
* A map backed by LMDB.
|
|
11
11
|
*/
|
|
12
|
-
export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V> {
|
|
12
|
+
export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, AztecAsyncMultiMap<K, V> {
|
|
13
13
|
protected db: Database<[K, V], MapValueSlot<K>>;
|
|
14
14
|
protected name: string;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
protected startSentinel: MapValueSlot<Buffer>;
|
|
17
|
+
protected endSentinel: MapValueSlot<Buffer>;
|
|
18
18
|
|
|
19
19
|
constructor(rootDb: Database, mapName: string) {
|
|
20
20
|
this.name = mapName;
|
|
@@ -23,8 +23,8 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V> {
|
|
|
23
23
|
// sentinels are used to define the start and end of the map
|
|
24
24
|
// with LMDB's key encoding, no _primitive value_ can be "less than" an empty buffer or greater than Byte 255
|
|
25
25
|
// these will be used later to answer range queries
|
|
26
|
-
this
|
|
27
|
-
this
|
|
26
|
+
this.startSentinel = ['map', this.name, 'slot', Buffer.from([])];
|
|
27
|
+
this.endSentinel = ['map', this.name, 'slot', Buffer.from([255])];
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
close(): Promise<void> {
|
|
@@ -32,45 +32,59 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V> {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
get(key: K): V | undefined {
|
|
35
|
-
return this.db.get(this
|
|
35
|
+
return this.db.get(this.slot(key))?.[1];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getAsync(key: K): Promise<V | undefined> {
|
|
39
|
+
return Promise.resolve(this.get(key));
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
*getValues(key: K): IterableIterator<V> {
|
|
39
|
-
const values = this.db.getValues(this
|
|
43
|
+
const values = this.db.getValues(this.slot(key));
|
|
40
44
|
for (const value of values) {
|
|
41
45
|
yield value?.[1];
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
async *getValuesAsync(key: K): AsyncIterableIterator<V> {
|
|
50
|
+
for (const value of this.getValues(key)) {
|
|
51
|
+
yield value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
has(key: K): boolean {
|
|
46
|
-
return this.db.doesExist(this
|
|
56
|
+
return this.db.doesExist(this.slot(key));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
hasAsync(key: K): Promise<boolean> {
|
|
60
|
+
return Promise.resolve(this.has(key));
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
async set(key: K, val: V): Promise<void> {
|
|
50
|
-
await this.db.put(this
|
|
64
|
+
await this.db.put(this.slot(key), [key, val]);
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
swap(key: K, fn: (val: V | undefined) => V): Promise<void> {
|
|
54
68
|
return this.db.childTransaction(() => {
|
|
55
|
-
const slot = this
|
|
69
|
+
const slot = this.slot(key);
|
|
56
70
|
const entry = this.db.get(slot);
|
|
57
71
|
void this.db.put(slot, [key, fn(entry?.[1])]);
|
|
58
72
|
});
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
setIfNotExists(key: K, val: V): Promise<boolean> {
|
|
62
|
-
const slot = this
|
|
76
|
+
const slot = this.slot(key);
|
|
63
77
|
return this.db.ifNoExists(slot, () => {
|
|
64
78
|
void this.db.put(slot, [key, val]);
|
|
65
79
|
});
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
async delete(key: K): Promise<void> {
|
|
69
|
-
await this.db.remove(this
|
|
83
|
+
await this.db.remove(this.slot(key));
|
|
70
84
|
}
|
|
71
85
|
|
|
72
86
|
async deleteValue(key: K, val: V): Promise<void> {
|
|
73
|
-
await this.db.remove(this
|
|
87
|
+
await this.db.remove(this.slot(key), [key, val]);
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
*entries(range: Range<K> = {}): IterableIterator<[K, V]> {
|
|
@@ -79,19 +93,19 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V> {
|
|
|
79
93
|
// in that case, we need to swap the start and end sentinels
|
|
80
94
|
const start = reverse
|
|
81
95
|
? range.end
|
|
82
|
-
? this
|
|
83
|
-
: this
|
|
96
|
+
? this.slot(range.end)
|
|
97
|
+
: this.endSentinel
|
|
84
98
|
: range.start
|
|
85
|
-
? this
|
|
86
|
-
: this
|
|
99
|
+
? this.slot(range.start)
|
|
100
|
+
: this.startSentinel;
|
|
87
101
|
|
|
88
102
|
const end = reverse
|
|
89
103
|
? range.start
|
|
90
|
-
? this
|
|
91
|
-
: this
|
|
104
|
+
? this.slot(range.start)
|
|
105
|
+
: this.startSentinel
|
|
92
106
|
: range.end
|
|
93
|
-
? this
|
|
94
|
-
: this
|
|
107
|
+
? this.slot(range.end)
|
|
108
|
+
: this.endSentinel;
|
|
95
109
|
|
|
96
110
|
const lmdbRange: RangeOptions = {
|
|
97
111
|
start,
|
|
@@ -109,19 +123,112 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V> {
|
|
|
109
123
|
}
|
|
110
124
|
}
|
|
111
125
|
|
|
126
|
+
async *entriesAsync(range?: Range<K> | undefined): AsyncIterableIterator<[K, V]> {
|
|
127
|
+
for (const entry of this.entries(range)) {
|
|
128
|
+
yield entry;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
112
132
|
*values(range: Range<K> = {}): IterableIterator<V> {
|
|
113
133
|
for (const [_, value] of this.entries(range)) {
|
|
114
134
|
yield value;
|
|
115
135
|
}
|
|
116
136
|
}
|
|
117
137
|
|
|
138
|
+
async *valuesAsync(range: Range<K> = {}): AsyncIterableIterator<V> {
|
|
139
|
+
for await (const [_, value] of this.entriesAsync(range)) {
|
|
140
|
+
yield value;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
118
144
|
*keys(range: Range<K> = {}): IterableIterator<K> {
|
|
119
145
|
for (const [key, _] of this.entries(range)) {
|
|
120
146
|
yield key;
|
|
121
147
|
}
|
|
122
148
|
}
|
|
123
149
|
|
|
124
|
-
|
|
150
|
+
async *keysAsync(range: Range<K> = {}): AsyncIterableIterator<K> {
|
|
151
|
+
for await (const [key, _] of this.entriesAsync(range)) {
|
|
152
|
+
yield key;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected slot(key: K): MapValueSlot<K> {
|
|
125
157
|
return ['map', this.name, 'slot', key];
|
|
126
158
|
}
|
|
159
|
+
|
|
160
|
+
async clear(): Promise<void> {
|
|
161
|
+
const lmdbRange: RangeOptions = {
|
|
162
|
+
start: this.startSentinel,
|
|
163
|
+
end: this.endSentinel,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const iterator = this.db.getRange(lmdbRange);
|
|
167
|
+
|
|
168
|
+
for (const { key } of iterator) {
|
|
169
|
+
await this.db.remove(key);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export class LmdbAztecMapWithSize<K extends Key, V>
|
|
175
|
+
extends LmdbAztecMap<K, V>
|
|
176
|
+
implements AztecMapWithSize<K, V>, AztecAsyncMultiMap<K, V>
|
|
177
|
+
{
|
|
178
|
+
#sizeCache?: number;
|
|
179
|
+
|
|
180
|
+
constructor(rootDb: Database, mapName: string) {
|
|
181
|
+
super(rootDb, mapName);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
override async set(key: K, val: V): Promise<void> {
|
|
185
|
+
await this.db.childTransaction(() => {
|
|
186
|
+
const exists = this.db.doesExist(this.slot(key));
|
|
187
|
+
this.db.putSync(this.slot(key), [key, val], {
|
|
188
|
+
appendDup: true,
|
|
189
|
+
});
|
|
190
|
+
if (!exists) {
|
|
191
|
+
this.#sizeCache = undefined; // Invalidate cache
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
override async delete(key: K): Promise<void> {
|
|
197
|
+
await this.db.childTransaction(async () => {
|
|
198
|
+
const exists = this.db.doesExist(this.slot(key));
|
|
199
|
+
if (exists) {
|
|
200
|
+
await this.db.remove(this.slot(key));
|
|
201
|
+
this.#sizeCache = undefined; // Invalidate cache
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
override async deleteValue(key: K, val: V): Promise<void> {
|
|
207
|
+
await this.db.childTransaction(async () => {
|
|
208
|
+
const exists = this.db.doesExist(this.slot(key));
|
|
209
|
+
if (exists) {
|
|
210
|
+
await this.db.remove(this.slot(key), [key, val]);
|
|
211
|
+
this.#sizeCache = undefined; // Invalidate cache
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Gets the size of the map by counting entries.
|
|
218
|
+
* @returns The number of entries in the map
|
|
219
|
+
*/
|
|
220
|
+
size(): number {
|
|
221
|
+
if (this.#sizeCache === undefined) {
|
|
222
|
+
this.#sizeCache = this.db.getCount({
|
|
223
|
+
start: this.startSentinel,
|
|
224
|
+
end: this.endSentinel,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return this.#sizeCache;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Reset cache on clear/drop operations
|
|
231
|
+
clearCache() {
|
|
232
|
+
this.#sizeCache = undefined;
|
|
233
|
+
}
|
|
127
234
|
}
|
package/src/lmdb/set.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { type Database } from 'lmdb';
|
|
2
2
|
|
|
3
3
|
import { type Key, type Range } from '../interfaces/common.js';
|
|
4
|
-
import { type AztecSet } from '../interfaces/set.js';
|
|
4
|
+
import { type AztecAsyncSet, type AztecSet } from '../interfaces/set.js';
|
|
5
5
|
import { LmdbAztecMap } from './map.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* A set backed by LMDB.
|
|
9
9
|
*/
|
|
10
|
-
export class LmdbAztecSet<K extends Key> implements AztecSet<K> {
|
|
10
|
+
export class LmdbAztecSet<K extends Key> implements AztecSet<K>, AztecAsyncSet<K> {
|
|
11
11
|
private map: LmdbAztecMap<K, boolean>;
|
|
12
12
|
constructor(rootDb: Database, mapName: string) {
|
|
13
13
|
this.map = new LmdbAztecMap(rootDb, mapName);
|
|
@@ -21,6 +21,10 @@ export class LmdbAztecSet<K extends Key> implements AztecSet<K> {
|
|
|
21
21
|
return this.map.has(key);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
hasAsync(key: K): Promise<boolean> {
|
|
25
|
+
return Promise.resolve(this.has(key));
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
add(key: K): Promise<void> {
|
|
25
29
|
return this.map.set(key, true);
|
|
26
30
|
}
|
|
@@ -32,4 +36,10 @@ export class LmdbAztecSet<K extends Key> implements AztecSet<K> {
|
|
|
32
36
|
entries(range: Range<K> = {}): IterableIterator<K> {
|
|
33
37
|
return this.map.keys(range);
|
|
34
38
|
}
|
|
39
|
+
|
|
40
|
+
async *entriesAsync(range: Range<K> = {}): AsyncIterableIterator<K> {
|
|
41
|
+
for await (const key of this.map.keysAsync(range)) {
|
|
42
|
+
yield key;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
35
45
|
}
|
package/src/lmdb/singleton.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Database, type Key } from 'lmdb';
|
|
2
2
|
|
|
3
|
-
import { type AztecSingleton } from '../interfaces/singleton.js';
|
|
3
|
+
import { type AztecAsyncSingleton, type AztecSingleton } from '../interfaces/singleton.js';
|
|
4
4
|
|
|
5
5
|
/** The slot where this singleton will store its value */
|
|
6
6
|
type ValueSlot = ['singleton', string, 'value'];
|
|
@@ -8,7 +8,7 @@ type ValueSlot = ['singleton', string, 'value'];
|
|
|
8
8
|
/**
|
|
9
9
|
* Stores a single value in LMDB.
|
|
10
10
|
*/
|
|
11
|
-
export class LmdbAztecSingleton<T> implements AztecSingleton<T> {
|
|
11
|
+
export class LmdbAztecSingleton<T> implements AztecSingleton<T>, AztecAsyncSingleton<T> {
|
|
12
12
|
#db: Database<T, ValueSlot>;
|
|
13
13
|
#slot: ValueSlot;
|
|
14
14
|
|
|
@@ -21,6 +21,10 @@ export class LmdbAztecSingleton<T> implements AztecSingleton<T> {
|
|
|
21
21
|
return this.#db.get(this.#slot);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
getAsync(): Promise<T | undefined> {
|
|
25
|
+
return Promise.resolve(this.get());
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
set(val: T): Promise<boolean> {
|
|
25
29
|
return this.#db.put(this.#slot, val);
|
|
26
30
|
}
|