@agoric/swing-store 0.9.2-dev-2f092c3.0 → 0.9.2-dev-aa10ecd.0

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.
@@ -0,0 +1,308 @@
1
+ import '@endo/init/debug.js';
2
+
3
+ import test from 'ava';
4
+
5
+ import { buffer } from '../src/util.js';
6
+ import { initSwingStore, makeSwingStoreExporter } from '../src/index.js';
7
+
8
+ import { tmpDir, getSnapshotStream, makeB0ID } from './util.js';
9
+
10
+ const snapshotData = 'snapshot data';
11
+ // this snapHash was computed manually
12
+ const snapHash =
13
+ 'e7dee7266896538616b630a5da40a90e007726a383e005a9c9c5dd0c2daf9329';
14
+
15
+ /** @type {import('../src/bundleStore.js').Bundle} */
16
+ const bundle0 = { moduleFormat: 'nestedEvaluate', source: '1+1' };
17
+ const bundle0ID = makeB0ID(bundle0);
18
+
19
+ const exportTest = test.macro(async (t, mode) => {
20
+ const [dbDir, cleanup] = await tmpDir('testdb');
21
+ t.teardown(cleanup);
22
+ // const dbDir = 't-db';
23
+
24
+ const options = {};
25
+ if (mode === 'debug') {
26
+ options.keepSnapshots = true; // else old snapshots are deleted
27
+ }
28
+ const ss1 = initSwingStore(dbDir, options);
29
+ const ks = ss1.kernelStorage;
30
+
31
+ // build a DB with three spans (only one inUse) and two snapshots
32
+ // (same)
33
+
34
+ ks.kvStore.set('key1', 'value1');
35
+ ks.bundleStore.addBundle(bundle0ID, bundle0);
36
+ ks.transcriptStore.initTranscript('v1');
37
+
38
+ ks.transcriptStore.addItem('v1', 'start-worker'); // 0
39
+ ks.transcriptStore.addItem('v1', 'delivery1'); // 1
40
+ await ks.snapStore.saveSnapshot('v1', 2, getSnapshotStream(snapshotData));
41
+ ks.transcriptStore.addItem('v1', 'save-snapshot'); // 2
42
+ ks.transcriptStore.rolloverSpan('v1'); // range= 0..3
43
+ const spanHash1 =
44
+ '57152efdd7fdf75c03371d2b4f1088d5bf3eae7fe643babce527ff81df38998c';
45
+
46
+ ks.transcriptStore.addItem('v1', 'load-snapshot'); // 3
47
+ ks.transcriptStore.addItem('v1', 'delivery2'); // 4
48
+ await ks.snapStore.saveSnapshot('v1', 5, getSnapshotStream(snapshotData));
49
+ ks.transcriptStore.addItem('v1', 'save-snapshot'); // 5
50
+ ks.transcriptStore.rolloverSpan('v1'); // range= 3..6
51
+ const spanHash2 =
52
+ '1947001e78e01bd1e773feb22b4ffc530447373b9de9274d5d5fbda3f23dbf2b';
53
+
54
+ ks.transcriptStore.addItem('v1', 'load-snapshot'); // 6
55
+ ks.transcriptStore.addItem('v1', 'delivery3'); // 7
56
+ const spanHash3 =
57
+ 'e6b42c6a3fb94285a93162f25a9fc0145fd4c5bb144917dc572c50ae2d02ee69';
58
+ // current range= 6..8
59
+
60
+ ss1.hostStorage.commit();
61
+
62
+ // create an export, and assert that the pieces match what we
63
+ // expect. exportMode='current' means we get all metadata, no
64
+ // historical transcript spans, and no historical snapshots
65
+
66
+ assert.typeof(mode, 'string');
67
+ /** @typedef {import('../src/exporter.js').ExportMode} ExportMode */
68
+ let exportMode = /** @type {ExportMode} */ (mode);
69
+ if (mode === 'debug-on-pruned') {
70
+ exportMode = 'debug';
71
+ }
72
+ const exporter = makeSwingStoreExporter(dbDir, exportMode);
73
+
74
+ // exportData
75
+ {
76
+ const exportData = new Map();
77
+ for await (const [key, value] of exporter.getExportData()) {
78
+ exportData.set(key, value);
79
+ }
80
+ // console.log('exportData:', exportData);
81
+
82
+ const check = (key, expected) => {
83
+ t.true(exportData.has(key));
84
+ let value = exportData.get(key);
85
+ exportData.delete(key);
86
+ if (typeof expected === 'object') {
87
+ value = JSON.parse(value);
88
+ }
89
+ t.deepEqual(value, expected);
90
+ };
91
+
92
+ check('kv.key1', 'value1');
93
+ check('snapshot.v1.2', {
94
+ vatID: 'v1',
95
+ snapPos: 2,
96
+ inUse: 0,
97
+ hash: snapHash,
98
+ });
99
+ check('snapshot.v1.5', {
100
+ vatID: 'v1',
101
+ snapPos: 5,
102
+ inUse: 1,
103
+ hash: snapHash,
104
+ });
105
+ check('snapshot.v1.current', 'snapshot.v1.5');
106
+ const base = { vatID: 'v1', incarnation: 0, isCurrent: 0 };
107
+ check('transcript.v1.0', {
108
+ ...base,
109
+ startPos: 0,
110
+ endPos: 3,
111
+ hash: spanHash1,
112
+ });
113
+ check('transcript.v1.3', {
114
+ ...base,
115
+ startPos: 3,
116
+ endPos: 6,
117
+ hash: spanHash2,
118
+ });
119
+ check('transcript.v1.current', {
120
+ ...base,
121
+ startPos: 6,
122
+ endPos: 8,
123
+ isCurrent: 1,
124
+ hash: spanHash3,
125
+ });
126
+ check(`bundle.${bundle0ID}`, bundle0ID);
127
+
128
+ // the above list is supposed to be exhaustive
129
+ if (exportData.size) {
130
+ console.log('unexpected exportData keys');
131
+ console.log(exportData);
132
+ t.fail('unexpected exportData keys');
133
+ }
134
+ }
135
+
136
+ // artifacts
137
+ {
138
+ const names = new Set();
139
+ const contents = new Map();
140
+ for await (const name of exporter.getArtifactNames()) {
141
+ names.add(name);
142
+ contents.set(name, (await buffer(exporter.getArtifact(name))).toString());
143
+ }
144
+ // console.log('artifacts:', contents);
145
+
146
+ const check = async (name, expected) => {
147
+ t.true(names.has(name));
148
+ names.delete(name);
149
+ let data = contents.get(name);
150
+ if (typeof expected === 'object') {
151
+ data = JSON.parse(data);
152
+ }
153
+ t.deepEqual(data, expected);
154
+ };
155
+
156
+ // export mode 'current' means we omit historical snapshots and
157
+ // transcript spans
158
+
159
+ await check('snapshot.v1.5', 'snapshot data');
160
+ await check('transcript.v1.6.8', 'load-snapshot\ndelivery3\n');
161
+ await check(`bundle.${bundle0ID}`, bundle0);
162
+
163
+ if (mode === 'archival' || mode === 'debug' || mode === 'debug-on-pruned') {
164
+ // adds the old transcript spans
165
+ await check(
166
+ 'transcript.v1.0.3',
167
+ 'start-worker\ndelivery1\nsave-snapshot\n',
168
+ );
169
+ await check(
170
+ 'transcript.v1.3.6',
171
+ 'load-snapshot\ndelivery2\nsave-snapshot\n',
172
+ );
173
+ }
174
+
175
+ if (mode === 'debug') {
176
+ // adds the old snapshots, which are only present if
177
+ // initSwingStore() was given {keepSnapshots: true}
178
+ await check('snapshot.v1.2', 'snapshot data');
179
+ // mode='debug-on-pruned' exercises the keepSnapshots:false case
180
+ }
181
+
182
+ if (names.size) {
183
+ console.log(`unexpected artifacts:`);
184
+ console.log(names);
185
+ t.fail('unexpected artifacts');
186
+ }
187
+ }
188
+ });
189
+
190
+ test('export current', exportTest, 'current');
191
+ test('export archival', exportTest, 'archival');
192
+ test('export debug', exportTest, 'debug');
193
+ test('export debug-on-pruned', exportTest, 'debug-on-pruned');
194
+
195
+ test('export omits pruned span artifacts', async t => {
196
+ const [dbDir, cleanup] = await tmpDir('testdb');
197
+ t.teardown(cleanup);
198
+ // const dbDir = 't-db';
199
+
200
+ // use keepTranscripts=false to simulate an explicit prune of the
201
+ // old span
202
+ const options = { keepTranscripts: false };
203
+ const ss1 = initSwingStore(dbDir, options);
204
+ const ks = ss1.kernelStorage;
205
+
206
+ // build a DB with two spans, one is inUse, other is pruned
207
+
208
+ ks.transcriptStore.initTranscript('v1');
209
+ ks.transcriptStore.addItem('v1', 'start-worker'); // 0
210
+ ks.transcriptStore.addItem('v1', 'delivery1'); // 1
211
+ await ks.snapStore.saveSnapshot('v1', 2, getSnapshotStream(snapshotData));
212
+ ks.transcriptStore.addItem('v1', 'save-snapshot'); // 2
213
+ ks.transcriptStore.rolloverSpan('v1'); // range= 0..3
214
+ const spanHash1 =
215
+ '57152efdd7fdf75c03371d2b4f1088d5bf3eae7fe643babce527ff81df38998c';
216
+ // rolloverSpan prunes the contents of the old span
217
+
218
+ ks.transcriptStore.addItem('v1', 'load-snapshot'); // 3
219
+ ks.transcriptStore.addItem('v1', 'delivery2'); // 4
220
+ const spanHash2 =
221
+ 'b26c8faf425c3c2738e0c5a5e9a7cd71075c68f0c9f2d6cdfd83c68204801dbb';
222
+
223
+ ss1.hostStorage.commit();
224
+
225
+ const exporter = makeSwingStoreExporter(dbDir, 'archival');
226
+
227
+ // exportData
228
+ {
229
+ const exportData = new Map();
230
+ for await (const [key, value] of exporter.getExportData()) {
231
+ exportData.set(key, value);
232
+ }
233
+ // console.log('exportData:', exportData);
234
+
235
+ const check = (key, expected) => {
236
+ t.true(exportData.has(key));
237
+ let value = exportData.get(key);
238
+ exportData.delete(key);
239
+ if (typeof expected === 'object') {
240
+ value = JSON.parse(value);
241
+ }
242
+ t.deepEqual(value, expected);
243
+ };
244
+
245
+ check('snapshot.v1.2', {
246
+ vatID: 'v1',
247
+ snapPos: 2,
248
+ inUse: 1,
249
+ hash: snapHash,
250
+ });
251
+ check('snapshot.v1.current', 'snapshot.v1.2');
252
+ const base = { vatID: 'v1', incarnation: 0, isCurrent: 0 };
253
+ check('transcript.v1.0', {
254
+ ...base,
255
+ startPos: 0,
256
+ endPos: 3,
257
+ hash: spanHash1,
258
+ });
259
+ check('transcript.v1.current', {
260
+ ...base,
261
+ startPos: 3,
262
+ endPos: 5,
263
+ isCurrent: 1,
264
+ hash: spanHash2,
265
+ });
266
+
267
+ // the above list is supposed to be exhaustive
268
+ if (exportData.size) {
269
+ console.log('unexpected exportData keys');
270
+ console.log(exportData);
271
+ t.fail('unexpected exportData keys');
272
+ }
273
+ }
274
+
275
+ // artifacts
276
+ {
277
+ const names = new Set();
278
+ const contents = new Map();
279
+ for await (const name of exporter.getArtifactNames()) {
280
+ names.add(name);
281
+ contents.set(name, (await buffer(exporter.getArtifact(name))).toString());
282
+ }
283
+ // console.log('artifacts:', contents);
284
+
285
+ const check = async (name, expected) => {
286
+ t.true(names.has(name));
287
+ names.delete(name);
288
+ let data = contents.get(name);
289
+ if (typeof expected === 'object') {
290
+ data = JSON.parse(data);
291
+ }
292
+ t.deepEqual(data, expected);
293
+ };
294
+
295
+ // export mode 'archival' means we include all available
296
+ // historical snapshots and transcript spans
297
+
298
+ await check('snapshot.v1.2', 'snapshot data');
299
+ // no transcript.v1.0.3 because the contents were pruned
300
+ await check('transcript.v1.3.5', 'load-snapshot\ndelivery2\n');
301
+
302
+ if (names.size) {
303
+ console.log(`unexpected artifacts:`);
304
+ console.log(names);
305
+ t.fail('unexpected artifacts');
306
+ }
307
+ }
308
+ });
@@ -7,11 +7,9 @@ import test from 'ava';
7
7
  import tmp from 'tmp';
