@aztec/kv-store 0.0.1-commit.e3c1de76 → 0.0.1-commit.e57c76e
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 +8 -1
- 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/indexeddb/array.js +18 -6
- package/dest/indexeddb/index.d.ts +2 -2
- package/dest/indexeddb/index.d.ts.map +1 -1
- package/dest/indexeddb/index.js +1 -1
- 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 +2 -1
- package/dest/indexeddb/multi_map.d.ts.map +1 -1
- package/dest/indexeddb/multi_map.js +16 -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_test_suite.d.ts +1 -1
- package/dest/interfaces/array_test_suite.d.ts.map +1 -1
- package/dest/interfaces/array_test_suite.js +33 -34
- package/dest/interfaces/index.d.ts +2 -2
- package/dest/interfaces/index.d.ts.map +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 +74 -29
- 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 +84 -60
- package/dest/interfaces/set_test_suite.d.ts +1 -1
- package/dest/interfaces/set_test_suite.d.ts.map +1 -1
- package/dest/interfaces/set_test_suite.js +13 -14
- package/dest/interfaces/singleton_test_suite.d.ts +1 -1
- package/dest/interfaces/singleton_test_suite.d.ts.map +1 -1
- package/dest/interfaces/singleton_test_suite.js +6 -7
- package/dest/lmdb/index.d.ts +2 -2
- package/dest/lmdb/index.d.ts.map +1 -1
- package/dest/lmdb/index.js +2 -2
- package/dest/lmdb/store.d.ts +3 -3
- package/dest/lmdb/store.d.ts.map +1 -1
- package/dest/lmdb/store.js +12 -8
- package/dest/lmdb-v2/factory.d.ts +29 -4
- package/dest/lmdb-v2/factory.d.ts.map +1 -1
- package/dest/lmdb-v2/factory.js +61 -10
- package/dest/lmdb-v2/read_transaction.js +21 -19
- package/dest/lmdb-v2/store.d.ts +2 -2
- package/dest/lmdb-v2/store.d.ts.map +1 -1
- package/dest/lmdb-v2/store.js +4 -4
- 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 +27 -0
- package/dest/sqlite-opfs/errors.d.ts.map +1 -0
- package/dest/sqlite-opfs/errors.js +34 -0
- package/dest/sqlite-opfs/index.d.ts +16 -0
- package/dest/sqlite-opfs/index.d.ts.map +1 -0
- package/dest/sqlite-opfs/index.js +22 -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/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/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 +79 -0
- package/dest/sqlite-opfs/store.d.ts.map +1 -0
- package/dest/sqlite-opfs/store.js +281 -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 +249 -0
- package/dest/stores/l2_tips_store.d.ts +3 -3
- package/dest/stores/l2_tips_store.d.ts.map +1 -1
- package/dest/stores/l2_tips_store.js +2 -2
- package/dest/utils.d.ts +1 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +7 -1
- package/package.json +29 -25
- package/src/bench/shared_map_bench.ts +111 -0
- package/src/indexeddb/array.ts +4 -4
- package/src/indexeddb/index.ts +2 -2
- package/src/indexeddb/map.ts +24 -11
- package/src/indexeddb/multi_map.ts +15 -1
- package/src/indexeddb/singleton.ts +1 -1
- package/src/indexeddb/store.ts +13 -6
- package/src/interfaces/array_test_suite.ts +33 -35
- package/src/interfaces/index.ts +1 -1
- package/src/interfaces/map_test_suite.ts +56 -30
- package/src/interfaces/multi_map_test_suite.ts +88 -58
- package/src/interfaces/set_test_suite.ts +13 -15
- package/src/interfaces/singleton_test_suite.ts +6 -8
- package/src/lmdb/index.ts +3 -3
- package/src/lmdb/store.ts +12 -8
- package/src/lmdb-v2/factory.ts +80 -10
- package/src/lmdb-v2/read_transaction.ts +23 -23
- package/src/lmdb-v2/store.ts +4 -2
- package/src/sqlite-opfs/array.ts +124 -0
- package/src/sqlite-opfs/errors.ts +44 -0
- package/src/sqlite-opfs/index.ts +39 -0
- package/src/sqlite-opfs/internal/ordered-binary-browser.js +465 -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/set.ts +29 -0
- package/src/sqlite-opfs/singleton.ts +48 -0
- package/src/sqlite-opfs/store.ts +296 -0
- package/src/sqlite-opfs/worker.ts +231 -0
- package/src/stores/l2_tips_store.ts +3 -2
- package/src/utils.ts +10 -0
- package/dest/config.d.ts +0 -17
- package/dest/config.d.ts.map +0 -1
- package/dest/config.js +0 -26
- package/src/config.ts +0 -36
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { toArray } from '@aztec/foundation/iterable';
|
|
2
2
|
|
|
3
|
-
import { expect } from 'chai';
|
|
4
|
-
|
|
5
3
|
import type { Key, Range } from './common.js';
|
|
6
4
|
import type { AztecAsyncMultiMap, AztecMultiMap } from './multi_map.js';
|
|
7
5
|
import type { AztecAsyncKVStore, AztecKVStore } from './store.js';
|
|
@@ -72,16 +70,16 @@ export function describeAztecMultiMap(
|
|
|
72
70
|
await multiMap.set('foo', 'bar');
|
|
73
71
|
await multiMap.set('baz', 'qux');
|
|
74
72
|
|
|
75
|
-
expect(await get('foo')).
|
|
76
|
-
expect(await get('baz')).
|
|
77
|
-
expect(await get('quux')).
|
|
73
|
+
expect(await get('foo')).toBe('bar');
|
|
74
|
+
expect(await get('baz')).toBe('qux');
|
|
75
|
+
expect(await get('quux')).toBe(undefined);
|
|
78
76
|
});
|
|
79
77
|
|
|
80
78
|
it('should be able to set values if they do not exist', async () => {
|
|
81
|
-
expect(await multiMap.setIfNotExists('foo', 'bar')).
|
|
82
|
-
expect(await multiMap.setIfNotExists('foo', 'baz')).
|
|
79
|
+
expect(await multiMap.setIfNotExists('foo', 'bar')).toBe(true);
|
|
80
|
+
expect(await multiMap.setIfNotExists('foo', 'baz')).toBe(false);
|
|
83
81
|
|
|
84
|
-
expect(await get('foo')).
|
|
82
|
+
expect(await get('foo')).toBe('bar');
|
|
85
83
|
});
|
|
86
84
|
|
|
87
85
|
it('should be able to delete values', async () => {
|
|
@@ -90,29 +88,61 @@ export function describeAztecMultiMap(
|
|
|
90
88
|
|
|
91
89
|
await multiMap.delete('foo');
|
|
92
90
|
|
|
93
|
-
expect(await get('foo')).
|
|
94
|
-
expect(await get('baz')).
|
|
91
|
+
expect(await get('foo')).toBe(undefined);
|
|
92
|
+
expect(await get('baz')).toBe('qux');
|
|
95
93
|
});
|
|
96
94
|
|
|
97
95
|
it('should be able to get size of the map', async () => {
|
|
98
96
|
await multiMap.set('foo', 'bar');
|
|
99
|
-
expect(await size()).
|
|
97
|
+
expect(await size()).toBe(1);
|
|
100
98
|
await multiMap.set('baz', 'qux');
|
|
101
|
-
expect(await size()).
|
|
99
|
+
expect(await size()).toBe(2);
|
|
102
100
|
|
|
103
101
|
await multiMap.delete('foo');
|
|
104
|
-
expect(await size()).
|
|
102
|
+
expect(await size()).toBe(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('returns 0 for empty multimap size', async () => {
|
|
106
|
+
expect(await size()).toBe(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('calculates size correctly with multiple values per key', async () => {
|
|
110
|
+
expect(await size()).toBe(0);
|
|
111
|
+
|
|
112
|
+
// Add multiple values for same key
|
|
113
|
+
await multiMap.set('key1', 'value1');
|
|
114
|
+
expect(await size()).toBe(1);
|
|
115
|
+
await multiMap.set('key1', 'value2');
|
|
116
|
+
expect(await size()).toBe(2);
|
|
117
|
+
await multiMap.set('key1', 'value3');
|
|
118
|
+
expect(await size()).toBe(3);
|
|
119
|
+
|
|
120
|
+
// Add values for different key
|
|
121
|
+
await multiMap.set('key2', 'value4');
|
|
122
|
+
expect(await size()).toBe(4);
|
|
123
|
+
|
|
124
|
+
// Delete one value from key1
|
|
125
|
+
await multiMap.deleteValue('key1', 'value2');
|
|
126
|
+
expect(await size()).toBe(3);
|
|
127
|
+
|
|
128
|
+
// Delete entire key
|
|
129
|
+
await multiMap.delete('key1');
|
|
130
|
+
expect(await size()).toBe(1);
|
|
131
|
+
|
|
132
|
+
// Delete last key
|
|
133
|
+
await multiMap.delete('key2');
|
|
134
|
+
expect(await size()).toBe(0);
|
|
105
135
|
});
|
|
106
136
|
|
|
107
137
|
it('should be able to iterate over entries when there are no keys', async () => {
|
|
108
|
-
expect(await entries()).
|
|
138
|
+
expect(await entries()).toEqual([]);
|
|
109
139
|
});
|
|
110
140
|
|
|
111
141
|
it('should be able to iterate over entries', async () => {
|
|
112
142
|
await multiMap.set('foo', 'bar');
|
|
113
143
|
await multiMap.set('baz', 'qux');
|
|
114
144
|
|
|
115
|
-
expect(await entries()).
|
|
145
|
+
expect(await entries()).toEqual([
|
|
116
146
|
['baz', 'qux'],
|
|
117
147
|
['foo', 'bar'],
|
|
118
148
|
]);
|
|
@@ -122,28 +152,28 @@ export function describeAztecMultiMap(
|
|
|
122
152
|
await multiMap.set('foo', 'bar');
|
|
123
153
|
await multiMap.set('baz', 'quux');
|
|
124
154
|
|
|
125
|
-
expect(await values()).
|
|
155
|
+
expect(await values()).toEqual(['quux', 'bar']);
|
|
126
156
|
});
|
|
127
157
|
|
|
128
158
|
it('should be able to iterate over keys', async () => {
|
|
129
159
|
await multiMap.set('foo', 'bar');
|
|
130
160
|
await multiMap.set('baz', 'qux');
|
|
131
161
|
|
|
132
|
-
expect(await keys()).
|
|
162
|
+
expect(await keys()).toEqual(['baz', 'foo']);
|
|
133
163
|
});
|
|
134
164
|
|
|
135
165
|
it('should be able to get multiple values for a single key', async () => {
|
|
136
166
|
await multiMap.set('foo', 'bar');
|
|
137
167
|
await multiMap.set('foo', 'baz');
|
|
138
168
|
|
|
139
|
-
expect(await getValues('foo')).
|
|
169
|
+
expect(await getValues('foo')).toEqual(['bar', 'baz']);
|
|
140
170
|
});
|
|
141
171
|
|
|
142
172
|
it('should ignore multiple identical values', async () => {
|
|
143
173
|
await multiMap.set('foo', 'bar');
|
|
144
174
|
await multiMap.set('foo', 'bar');
|
|
145
175
|
|
|
146
|
-
expect(await getValues('foo')).
|
|
176
|
+
expect(await getValues('foo')).toEqual(['bar']);
|
|
147
177
|
});
|
|
148
178
|
|
|
149
179
|
it('should be able to delete individual values for a single key', async () => {
|
|
@@ -153,22 +183,22 @@ export function describeAztecMultiMap(
|
|
|
153
183
|
|
|
154
184
|
await multiMap.deleteValue('foo', '2');
|
|
155
185
|
|
|
156
|
-
expect(await getValues('foo')).
|
|
186
|
+
expect(await getValues('foo')).toEqual(['1', '3']);
|
|
157
187
|
});
|
|
158
188
|
|
|
159
189
|
it('should be able to get size of the map with duplicate keys', async () => {
|
|
160
190
|
await multiMap.set('foo', '1');
|
|
161
191
|
await multiMap.set('foo', '2');
|
|
162
192
|
await multiMap.set('foo', '3');
|
|
163
|
-
expect(await size()).
|
|
193
|
+
expect(await size()).toBe(3);
|
|
164
194
|
|
|
165
195
|
await multiMap.set('bar', '1');
|
|
166
196
|
await multiMap.set('bar', '2');
|
|
167
197
|
await multiMap.set('bar', '3');
|
|
168
|
-
expect(await size()).
|
|
198
|
+
expect(await size()).toBe(6);
|
|
169
199
|
|
|
170
200
|
await multiMap.deleteValue('foo', '2');
|
|
171
|
-
expect(await size()).
|
|
201
|
+
expect(await size()).toBe(5);
|
|
172
202
|
});
|
|
173
203
|
|
|
174
204
|
it('should be able to delete the last and first values for a key', async () => {
|
|
@@ -178,11 +208,11 @@ export function describeAztecMultiMap(
|
|
|
178
208
|
|
|
179
209
|
await multiMap.deleteValue('foo', '1');
|
|
180
210
|
|
|
181
|
-
expect(await getValues('foo')).
|
|
211
|
+
expect(await getValues('foo')).toEqual(['2', '3']);
|
|
182
212
|
|
|
183
213
|
await multiMap.deleteValue('foo', '3');
|
|
184
214
|
|
|
185
|
-
expect(await getValues('foo')).
|
|
215
|
+
expect(await getValues('foo')).toEqual(['2']);
|
|
186
216
|
});
|
|
187
217
|
|
|
188
218
|
it('should be able to fully clear a key', async () => {
|
|
@@ -194,7 +224,7 @@ export function describeAztecMultiMap(
|
|
|
194
224
|
await multiMap.deleteValue('foo', '3');
|
|
195
225
|
await multiMap.deleteValue('foo', '2');
|
|
196
226
|
|
|
197
|
-
expect(await getValues('foo')).
|
|
227
|
+
expect(await getValues('foo')).toEqual([]);
|
|
198
228
|
});
|
|
199
229
|
|
|
200
230
|
it('should be able to insert after deletion', async () => {
|
|
@@ -205,12 +235,12 @@ export function describeAztecMultiMap(
|
|
|
205
235
|
await multiMap.deleteValue('foo', '2');
|
|
206
236
|
await multiMap.set('foo', 'bar');
|
|
207
237
|
|
|
208
|
-
expect(await getValues('foo')).
|
|
238
|
+
expect(await getValues('foo')).toEqual(['1', '3', 'bar']);
|
|
209
239
|
|
|
210
240
|
// Delete the just-added entry
|
|
211
241
|
await multiMap.deleteValue('foo', 'bar');
|
|
212
242
|
|
|
213
|
-
expect(await getValues('foo')).
|
|
243
|
+
expect(await getValues('foo')).toEqual(['1', '3']);
|
|
214
244
|
|
|
215
245
|
// Reinsert the initially deleted key
|
|
216
246
|
await multiMap.set('foo', '2');
|
|
@@ -218,7 +248,7 @@ export function describeAztecMultiMap(
|
|
|
218
248
|
// LMDB and IndexedDB behave differently here, the former ordering by value and the latter by insertion. This is
|
|
219
249
|
// fine because there is no expectation for values in a multimap to be ordered.
|
|
220
250
|
const values = (await getValues('foo')).sort((a, b) => a.localeCompare(b));
|
|
221
|
-
expect(values).
|
|
251
|
+
expect(values).toEqual(['1', '2', '3']);
|
|
222
252
|
|
|
223
253
|
// Fully clear the key
|
|
224
254
|
await multiMap.deleteValue('foo', '1');
|
|
@@ -228,7 +258,7 @@ export function describeAztecMultiMap(
|
|
|
228
258
|
// Insert some more
|
|
229
259
|
await multiMap.set('foo', 'baz');
|
|
230
260
|
await multiMap.set('foo', 'qux');
|
|
231
|
-
expect(await getValues('foo')).
|
|
261
|
+
expect(await getValues('foo')).toEqual(['baz', 'qux']);
|
|
232
262
|
});
|
|
233
263
|
|
|
234
264
|
it('supports range queries', async () => {
|
|
@@ -237,29 +267,29 @@ export function describeAztecMultiMap(
|
|
|
237
267
|
await multiMap.set('c', 'c');
|
|
238
268
|
await multiMap.set('d', 'd');
|
|
239
269
|
|
|
240
|
-
expect(await keys({ start: 'b', end: 'c' })).
|
|
241
|
-
expect(await keys({ start: 'b' })).
|
|
242
|
-
expect(await keys({ end: 'c' })).
|
|
243
|
-
expect(await keys({ start: 'b', end: 'c', reverse: true })).
|
|
244
|
-
expect(await keys({ start: 'b', limit: 1 })).
|
|
245
|
-
expect(await keys({ start: 'b', reverse: true })).
|
|
246
|
-
expect(await keys({ end: 'b', reverse: true })).
|
|
270
|
+
expect(await keys({ start: 'b', end: 'c' })).toEqual(['b']);
|
|
271
|
+
expect(await keys({ start: 'b' })).toEqual(['b', 'c', 'd']);
|
|
272
|
+
expect(await keys({ end: 'c' })).toEqual(['a', 'b']);
|
|
273
|
+
expect(await keys({ start: 'b', end: 'c', reverse: true })).toEqual(['c']);
|
|
274
|
+
expect(await keys({ start: 'b', limit: 1 })).toEqual(['b']);
|
|
275
|
+
expect(await keys({ start: 'b', reverse: true })).toEqual(['d', 'c']);
|
|
276
|
+
expect(await keys({ end: 'b', reverse: true })).toEqual(['b', 'a']);
|
|
247
277
|
});
|
|
248
278
|
|
|
249
279
|
it('returns 0 for missing key', async () => {
|
|
250
|
-
expect(await getValueCount('missing')).
|
|
280
|
+
expect(await getValueCount('missing')).toBe(0);
|
|
251
281
|
});
|
|
252
282
|
|
|
253
283
|
it('counts a single value', async () => {
|
|
254
284
|
await multiMap.set('foo', 'bar');
|
|
255
|
-
expect(await getValueCount('foo')).
|
|
285
|
+
expect(await getValueCount('foo')).toBe(1);
|
|
256
286
|
});
|
|
257
287
|
|
|
258
288
|
it('counts multiple distinct values for same key', async () => {
|
|
259
289
|
await multiMap.set('foo', 'bar');
|
|
260
290
|
await multiMap.set('foo', 'baz');
|
|
261
291
|
await multiMap.set('foo', 'qux');
|
|
262
|
-
expect(await getValueCount('foo')).
|
|
292
|
+
expect(await getValueCount('foo')).toBe(3);
|
|
263
293
|
});
|
|
264
294
|
|
|
265
295
|
it('does not increase count for duplicate inserts', async () => {
|
|
@@ -267,42 +297,42 @@ export function describeAztecMultiMap(
|
|
|
267
297
|
await multiMap.set('foo', 'bar');
|
|
268
298
|
await multiMap.set('foo', 'baz');
|
|
269
299
|
await multiMap.set('foo', 'baz');
|
|
270
|
-
expect(await getValueCount('foo')).
|
|
271
|
-
expect(await getValues('foo')).
|
|
300
|
+
expect(await getValueCount('foo')).toBe(2);
|
|
301
|
+
expect((await getValues('foo')).sort()).toEqual(['bar', 'baz'].sort());
|
|
272
302
|
});
|
|
273
303
|
|
|
274
304
|
it('decrements when deleting a single value', async () => {
|
|
275
305
|
await multiMap.set('foo', '1');
|
|
276
306
|
await multiMap.set('foo', '2');
|
|
277
307
|
await multiMap.set('foo', '3');
|
|
278
|
-
expect(await getValueCount('foo')).
|
|
308
|
+
expect(await getValueCount('foo')).toBe(3);
|
|
279
309
|
await multiMap.deleteValue('foo', '2');
|
|
280
|
-
expect(await getValueCount('foo')).
|
|
281
|
-
expect(await getValues('foo')).
|
|
310
|
+
expect(await getValueCount('foo')).toBe(2);
|
|
311
|
+
expect((await getValues('foo')).sort()).toEqual(['1', '3'].sort());
|
|
282
312
|
});
|
|
283
313
|
|
|
284
314
|
it('does not change count when deleting a non-existent value', async () => {
|
|
285
315
|
await multiMap.set('foo', '1');
|
|
286
316
|
await multiMap.set('foo', '3');
|
|
287
|
-
expect(await getValueCount('foo')).
|
|
317
|
+
expect(await getValueCount('foo')).toBe(2);
|
|
288
318
|
await multiMap.deleteValue('foo', '2');
|
|
289
|
-
expect(await getValueCount('foo')).
|
|
319
|
+
expect(await getValueCount('foo')).toBe(2);
|
|
290
320
|
});
|
|
291
321
|
|
|
292
322
|
it('clears all values when deleting a key', async () => {
|
|
293
323
|
await multiMap.set('foo', 'bar');
|
|
294
324
|
await multiMap.set('foo', 'baz');
|
|
295
|
-
expect(await getValueCount('foo')).
|
|
325
|
+
expect(await getValueCount('foo')).toBe(2);
|
|
296
326
|
await multiMap.delete('foo');
|
|
297
|
-
expect(await getValueCount('foo')).
|
|
298
|
-
expect(await getValues('foo')).
|
|
327
|
+
expect(await getValueCount('foo')).toBe(0);
|
|
328
|
+
expect(await getValues('foo')).toEqual([]);
|
|
299
329
|
});
|
|
300
330
|
|
|
301
331
|
it('count equals enumerated values length', async () => {
|
|
302
332
|
await multiMap.set('foo', '1');
|
|
303
333
|
await multiMap.set('foo', '2');
|
|
304
334
|
const vals = await getValues('foo');
|
|
305
|
-
expect(await getValueCount('foo')).
|
|
335
|
+
expect(await getValueCount('foo')).toBe(vals.length);
|
|
306
336
|
});
|
|
307
337
|
|
|
308
338
|
it('sum of per-key counts equals total size', async () => {
|
|
@@ -316,27 +346,27 @@ export function describeAztecMultiMap(
|
|
|
316
346
|
const uniqueKeys = Array.from(new Set(allKeys));
|
|
317
347
|
const counts = await Promise.all(uniqueKeys.map(k => getValueCount(k)));
|
|
318
348
|
const sum = counts.reduce((s, n) => s + n, 0);
|
|
319
|
-
expect(sum).
|
|
349
|
+
expect(sum).toBe(await size());
|
|
320
350
|
});
|
|
321
351
|
|
|
322
352
|
it('supports sparse slots: delete middle, reinsert new, count remains correct', async () => {
|
|
323
353
|
await multiMap.set('foo', '1');
|
|
324
354
|
await multiMap.set('foo', '2');
|
|
325
355
|
await multiMap.set('foo', '3');
|
|
326
|
-
expect(await getValueCount('foo')).
|
|
356
|
+
expect(await getValueCount('foo')).toBe(3);
|
|
327
357
|
await multiMap.deleteValue('foo', '2');
|
|
328
|
-
expect(await getValueCount('foo')).
|
|
358
|
+
expect(await getValueCount('foo')).toBe(2);
|
|
329
359
|
await multiMap.set('foo', '4');
|
|
330
|
-
expect(await getValueCount('foo')).
|
|
331
|
-
expect(await getValues('foo')).
|
|
360
|
+
expect(await getValueCount('foo')).toBe(3);
|
|
361
|
+
expect((await getValues('foo')).sort()).toEqual(['1', '3', '4'].sort());
|
|
332
362
|
});
|
|
333
363
|
|
|
334
364
|
it('multiple keys are independent', async () => {
|
|
335
365
|
await multiMap.set('foo', '1');
|
|
336
366
|
await multiMap.set('foo', '2');
|
|
337
367
|
await multiMap.set('bar', '3');
|
|
338
|
-
expect(await getValueCount('foo')).
|
|
339
|
-
expect(await getValueCount('bar')).
|
|
368
|
+
expect(await getValueCount('foo')).toBe(2);
|
|
369
|
+
expect(await getValueCount('bar')).toBe(1);
|
|
340
370
|
});
|
|
341
371
|
});
|
|
342
372
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { toArray } from '@aztec/foundation/iterable';
|
|
2
2
|
|
|
3
|
-
import { expect } from 'chai';
|
|
4
|
-
|
|
5
3
|
import type { Range } from './common.js';
|
|
6
4
|
import type { AztecAsyncSet, AztecSet } from './set.js';
|
|
7
5
|
import type { AztecAsyncKVStore, AztecKVStore } from './store.js';
|
|
@@ -41,9 +39,9 @@ export function describeAztecSet(
|
|
|
41
39
|
await set.add('foo');
|
|
42
40
|
await set.add('baz');
|
|
43
41
|
|
|
44
|
-
expect(await has('foo')).
|
|
45
|
-
expect(await has('baz')).
|
|
46
|
-
expect(await has('bar')).
|
|
42
|
+
expect(await has('foo')).toBe(true);
|
|
43
|
+
expect(await has('baz')).toBe(true);
|
|
44
|
+
expect(await has('bar')).toBe(false);
|
|
47
45
|
});
|
|
48
46
|
|
|
49
47
|
it('should be able to delete values', async () => {
|
|
@@ -52,15 +50,15 @@ export function describeAztecSet(
|
|
|
52
50
|
|
|
53
51
|
await set.delete('foo');
|
|
54
52
|
|
|
55
|
-
expect(await has('foo')).
|
|
56
|
-
expect(await has('baz')).
|
|
53
|
+
expect(await has('foo')).toBe(false);
|
|
54
|
+
expect(await has('baz')).toBe(true);
|
|
57
55
|
});
|
|
58
56
|
|
|
59
57
|
it('should be able to iterate over entries', async () => {
|
|
60
58
|
await set.add('baz');
|
|
61
59
|
await set.add('foo');
|
|
62
60
|
|
|
63
|
-
expect(await entries()).
|
|
61
|
+
expect(await entries()).toEqual(['baz', 'foo']);
|
|
64
62
|
});
|
|
65
63
|
|
|
66
64
|
it('supports range queries', async () => {
|
|
@@ -69,13 +67,13 @@ export function describeAztecSet(
|
|
|
69
67
|
await set.add('c');
|
|
70
68
|
await set.add('d');
|
|
71
69
|
|
|
72
|
-
expect(await entries({ start: 'b', end: 'c' })).
|
|
73
|
-
expect(await entries({ start: 'b' })).
|
|
74
|
-
expect(await entries({ end: 'c' })).
|
|
75
|
-
expect(await entries({ start: 'b', end: 'c', reverse: true })).
|
|
76
|
-
expect(await entries({ start: 'b', limit: 1 })).
|
|
77
|
-
expect(await entries({ start: 'b', reverse: true })).
|
|
78
|
-
expect(await entries({ end: 'b', reverse: true })).
|
|
70
|
+
expect(await entries({ start: 'b', end: 'c' })).toEqual(['b']);
|
|
71
|
+
expect(await entries({ start: 'b' })).toEqual(['b', 'c', 'd']);
|
|
72
|
+
expect(await entries({ end: 'c' })).toEqual(['a', 'b']);
|
|
73
|
+
expect(await entries({ start: 'b', end: 'c', reverse: true })).toEqual(['c']);
|
|
74
|
+
expect(await entries({ start: 'b', limit: 1 })).toEqual(['b']);
|
|
75
|
+
expect(await entries({ start: 'b', reverse: true })).toEqual(['d', 'c']);
|
|
76
|
+
expect(await entries({ end: 'b', reverse: true })).toEqual(['b', 'a']);
|
|
79
77
|
});
|
|
80
78
|
});
|
|
81
79
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { expect } from 'chai';
|
|
2
|
-
|
|
3
1
|
import type { AztecAsyncSingleton, AztecSingleton } from './singleton.js';
|
|
4
2
|
import type { AztecAsyncKVStore, AztecKVStore } from './store.js';
|
|
5
3
|
import { isSyncStore } from './utils.js';
|
|
@@ -29,18 +27,18 @@ export function describeAztecSingleton(
|
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
it('returns undefined if the value is not set', async () => {
|
|
32
|
-
expect(await get()).
|
|
30
|
+
expect(await get()).toBe(undefined);
|
|
33
31
|
});
|
|
34
32
|
|
|
35
33
|
it('should be able to set and get values', async () => {
|
|
36
|
-
expect(await singleton.set('foo')).
|
|
37
|
-
expect(await get()).
|
|
34
|
+
expect(await singleton.set('foo')).toBe(true);
|
|
35
|
+
expect(await get()).toBe('foo');
|
|
38
36
|
});
|
|
39
37
|
|
|
40
38
|
it('overwrites the value if it is set again', async () => {
|
|
41
|
-
expect(await singleton.set('foo')).
|
|
42
|
-
expect(await singleton.set('bar')).
|
|
43
|
-
expect(await get()).
|
|
39
|
+
expect(await singleton.set('foo')).toBe(true);
|
|
40
|
+
expect(await singleton.set('bar')).toBe(true);
|
|
41
|
+
expect(await get()).toBe('bar');
|
|
44
42
|
});
|
|
45
43
|
});
|
|
46
44
|
}
|
package/src/lmdb/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
2
3
|
|
|
3
4
|
import { join } from 'path';
|
|
4
5
|
|
|
5
|
-
import type { DataStoreConfig } from '../config.js';
|
|
6
6
|
import { initStoreForRollupAndSchemaVersion } from '../utils.js';
|
|
7
7
|
import { AztecLmdbStore } from './store.js';
|
|
8
8
|
|
|
@@ -26,8 +26,8 @@ export function createStore(
|
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
const store = AztecLmdbStore.open(dataDirectory, config.dataStoreMapSizeKb, false);
|
|
29
|
-
if (config.
|
|
30
|
-
return initStoreForRollupAndSchemaVersion(store, schemaVersion, config.
|
|
29
|
+
if (config.rollupAddress) {
|
|
30
|
+
return initStoreForRollupAndSchemaVersion(store, schemaVersion, config.rollupAddress, log);
|
|
31
31
|
}
|
|
32
32
|
return store;
|
|
33
33
|
}
|
package/src/lmdb/store.ts
CHANGED
|
@@ -147,21 +147,25 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
|
-
* Clears all entries in the store & sub DBs.
|
|
150
|
+
* Clears all entries in the store & sub DBs atomically within a single transaction.
|
|
151
151
|
*/
|
|
152
152
|
async clear() {
|
|
153
|
-
await this.#
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
await this.#rootDb.transaction(async () => {
|
|
154
|
+
await this.#data.clearAsync();
|
|
155
|
+
await this.#multiMapData.clearAsync();
|
|
156
|
+
await this.#rootDb.clearAsync();
|
|
157
|
+
});
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
/**
|
|
159
|
-
* Drops the database & sub DBs.
|
|
161
|
+
* Drops the database & sub DBs atomically within a single transaction.
|
|
160
162
|
*/
|
|
161
163
|
async drop() {
|
|
162
|
-
await this.#
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
await this.#rootDb.transaction(async () => {
|
|
165
|
+
await this.#data.drop();
|
|
166
|
+
await this.#multiMapData.drop();
|
|
167
|
+
await this.#rootDb.drop();
|
|
168
|
+
});
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
/**
|
package/src/lmdb-v2/factory.ts
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
2
|
import { type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
3
|
-
import { DatabaseVersionManager } from '@aztec/stdlib/database-version/manager';
|
|
3
|
+
import { DatabaseVersionManager, type SchemaVersionMismatchPolicy } from '@aztec/stdlib/database-version/manager';
|
|
4
|
+
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
4
5
|
|
|
5
|
-
import { mkdir, mkdtemp, rm } from 'fs/promises';
|
|
6
|
+
import { copyFile, mkdir, mkdtemp, rm } from 'fs/promises';
|
|
6
7
|
import { tmpdir } from 'os';
|
|
7
8
|
import { join } from 'path';
|
|
8
9
|
|
|
9
|
-
import type { DataStoreConfig } from '../config.js';
|
|
10
10
|
import { AztecLMDBStoreV2 } from './store.js';
|
|
11
11
|
|
|
12
12
|
const MAX_READERS = 16;
|
|
13
13
|
|
|
14
|
+
/** Optional versioning hooks for persistent LMDB stores. */
|
|
15
|
+
export type CreateStoreOptions = {
|
|
16
|
+
onUpgrade?: (dataDir: string, currentVersion: number, latestVersion: number) => Promise<void>;
|
|
17
|
+
schemaVersionMismatchPolicy?: SchemaVersionMismatchPolicy;
|
|
18
|
+
};
|
|
19
|
+
|
|
14
20
|
export async function createStore(
|
|
15
21
|
name: string,
|
|
16
22
|
schemaVersion: number,
|
|
17
23
|
config: DataStoreConfig,
|
|
18
24
|
bindings?: LoggerBindings,
|
|
25
|
+
options: CreateStoreOptions = {},
|
|
19
26
|
): Promise<AztecLMDBStoreV2> {
|
|
20
27
|
const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
|
|
21
|
-
const { dataDirectory,
|
|
28
|
+
const { dataDirectory, rollupAddress: rollupFromConfig } = config;
|
|
22
29
|
|
|
23
30
|
let store: AztecLMDBStoreV2;
|
|
24
31
|
if (typeof dataDirectory !== 'undefined') {
|
|
@@ -26,7 +33,7 @@ export async function createStore(
|
|
|
26
33
|
const subDir = join(dataDirectory, name);
|
|
27
34
|
await mkdir(subDir, { recursive: true });
|
|
28
35
|
|
|
29
|
-
const rollupAddress =
|
|
36
|
+
const rollupAddress = rollupFromConfig ?? EthAddress.ZERO;
|
|
30
37
|
|
|
31
38
|
// Create a version manager
|
|
32
39
|
const versionManager = new DatabaseVersionManager({
|
|
@@ -35,6 +42,8 @@ export async function createStore(
|
|
|
35
42
|
dataDirectory: subDir,
|
|
36
43
|
onOpen: dbDirectory =>
|
|
37
44
|
AztecLMDBStoreV2.new(dbDirectory, config.dataStoreMapSizeKb, MAX_READERS, () => Promise.resolve(), bindings),
|
|
45
|
+
onUpgrade: options.onUpgrade,
|
|
46
|
+
schemaVersionMismatchPolicy: options.schemaVersionMismatchPolicy,
|
|
38
47
|
});
|
|
39
48
|
|
|
40
49
|
log.info(
|
|
@@ -48,9 +57,14 @@ export async function createStore(
|
|
|
48
57
|
return store;
|
|
49
58
|
}
|
|
50
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Open a persistent on-disk store rooted in OS tmpdir. Caller chooses whether to
|
|
62
|
+
* auto-remove the directory on close. Uses standard durable LMDB flags (fsync on
|
|
63
|
+
* every commit) — for full in-memory / NOSYNC semantics use `openEphemeralStore`.
|
|
64
|
+
*/
|
|
51
65
|
export async function openTmpStore(
|
|
52
66
|
name: string,
|
|
53
|
-
|
|
67
|
+
cleanupTmpDir: boolean = true,
|
|
54
68
|
dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
|
|
55
69
|
maxReaders = MAX_READERS,
|
|
56
70
|
bindings?: LoggerBindings,
|
|
@@ -61,7 +75,7 @@ export async function openTmpStore(
|
|
|
61
75
|
|
|
62
76
|
// pass a cleanup callback because process.on('beforeExit', cleanup) does not work under Jest
|
|
63
77
|
const cleanup = async () => {
|
|
64
|
-
if (
|
|
78
|
+
if (cleanupTmpDir) {
|
|
65
79
|
try {
|
|
66
80
|
await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
|
|
67
81
|
log.debug(`Deleted temporary data store: ${dataDir}`);
|
|
@@ -73,9 +87,35 @@ export async function openTmpStore(
|
|
|
73
87
|
}
|
|
74
88
|
};
|
|
75
89
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
90
|
+
return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, bindings, false);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Open a fully-ephemeral store: a fresh tmpdir backing file, LMDB opened with
|
|
95
|
+
* MDB_NOSYNC | MDB_NOMETASYNC (no fsync, kernel-only writeback), and the directory
|
|
96
|
+
* unconditionally removed on close. Intended for tests and worker-local scratch
|
|
97
|
+
* stores where durability is explicitly not desired.
|
|
98
|
+
*/
|
|
99
|
+
export async function openEphemeralStore(
|
|
100
|
+
name: string,
|
|
101
|
+
dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
|
|
102
|
+
maxReaders = MAX_READERS,
|
|
103
|
+
bindings?: LoggerBindings,
|
|
104
|
+
): Promise<AztecLMDBStoreV2> {
|
|
105
|
+
const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
|
|
106
|
+
const dataDir = await mkdtemp(join(tmpdir(), name + '-'));
|
|
107
|
+
log.debug(`Created ephemeral data store at: ${dataDir} with size: ${dbMapSizeKb} KB (LMDB v2)`);
|
|
108
|
+
|
|
109
|
+
const cleanup = async () => {
|
|
110
|
+
try {
|
|
111
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
|
|
112
|
+
log.debug(`Deleted ephemeral data store: ${dataDir}`);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
log.warn(`Failed to delete ephemeral data directory (LMDB v2) ${dataDir}: ${err}`);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, bindings, true);
|
|
79
119
|
}
|
|
80
120
|
|
|
81
121
|
export async function openStoreAt(
|
|
@@ -89,6 +129,36 @@ export async function openStoreAt(
|
|
|
89
129
|
return await AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, undefined, bindings);
|
|
90
130
|
}
|
|
91
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Open a fully-ephemeral store seeded from an existing `data.mdb` file. Creates a fresh tmpdir,
|
|
134
|
+
* copies `srcDataMdbPath` into it as `data.mdb`, opens with MDB_NOSYNC | MDB_NOMETASYNC, and
|
|
135
|
+
* removes the tmpdir on close. Intended for worker clones of a main-thread-built store — the
|
|
136
|
+
* source file is never written to.
|
|
137
|
+
*/
|
|
138
|
+
export async function cloneEphemeralStoreFrom(
|
|
139
|
+
srcDataMdbPath: string,
|
|
140
|
+
name: string,
|
|
141
|
+
dbMapSizeKb = 10 * 1_024 * 1_024, // 10GB
|
|
142
|
+
maxReaders = MAX_READERS,
|
|
143
|
+
bindings?: LoggerBindings,
|
|
144
|
+
): Promise<AztecLMDBStoreV2> {
|
|
145
|
+
const log = createLogger('kv-store:lmdb-v2:' + name, bindings);
|
|
146
|
+
const dataDir = await mkdtemp(join(tmpdir(), name + '-'));
|
|
147
|
+
await copyFile(srcDataMdbPath, join(dataDir, 'data.mdb'));
|
|
148
|
+
log.debug(`Cloned ephemeral data store at: ${dataDir} from ${srcDataMdbPath} (LMDB v2)`);
|
|
149
|
+
|
|
150
|
+
const cleanup = async () => {
|
|
151
|
+
try {
|
|
152
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 3 });
|
|
153
|
+
log.debug(`Deleted ephemeral data store: ${dataDir}`);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
log.warn(`Failed to delete ephemeral data directory (LMDB v2) ${dataDir}: ${err}`);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return AztecLMDBStoreV2.new(dataDir, dbMapSizeKb, maxReaders, cleanup, bindings, true);
|
|
160
|
+
}
|
|
161
|
+
|
|
92
162
|
export async function openVersionedStoreAt(
|
|
93
163
|
dataDirectory: string,
|
|
94
164
|
schemaVersion: number,
|