@aztec/kv-store 0.67.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/store.d.ts.map +1 -1
- package/dest/indexeddb/store.js +4 -3
- package/dest/interfaces/array_test_suite.d.ts.map +1 -1
- package/dest/interfaces/array_test_suite.js +4 -1
- package/dest/interfaces/map.d.ts +18 -0
- package/dest/interfaces/map.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.d.ts.map +1 -1
- package/dest/interfaces/map_test_suite.js +4 -1
- package/dest/interfaces/set_test_suite.d.ts.map +1 -1
- package/dest/interfaces/set_test_suite.js +4 -1
- package/dest/interfaces/singleton_test_suite.d.ts.map +1 -1
- package/dest/interfaces/singleton_test_suite.js +4 -1
- package/dest/interfaces/store.d.ts +13 -1
- package/dest/interfaces/store.d.ts.map +1 -1
- package/dest/interfaces/store_test_suite.d.ts.map +1 -1
- package/dest/interfaces/store_test_suite.js +5 -1
- package/dest/lmdb/map.d.ts +18 -2
- package/dest/lmdb/map.d.ts.map +1 -1
- package/dest/lmdb/map.js +86 -26
- package/dest/lmdb/store.d.ts +15 -3
- package/dest/lmdb/store.d.ts.map +1 -1
- package/dest/lmdb/store.js +26 -10
- package/dest/stores/l2_tips_store.js +2 -2
- package/package.json +4 -4
- package/src/indexeddb/store.ts +3 -2
- package/src/interfaces/array_test_suite.ts +4 -0
- package/src/interfaces/map.ts +21 -0
- package/src/interfaces/map_test_suite.ts +4 -0
- package/src/interfaces/set_test_suite.ts +4 -0
- package/src/interfaces/singleton_test_suite.ts +4 -0
- package/src/interfaces/store.ts +22 -1
- package/src/interfaces/store_test_suite.ts +4 -0
- package/src/lmdb/map.ts +97 -22
- package/src/lmdb/store.ts +35 -12
- package/src/stores/l2_tips_store.ts +1 -1
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 AztecAsyncMultiMap, 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];
|
|
@@ -13,8 +13,8 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, Azte
|
|
|
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>, Azte
|
|
|
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,7 +32,7 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, Azte
|
|
|
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
36
|
}
|
|
37
37
|
|
|
38
38
|
getAsync(key: K): Promise<V | undefined> {
|
|
@@ -40,7 +40,7 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, Azte
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
*getValues(key: K): IterableIterator<V> {
|
|
43
|
-
const values = this.db.getValues(this
|
|
43
|
+
const values = this.db.getValues(this.slot(key));
|
|
44
44
|
for (const value of values) {
|
|
45
45
|
yield value?.[1];
|
|
46
46
|
}
|
|
@@ -53,7 +53,7 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, Azte
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
has(key: K): boolean {
|
|
56
|
-
return this.db.doesExist(this
|
|
56
|
+
return this.db.doesExist(this.slot(key));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
hasAsync(key: K): Promise<boolean> {
|
|
@@ -61,30 +61,30 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, Azte
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
async set(key: K, val: V): Promise<void> {
|
|
64
|
-
await this.db.put(this
|
|
64
|
+
await this.db.put(this.slot(key), [key, val]);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
swap(key: K, fn: (val: V | undefined) => V): Promise<void> {
|
|
68
68
|
return this.db.childTransaction(() => {
|
|
69
|
-
const slot = this
|
|
69
|
+
const slot = this.slot(key);
|
|
70
70
|
const entry = this.db.get(slot);
|
|
71
71
|
void this.db.put(slot, [key, fn(entry?.[1])]);
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
setIfNotExists(key: K, val: V): Promise<boolean> {
|
|
76
|
-
const slot = this
|
|
76
|
+
const slot = this.slot(key);
|
|
77
77
|
return this.db.ifNoExists(slot, () => {
|
|
78
78
|
void this.db.put(slot, [key, val]);
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
async delete(key: K): Promise<void> {
|
|
83
|
-
await this.db.remove(this
|
|
83
|
+
await this.db.remove(this.slot(key));
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
async deleteValue(key: K, val: V): Promise<void> {
|
|
87
|
-
await this.db.remove(this
|
|
87
|
+
await this.db.remove(this.slot(key), [key, val]);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
*entries(range: Range<K> = {}): IterableIterator<[K, V]> {
|
|
@@ -93,19 +93,19 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, Azte
|
|
|
93
93
|
// in that case, we need to swap the start and end sentinels
|
|
94
94
|
const start = reverse
|
|
95
95
|
? range.end
|
|
96
|
-
? this
|
|
97
|
-
: this
|
|
96
|
+
? this.slot(range.end)
|
|
97
|
+
: this.endSentinel
|
|
98
98
|
: range.start
|
|
99
|
-
? this
|
|
100
|
-
: this
|
|
99
|
+
? this.slot(range.start)
|
|
100
|
+
: this.startSentinel;
|
|
101
101
|
|
|
102
102
|
const end = reverse
|
|
103
103
|
? range.start
|
|
104
|
-
? this
|
|
105
|
-
: this
|
|
104
|
+
? this.slot(range.start)
|
|
105
|
+
: this.startSentinel
|
|
106
106
|
: range.end
|
|
107
|
-
? this
|
|
108
|
-
: this
|
|
107
|
+
? this.slot(range.end)
|
|
108
|
+
: this.endSentinel;
|
|
109
109
|
|
|
110
110
|
const lmdbRange: RangeOptions = {
|
|
111
111
|
start,
|
|
@@ -153,7 +153,82 @@ export class LmdbAztecMap<K extends Key, V> implements AztecMultiMap<K, V>, Azte
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
protected slot(key: K): MapValueSlot<K> {
|
|
157
157
|
return ['map', this.name, 'slot', key];
|
|
158
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
|
+
}
|
|
159
234
|
}
|
package/src/lmdb/store.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
|
+
import { randomBytes } from '@aztec/foundation/crypto';
|
|
1
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
3
|
|
|
3
4
|
import { promises as fs, mkdirSync } from 'fs';
|
|
4
5
|
import { type Database, type RootDatabase, open } from 'lmdb';
|
|
5
6
|
import { tmpdir } from 'os';
|
|
6
|
-
import {
|
|
7
|
+
import { join } from 'path';
|
|
7
8
|
|
|
8
9
|
import { type AztecArray, type AztecAsyncArray } from '../interfaces/array.js';
|
|
9
10
|
import { type Key } from '../interfaces/common.js';
|
|
10
11
|
import { type AztecAsyncCounter, type AztecCounter } from '../interfaces/counter.js';
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type AztecAsyncMap,
|
|
14
|
+
type AztecAsyncMultiMap,
|
|
15
|
+
type AztecMap,
|
|
16
|
+
type AztecMapWithSize,
|
|
17
|
+
type AztecMultiMap,
|
|
18
|
+
type AztecMultiMapWithSize,
|
|
19
|
+
} from '../interfaces/map.js';
|
|
12
20
|
import { type AztecAsyncSet, type AztecSet } from '../interfaces/set.js';
|
|
13
21
|
import { type AztecAsyncSingleton, type AztecSingleton } from '../interfaces/singleton.js';
|
|
14
22
|
import { type AztecAsyncKVStore, type AztecKVStore } from '../interfaces/store.js';
|
|
15
23
|
import { LmdbAztecArray } from './array.js';
|
|
16
24
|
import { LmdbAztecCounter } from './counter.js';
|
|
17
|
-
import { LmdbAztecMap } from './map.js';
|
|
25
|
+
import { LmdbAztecMap, LmdbAztecMapWithSize } from './map.js';
|
|
18
26
|
import { LmdbAztecSet } from './set.js';
|
|
19
27
|
import { LmdbAztecSingleton } from './singleton.js';
|
|
20
28
|
|
|
@@ -29,7 +37,7 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
29
37
|
#multiMapData: Database<unknown, Key>;
|
|
30
38
|
#log = createLogger('kv-store:lmdb');
|
|
31
39
|
|
|
32
|
-
constructor(rootDb: RootDatabase, public readonly isEphemeral: boolean, private path
|
|
40
|
+
constructor(rootDb: RootDatabase, public readonly isEphemeral: boolean, private path: string) {
|
|
33
41
|
this.#rootDb = rootDb;
|
|
34
42
|
|
|
35
43
|
// big bucket to store all the data
|
|
@@ -64,13 +72,12 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
64
72
|
ephemeral: boolean = false,
|
|
65
73
|
log = createLogger('kv-store:lmdb'),
|
|
66
74
|
): AztecLmdbStore {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
75
|
+
const dbPath = path ?? join(tmpdir(), randomBytes(8).toString('hex'));
|
|
76
|
+
mkdirSync(dbPath, { recursive: true });
|
|
70
77
|
const mapSize = 1024 * mapSizeKb;
|
|
71
78
|
log.debug(`Opening LMDB database at ${path || 'temporary location'} with map size ${mapSize}`);
|
|
72
|
-
const rootDb = open({ path, noSync: ephemeral, mapSize });
|
|
73
|
-
return new AztecLmdbStore(rootDb, ephemeral,
|
|
79
|
+
const rootDb = open({ path: dbPath, noSync: ephemeral, mapSize });
|
|
80
|
+
return new AztecLmdbStore(rootDb, ephemeral, dbPath);
|
|
74
81
|
}
|
|
75
82
|
|
|
76
83
|
/**
|
|
@@ -78,10 +85,9 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
78
85
|
* @returns A new AztecLmdbStore.
|
|
79
86
|
*/
|
|
80
87
|
async fork() {
|
|
81
|
-
const baseDir = this.path
|
|
88
|
+
const baseDir = this.path;
|
|
82
89
|
this.#log.debug(`Forking store with basedir ${baseDir}`);
|
|
83
|
-
const forkPath =
|
|
84
|
-
(await fs.mkdtemp(join(baseDir, 'aztec-store-fork-'))) + (this.isEphemeral || !this.path ? '/data.mdb' : '');
|
|
90
|
+
const forkPath = await fs.mkdtemp(join(baseDir, 'aztec-store-fork-'));
|
|
85
91
|
this.#log.verbose(`Forking store to ${forkPath}`);
|
|
86
92
|
await this.#rootDb.backup(forkPath, false);
|
|
87
93
|
const forkDb = open(forkPath, { noSync: this.isEphemeral });
|
|
@@ -119,6 +125,23 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
119
125
|
openCounter<K extends Key>(name: string): AztecCounter<K> & AztecAsyncCounter<K> {
|
|
120
126
|
return new LmdbAztecCounter(this.#data, name);
|
|
121
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Creates a new AztecMultiMapWithSize in the store. A multi-map with size stores multiple values for a single key automatically.
|
|
130
|
+
* @param name - Name of the map
|
|
131
|
+
* @returns A new AztecMultiMapWithSize
|
|
132
|
+
*/
|
|
133
|
+
openMultiMapWithSize<K extends Key, V>(name: string): AztecMultiMapWithSize<K, V> {
|
|
134
|
+
return new LmdbAztecMapWithSize(this.#multiMapData, name);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Creates a new AztecMapWithSize in the store.
|
|
139
|
+
* @param name - Name of the map
|
|
140
|
+
* @returns A new AztecMapWithSize
|
|
141
|
+
*/
|
|
142
|
+
openMapWithSize<K extends Key, V>(name: string): AztecMapWithSize<K, V> {
|
|
143
|
+
return new LmdbAztecMapWithSize(this.#data, name);
|
|
144
|
+
}
|
|
122
145
|
|
|
123
146
|
/**
|
|
124
147
|
* Creates a new AztecArray in the store.
|
|
@@ -47,10 +47,10 @@ export class L2TipsStore implements L2BlockStreamEventHandler, L2BlockStreamLoca
|
|
|
47
47
|
public async handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void> {
|
|
48
48
|
switch (event.type) {
|
|
49
49
|
case 'blocks-added':
|
|
50
|
-
await this.l2TipsStore.set('latest', event.blocks.at(-1)!.number);
|
|
51
50
|
for (const block of event.blocks) {
|
|
52
51
|
await this.l2BlockHashesStore.set(block.number, block.header.hash().toString());
|
|
53
52
|
}
|
|
53
|
+
await this.l2TipsStore.set('latest', event.blocks.at(-1)!.number);
|
|
54
54
|
break;
|
|
55
55
|
case 'chain-pruned':
|
|
56
56
|
await this.l2TipsStore.set('latest', event.blockNumber);
|