8
8
  import bundleSource from '@endo/bundle-source';
9
9
 
10
- import {
11
- initSwingStore,
12
- makeSwingStoreExporter,
13
- importSwingStore,
14
- } from '../src/swingStore.js';
10
+ import { initSwingStore } from '../src/swingStore.js';
11
+ import { makeSwingStoreExporter } from '../src/exporter.js';
12
+ import { importSwingStore } from '../src/importer.js';
15
13
 
16
14
  function makeExportLog() {
17
15
  const exportLog = [];
@@ -173,7 +171,6 @@ async function testExportImport(
173
171
  runMode,
174
172
  exportMode,
175
173
  importMode,
176
- failureMode,
177
174
  expectedArtifactNames,
178
175
  ) {
179
176
  const exportLog = makeExportLog();
@@ -295,35 +292,20 @@ async function testExportImport(
295
292
  ],
296
293
  ]);
297
294
 
298
- expectedArtifactNames = Array.from(expectedArtifactNames);
299
- expectedArtifactNames.push(`bundle.${bundleIDA}`);
300
- expectedArtifactNames.push(`bundle.${bundleIDB}`);
295
+ expectedArtifactNames = new Set(expectedArtifactNames);
296
+ expectedArtifactNames.add(`bundle.${bundleIDA}`);
297
+ expectedArtifactNames.add(`bundle.${bundleIDB}`);
301
298
 
302
- const artifactNames = [];
299
+ const artifactNames = new Set();
303
300
  for await (const name of exporter.getArtifactNames()) {
304
- artifactNames.push(name);
301
+ artifactNames.add(name);
305
302
  }
306
303
  t.deepEqual(artifactNames, expectedArtifactNames);
307
304
 
308
305
  const includeHistorical = importMode !== 'current';
309
306
 
310
307
  const beforeDump = debug.dump(keepSnapshots);
311
- let ssIn;
312
- try {
313
- ssIn = await importSwingStore(exporter, null, {
314
- includeHistorical,
315
- });
316
- } catch (e) {
317
- if (failureMode === 'transcript') {
318
- t.is(e.message, 'artifact "transcript.vatA.0.3" is not available');
319
- return;
320
- } else if (failureMode === 'snapshot') {
321
- t.is(e.message, 'artifact "snapshot.vatA.2" is not available');
322
- return;
323
- }
324
- throw e;
325
- }
326
- t.is(failureMode, 'none');
308
+ const ssIn = await importSwingStore(exporter, null, { includeHistorical });
327
309
  await ssIn.hostStorage.commit();
328
310
  const dumpsShouldMatch =
329
311
  runMode !== 'debug' || (exportMode === 'debug' && importMode !== 'current');
@@ -366,95 +348,95 @@ const A = 'archival';
366
348
  const D = 'debug';
367
349
 
368
350
  test('export and import data for state sync - current->current->current', async t => {
369
- await testExportImport(t, C, C, C, 'none', expectedCurrentArtifacts);
351
+ await testExportImport(t, C, C, C, expectedCurrentArtifacts);
370
352
  });
371
353
  test('export and import data for state sync - current->current->archival', async t => {
372
- await testExportImport(t, C, C, A, 'none', expectedCurrentArtifacts);
354
+ await testExportImport(t, C, C, A, expectedCurrentArtifacts);
373
355
  });
374
356
  test('export and import data for state sync - current->current->debug', async t => {
375
- await testExportImport(t, C, C, D, 'none', expectedCurrentArtifacts);
357
+ await testExportImport(t, C, C, D, expectedCurrentArtifacts);
376
358
  });
377
359
 
378
360
  test('export and import data for state sync - current->archival->current', async t => {
379
- await testExportImport(t, C, A, C, 'none', expectedArchivalArtifacts);
361
+ await testExportImport(t, C, A, C, expectedCurrentArtifacts);
380
362
  });
381
363
  test('export and import data for state sync - current->archival->archival', async t => {
382
- await testExportImport(t, C, A, A, 'transcript', expectedArchivalArtifacts);
364
+ await testExportImport(t, C, A, A, expectedCurrentArtifacts);
383
365
  });
384
366
  test('export and import data for state sync - current->archival->debug', async t => {
385
- await testExportImport(t, C, A, D, 'transcript', expectedArchivalArtifacts);
367
+ await testExportImport(t, C, A, D, expectedCurrentArtifacts);
386
368
  });
387
369
 
388
370
  test('export and import data for state sync - current->debug->current', async t => {
389
- await testExportImport(t, C, D, C, 'none', expectedDebugArtifacts);
371
+ await testExportImport(t, C, D, C, expectedCurrentArtifacts);
390
372
  });
391
373
  test('export and import data for state sync - current->debug->archival', async t => {
392
- await testExportImport(t, C, D, A, 'snapshot', expectedDebugArtifacts);
374
+ await testExportImport(t, C, D, A, expectedCurrentArtifacts);
393
375
  });
394
376
  test('export and import data for state sync - current->debug->debug', async t => {
395
- await testExportImport(t, C, D, D, 'snapshot', expectedDebugArtifacts);
377
+ await testExportImport(t, C, D, D, expectedCurrentArtifacts);
396
378
  });
397
379
 
398
380
  // ------------------------------------------------------------
399
381
 
400
382
  test('export and import data for state sync - archival->current->current', async t => {
401
- await testExportImport(t, A, C, C, 'none', expectedCurrentArtifacts);
383
+ await testExportImport(t, A, C, C, expectedCurrentArtifacts);
402
384
  });
403
385
  test('export and import data for state sync - archival->current->archival', async t => {
404
- await testExportImport(t, A, C, A, 'none', expectedCurrentArtifacts);
386
+ await testExportImport(t, A, C, A, expectedCurrentArtifacts);
405
387
  });
406
388
  test('export and import data for state sync - archival->current->debug', async t => {
407
- await testExportImport(t, A, C, D, 'none', expectedCurrentArtifacts);
389
+ await testExportImport(t, A, C, D, expectedCurrentArtifacts);
408
390
  });
409
391
 
410
392
  test('export and import data for state sync - archival->archival->current', async t => {
411
- await testExportImport(t, A, A, C, 'none', expectedArchivalArtifacts);
393
+ await testExportImport(t, A, A, C, expectedArchivalArtifacts);
412
394
  });
413
395
  test('export and import data for state sync - archival->archival->archival', async t => {
414
- await testExportImport(t, A, A, A, 'none', expectedArchivalArtifacts);
396
+ await testExportImport(t, A, A, A, expectedArchivalArtifacts);
415
397
  });
416
398
  test('export and import data for state sync - archival->archival->debug', async t => {
417
- await testExportImport(t, A, A, D, 'none', expectedArchivalArtifacts);
399
+ await testExportImport(t, A, A, D, expectedArchivalArtifacts);
418
400
  });
419
401
 
420
402
  test('export and import data for state sync - archival->debug->current', async t => {
421
- await testExportImport(t, A, D, C, 'none', expectedDebugArtifacts);
403
+ await testExportImport(t, A, D, C, expectedArchivalArtifacts);
422
404
  });
423
405
  test('export and import data for state sync - archival->debug->archival', async t => {
424
- await testExportImport(t, A, D, A, 'snapshot', expectedDebugArtifacts);
406
+ await testExportImport(t, A, D, A, expectedArchivalArtifacts);
425
407
  });
426
408
  test('export and import data for state sync - archival->debug->debug', async t => {
427
- await testExportImport(t, A, D, D, 'snapshot', expectedDebugArtifacts);
409
+ await testExportImport(t, A, D, D, expectedArchivalArtifacts);
428
410
  });
429
411
 
430
412
  // ------------------------------------------------------------
431
413
 
432
414
  test('export and import data for state sync - debug->current->current', async t => {
433
- await testExportImport(t, D, C, C, 'none', expectedCurrentArtifacts);
415
+ await testExportImport(t, D, C, C, expectedCurrentArtifacts);
434
416
  });
435
417
  test('export and import data for state sync - debug->current->archival', async t => {
436
- await testExportImport(t, D, C, A, 'none', expectedCurrentArtifacts);
418
+ await testExportImport(t, D, C, A, expectedCurrentArtifacts);
437
419
  });
438
420
  test('export and import data for state sync - debug->current->debug', async t => {
439
- await testExportImport(t, D, C, D, 'none', expectedCurrentArtifacts);
421
+ await testExportImport(t, D, C, D, expectedCurrentArtifacts);
440
422
  });
441
423
 
442
424
  test('export and import data for state sync - debug->archival->current', async t => {
443
- await testExportImport(t, D, A, C, 'none', expectedArchivalArtifacts);
425
+ await testExportImport(t, D, A, C, expectedArchivalArtifacts);
444
426
  });
445
427
  test('export and import data for state sync - debug->archival->archival', async t => {
446
- await testExportImport(t, D, A, A, 'none', expectedArchivalArtifacts);
428
+ await testExportImport(t, D, A, A, expectedArchivalArtifacts);
447
429
  });
448
430
  test('export and import data for state sync - debug->archival->debug', async t => {
449
- await testExportImport(t, D, A, D, 'none', expectedArchivalArtifacts);
431
+ await testExportImport(t, D, A, D, expectedArchivalArtifacts);
450
432
  });
451
433
 
452
434
  test('export and import data for state sync - debug->debug->current', async t => {
453
- await testExportImport(t, D, D, C, 'none', expectedDebugArtifacts);
435
+ await testExportImport(t, D, D, C, expectedDebugArtifacts);
454
436
  });
455
437
  test('export and import data for state sync - debug->debug->archival', async t => {
456
- await testExportImport(t, D, D, A, 'none', expectedDebugArtifacts);
438
+ await testExportImport(t, D, D, A, expectedDebugArtifacts);
457
439
  });
458
440
  test('export and import data for state sync - debug->debug->debug', async t => {
459
- await testExportImport(t, D, D, D, 'none', expectedDebugArtifacts);
441
+ await testExportImport(t, D, D, D, expectedDebugArtifacts);
460
442
  });