@agoric/swing-store 0.9.2-dev-bf1e645.0 → 0.9.2-dev-07f6885.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.
- package/docs/transcriptstore.md +3 -3
- package/package.json +3 -3
- package/src/archiver.js +80 -0
- package/src/index.js +2 -0
- package/src/snapStore.js +55 -67
- package/src/swingStore.js +9 -1
- package/src/transcriptStore.js +167 -145
- package/test/deletion.test.js +182 -131
- package/test/export.test.js +4 -4
- package/test/exportImport.test.js +1 -1
- package/test/snapstore.test.js +47 -2
- package/test/state.test.js +2 -2
package/test/deletion.test.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import test from 'ava';
|
|
3
|
-
import path from 'path';
|
|
4
3
|
|
|
5
4
|
import { Buffer } from 'node:buffer';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import zlib from 'node:zlib';
|
|
6
8
|
import sqlite3 from 'better-sqlite3';
|
|
9
|
+
import tmp from 'tmp';
|
|
10
|
+
import { arrayIsLike } from '@agoric/internal/tools/ava-assertions.js';
|
|
7
11
|
import { tmpDir } from './util.js';
|
|
8
12
|
import { initSwingStore } from '../src/swingStore.js';
|
|
13
|
+
import { makeArchiveSnapshot, makeArchiveTranscript } from '../src/archiver.js';
|
|
9
14
|
import { makeSwingStoreExporter } from '../src/exporter.js';
|
|
10
15
|
import { importSwingStore } from '../src/importer.js';
|
|
11
16
|
|
|
@@ -50,32 +55,31 @@ test('delete snapshots with export callback', async t => {
|
|
|
50
55
|
|
|
51
56
|
await commit();
|
|
52
57
|
|
|
53
|
-
t.is(exportLog.length, 4);
|
|
54
|
-
t.is(exportLog[0][0], 'snapshot.v1.10');
|
|
55
|
-
t.is(exportLog[1][0], 'snapshot.v1.11');
|
|
56
|
-
t.is(exportLog[2][0], 'snapshot.v1.12');
|
|
57
|
-
t.is(exportLog[3][0], 'snapshot.v1.current');
|
|
58
58
|
const hash = JSON.parse(exportLog[0][1]).hash;
|
|
59
|
+
arrayIsLike(t, exportLog.splice(0), [
|
|
60
|
+
['snapshot.v1.10'],
|
|
61
|
+
['snapshot.v1.11'],
|
|
62
|
+
['snapshot.v1.12'],
|
|
63
|
+
['snapshot.v1.current'],
|
|
64
|
+
]);
|
|
59
65
|
t.deepEqual(exportData, {
|
|
60
66
|
'snapshot.v1.10': JSON.stringify({ vatID, snapPos: 10, hash, inUse: 0 }),
|
|
61
67
|
'snapshot.v1.11': JSON.stringify({ vatID, snapPos: 11, hash, inUse: 0 }),
|
|
62
68
|
'snapshot.v1.12': JSON.stringify({ vatID, snapPos: 12, hash, inUse: 1 }),
|
|
63
69
|
'snapshot.v1.current': 'snapshot.v1.12',
|
|
64
70
|
});
|
|
65
|
-
exportLog.length = 0;
|
|
66
71
|
|
|
67
72
|
// in a previous version, deleteVatSnapshots caused overlapping SQL
|
|
68
73
|
// queries, and failed
|
|
69
74
|
snapStore.deleteVatSnapshots(vatID);
|
|
70
75
|
await commit();
|
|
71
76
|
|
|
72
|
-
t.deepEqual(exportLog, [
|
|
77
|
+
t.deepEqual(exportLog.splice(0), [
|
|
73
78
|
['snapshot.v1.10', null],
|
|
74
79
|
['snapshot.v1.11', null],
|
|
75
80
|
['snapshot.v1.12', null],
|
|
76
81
|
['snapshot.v1.current', null],
|
|
77
82
|
]);
|
|
78
|
-
exportLog.length = 0;
|
|
79
83
|
t.deepEqual(exportData, {});
|
|
80
84
|
});
|
|
81
85
|
|
|
@@ -95,7 +99,7 @@ test('delete transcripts with export callback', async t => {
|
|
|
95
99
|
transcriptStore.addItem('v1', 'aaa');
|
|
96
100
|
transcriptStore.addItem('v1', 'bbb');
|
|
97
101
|
transcriptStore.addItem('v1', 'ccc');
|
|
98
|
-
transcriptStore.rolloverSpan('v1');
|
|
102
|
+
await transcriptStore.rolloverSpan('v1');
|
|
99
103
|
transcriptStore.addItem('v1', 'ddd');
|
|
100
104
|
transcriptStore.addItem('v1', 'eee');
|
|
101
105
|
transcriptStore.addItem('v1', 'fff');
|
|
@@ -104,22 +108,20 @@ test('delete transcripts with export callback', async t => {
|
|
|
104
108
|
|
|
105
109
|
await commit();
|
|
106
110
|
|
|
107
|
-
t.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
arrayIsLike(t, exportLog.splice(0), [
|
|
112
|
+
['transcript.v1.0'],
|
|
113
|
+
['transcript.v1.current'],
|
|
114
|
+
]);
|
|
111
115
|
|
|
112
116
|
// in a previous version, deleteVatTranscripts caused overlapping SQL
|
|
113
117
|
// queries, and failed
|
|
114
118
|
transcriptStore.deleteVatTranscripts('v1');
|
|
115
119
|
await commit();
|
|
116
120
|
|
|
117
|
-
t.deepEqual(exportLog, [
|
|
121
|
+
t.deepEqual(exportLog.splice(0), [
|
|
118
122
|
['transcript.v1.0', null],
|
|
119
123
|
['transcript.v1.current', null],
|
|
120
124
|
]);
|
|
121
|
-
|
|
122
|
-
exportLog.length = 0;
|
|
123
125
|
});
|
|
124
126
|
|
|
125
127
|
const getExport = async (dbDir, artifactMode) => {
|
|
@@ -145,16 +147,20 @@ const reImport = async (t, dbDir, artifactMode) => {
|
|
|
145
147
|
return sqlite3(path.join(dbDir2, 'swingstore.sqlite'));
|
|
146
148
|
};
|
|
147
149
|
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
150
|
+
const stripHashes = exportData => {
|
|
151
|
+
const stripped = {};
|
|
152
|
+
const entries =
|
|
153
|
+
exportData instanceof Map
|
|
154
|
+
? exportData.entries()
|
|
155
|
+
: Object.entries(exportData);
|
|
156
|
+
for (const [key, value] of entries) {
|
|
151
157
|
const { hash: _, ...data } = JSON.parse(value);
|
|
152
|
-
|
|
158
|
+
stripped[key] = data;
|
|
153
159
|
}
|
|
154
|
-
return
|
|
160
|
+
return stripped;
|
|
155
161
|
};
|
|
156
162
|
|
|
157
|
-
const setupTranscript = async t => {
|
|
163
|
+
const setupTranscript = async (t, keepTranscripts) => {
|
|
158
164
|
const vatID = 'v1';
|
|
159
165
|
const exportLog = [];
|
|
160
166
|
const currentExportData = {};
|
|
@@ -166,7 +172,17 @@ const setupTranscript = async t => {
|
|
|
166
172
|
};
|
|
167
173
|
const [dbDir, cleanup] = await tmpDir('testdb');
|
|
168
174
|
t.teardown(cleanup);
|
|
169
|
-
const
|
|
175
|
+
const [archiveDir, cleanupArchives] = await tmpDir('archives');
|
|
176
|
+
t.teardown(cleanupArchives);
|
|
177
|
+
const fsPowers = { fs, path, tmp };
|
|
178
|
+
const archiveSnapshot = makeArchiveSnapshot(archiveDir, fsPowers);
|
|
179
|
+
const archiveTranscript = makeArchiveTranscript(archiveDir, fsPowers);
|
|
180
|
+
const store = initSwingStore(dbDir, {
|
|
181
|
+
exportCallback,
|
|
182
|
+
keepTranscripts,
|
|
183
|
+
archiveSnapshot,
|
|
184
|
+
archiveTranscript,
|
|
185
|
+
});
|
|
170
186
|
const { kernelStorage, hostStorage } = store;
|
|
171
187
|
const { transcriptStore } = kernelStorage;
|
|
172
188
|
const { commit } = hostStorage;
|
|
@@ -177,13 +193,13 @@ const setupTranscript = async t => {
|
|
|
177
193
|
transcriptStore.initTranscript(vatID);
|
|
178
194
|
transcriptStore.addItem(vatID, 'aaa');
|
|
179
195
|
transcriptStore.addItem(vatID, 'bbb');
|
|
180
|
-
transcriptStore.rolloverSpan(vatID);
|
|
196
|
+
await transcriptStore.rolloverSpan(vatID);
|
|
181
197
|
transcriptStore.addItem(vatID, 'ccc');
|
|
182
198
|
transcriptStore.addItem(vatID, 'ddd');
|
|
183
|
-
transcriptStore.rolloverIncarnation(vatID);
|
|
199
|
+
await transcriptStore.rolloverIncarnation(vatID);
|
|
184
200
|
transcriptStore.addItem(vatID, 'eee');
|
|
185
201
|
transcriptStore.addItem(vatID, 'fff');
|
|
186
|
-
transcriptStore.rolloverSpan(vatID);
|
|
202
|
+
await transcriptStore.rolloverSpan(vatID);
|
|
187
203
|
transcriptStore.addItem(vatID, 'ggg');
|
|
188
204
|
transcriptStore.addItem(vatID, 'hhh');
|
|
189
205
|
await commit();
|
|
@@ -191,6 +207,7 @@ const setupTranscript = async t => {
|
|
|
191
207
|
return {
|
|
192
208
|
db,
|
|
193
209
|
dbDir,
|
|
210
|
+
archiveDir,
|
|
194
211
|
commit,
|
|
195
212
|
transcriptStore,
|
|
196
213
|
exportLog,
|
|
@@ -199,74 +216,93 @@ const setupTranscript = async t => {
|
|
|
199
216
|
};
|
|
200
217
|
};
|
|
201
218
|
|
|
202
|
-
|
|
219
|
+
/**
|
|
220
|
+
* @param {import('ava').ExecutionContext} t
|
|
221
|
+
* @param {{ keepTranscripts: boolean }} config
|
|
222
|
+
*/
|
|
223
|
+
const execSlowTranscriptDeletion = async (t, { keepTranscripts }) => {
|
|
203
224
|
// slow transcript deletion should remove export-data as it removes
|
|
204
225
|
// transcript spans and their items
|
|
205
226
|
|
|
206
227
|
const {
|
|
207
228
|
db,
|
|
208
229
|
dbDir,
|
|
230
|
+
archiveDir,
|
|
209
231
|
commit,
|
|
210
232
|
transcriptStore,
|
|
211
233
|
exportLog,
|
|
212
234
|
currentExportData,
|
|
213
235
|
vatID,
|
|
214
|
-
} = await setupTranscript(t);
|
|
215
|
-
|
|
216
|
-
t.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
236
|
+
} = await setupTranscript(t, keepTranscripts);
|
|
237
|
+
|
|
238
|
+
arrayIsLike(t, exportLog.splice(0), [
|
|
239
|
+
['transcript.v1.0'],
|
|
240
|
+
['transcript.v1.2'],
|
|
241
|
+
['transcript.v1.4'],
|
|
242
|
+
['transcript.v1.current'],
|
|
243
|
+
]);
|
|
222
244
|
const t0 = { vatID, startPos: 0, endPos: 2, isCurrent: 0, incarnation: 0 };
|
|
223
245
|
const t2 = { vatID, startPos: 2, endPos: 4, isCurrent: 0, incarnation: 0 };
|
|
224
246
|
const t4 = { vatID, startPos: 4, endPos: 6, isCurrent: 0, incarnation: 1 };
|
|
225
|
-
const tc = { vatID, startPos: 6, endPos: 8, isCurrent: 1, incarnation: 1 };
|
|
226
247
|
const t6 = { vatID, startPos: 6, endPos: 8, isCurrent: 0, incarnation: 1 };
|
|
227
|
-
|
|
248
|
+
const expectedLiveExportData = {
|
|
228
249
|
'transcript.v1.0': t0,
|
|
229
250
|
'transcript.v1.2': t2,
|
|
230
251
|
'transcript.v1.4': t4,
|
|
231
|
-
'transcript.v1.current':
|
|
232
|
-
}
|
|
252
|
+
'transcript.v1.current': { ...t6, isCurrent: 1 },
|
|
253
|
+
};
|
|
254
|
+
const expectedStoppedExportData = {
|
|
255
|
+
'transcript.v1.0': t0,
|
|
256
|
+
'transcript.v1.2': t2,
|
|
257
|
+
'transcript.v1.4': t4,
|
|
258
|
+
'transcript.v1.6': t6,
|
|
259
|
+
};
|
|
260
|
+
const expectedArtifactContents = {
|
|
261
|
+
'transcript.v1.0.2': 'aaa\nbbb\n',
|
|
262
|
+
'transcript.v1.2.4': 'ccc\nddd\n',
|
|
263
|
+
'transcript.v1.4.6': 'eee\nfff\n',
|
|
264
|
+
'transcript.v1.6.8': 'ggg\nhhh\n',
|
|
265
|
+
};
|
|
266
|
+
const expectedArtifactNames = Object.keys(expectedArtifactContents);
|
|
233
267
|
|
|
234
|
-
t.
|
|
268
|
+
t.deepEqual(stripHashes(currentExportData), expectedLiveExportData);
|
|
269
|
+
t.is(
|
|
270
|
+
db.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(),
|
|
271
|
+
keepTranscripts ? 8 : 2,
|
|
272
|
+
);
|
|
235
273
|
t.is(db.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 4);
|
|
236
274
|
|
|
275
|
+
// verify archived transcripts
|
|
276
|
+
t.deepEqual(
|
|
277
|
+
fs.readdirSync(archiveDir),
|
|
278
|
+
expectedArtifactNames.slice(0, -1).map(name => `${name}.gz`),
|
|
279
|
+
);
|
|
280
|
+
for (const name of expectedArtifactNames.slice(0, -1)) {
|
|
281
|
+
const filePath = path.join(archiveDir, `${name}.gz`);
|
|
282
|
+
const contents = zlib.gunzipSync(fs.readFileSync(filePath)).toString();
|
|
283
|
+
t.is(contents, expectedArtifactContents[name], `${filePath} contents`);
|
|
284
|
+
}
|
|
285
|
+
|
|
237
286
|
// an "operational"-mode export should list all spans, but only have
|
|
238
287
|
// artifacts for the current one
|
|
239
288
|
{
|
|
240
289
|
const { exportData, artifactNames } = await getExport(dbDir, 'operational');
|
|
241
290
|
t.deepEqual(currentExportData, mapToObj(exportData));
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
'transcript.v1.2': t2,
|
|
245
|
-
'transcript.v1.4': t4,
|
|
246
|
-
'transcript.v1.current': tc,
|
|
247
|
-
});
|
|
248
|
-
t.deepEqual(artifactNames, ['transcript.v1.6.8']);
|
|
291
|
+
t.deepEqual(stripHashes(exportData), expectedLiveExportData);
|
|
292
|
+
t.deepEqual(artifactNames, expectedArtifactNames.slice(-1));
|
|
249
293
|
const db2 = await reImport(t, dbDir, 'operational');
|
|
250
294
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 2);
|
|
251
295
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 4);
|
|
252
296
|
}
|
|
253
297
|
|
|
254
|
-
// an "archival"-mode export should list all four spans
|
|
255
|
-
// artifacts for each
|
|
256
|
-
{
|
|
298
|
+
// an "archival"-mode export should list all four spans with
|
|
299
|
+
// artifacts for each, but is only available with keepTranscripts=true
|
|
300
|
+
if (!keepTranscripts) {
|
|
301
|
+
await t.throwsAsync(getExport(dbDir, 'archival'));
|
|
302
|
+
} else {
|
|
257
303
|
const { exportData, artifactNames } = await getExport(dbDir, 'archival');
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
'transcript.v1.2': t2,
|
|
261
|
-
'transcript.v1.4': t4,
|
|
262
|
-
'transcript.v1.current': tc,
|
|
263
|
-
});
|
|
264
|
-
t.deepEqual(artifactNames, [
|
|
265
|
-
'transcript.v1.0.2',
|
|
266
|
-
'transcript.v1.2.4',
|
|
267
|
-
'transcript.v1.4.6',
|
|
268
|
-
'transcript.v1.6.8',
|
|
269
|
-
]);
|
|
304
|
+
t.deepEqual(stripHashes(exportData), expectedLiveExportData);
|
|
305
|
+
t.deepEqual(artifactNames, expectedArtifactNames);
|
|
270
306
|
const db2 = await reImport(t, dbDir, 'archival');
|
|
271
307
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 8);
|
|
272
308
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 4);
|
|
@@ -276,60 +312,60 @@ test('slow deletion of transcripts', async t => {
|
|
|
276
312
|
// deletes the .current record (i.e. it transforms .current into a
|
|
277
313
|
// closed record)
|
|
278
314
|
{
|
|
279
|
-
transcriptStore.stopUsingTranscript(vatID);
|
|
315
|
+
await transcriptStore.stopUsingTranscript(vatID);
|
|
280
316
|
await commit();
|
|
281
|
-
|
|
282
|
-
'transcript.v1.0': t0,
|
|
283
|
-
'transcript.v1.2': t2,
|
|
284
|
-
'transcript.v1.4': t4,
|
|
285
|
-
'transcript.v1.6': t6,
|
|
286
|
-
});
|
|
317
|
+
t.deepEqual(stripHashes(currentExportData), expectedStoppedExportData);
|
|
287
318
|
exportLog.length = 0;
|
|
288
319
|
// stopUsingTranscript is idempotent
|
|
289
|
-
transcriptStore.stopUsingTranscript(vatID);
|
|
320
|
+
await transcriptStore.stopUsingTranscript(vatID);
|
|
290
321
|
await commit();
|
|
291
|
-
t.
|
|
322
|
+
t.deepEqual(exportLog, []);
|
|
323
|
+
}
|
|
324
|
+
t.deepEqual(
|
|
325
|
+
fs.readdirSync(archiveDir),
|
|
326
|
+
expectedArtifactNames.map(name => `${name}.gz`),
|
|
327
|
+
);
|
|
328
|
+
for (const name of expectedArtifactNames) {
|
|
329
|
+
const filePath = path.join(archiveDir, `${name}.gz`);
|
|
330
|
+
const contents = zlib.gunzipSync(fs.readFileSync(filePath)).toString();
|
|
331
|
+
t.is(contents, expectedArtifactContents[name], `${filePath} contents`);
|
|
292
332
|
}
|
|
293
333
|
|
|
294
334
|
// All exports (debug and non-debug) in this "terminated but not
|
|
295
335
|
// deleted" state will still have the export-data keys. Only
|
|
296
|
-
// debug-mode will have artifacts.
|
|
336
|
+
// debug-mode will have artifacts (and only with keepTranscripts=true).
|
|
297
337
|
for (const mode of ['operational', 'replay', 'archival', 'debug']) {
|
|
298
338
|
const { exportData, artifactNames } = await getExport(dbDir, mode);
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
'transcript.v1.4.6',
|
|
310
|
-
'transcript.v1.6.8',
|
|
311
|
-
]);
|
|
312
|
-
} else {
|
|
313
|
-
t.deepEqual(artifactNames, []);
|
|
314
|
-
}
|
|
339
|
+
t.deepEqual(
|
|
340
|
+
stripHashes(exportData),
|
|
341
|
+
expectedStoppedExportData,
|
|
342
|
+
`${mode} stopped-vat export data`,
|
|
343
|
+
);
|
|
344
|
+
t.deepEqual(
|
|
345
|
+
artifactNames,
|
|
346
|
+
mode === 'debug' && keepTranscripts ? expectedArtifactNames : [],
|
|
347
|
+
`${mode} stopped-vat export artifacts`,
|
|
348
|
+
);
|
|
315
349
|
const db2 = await reImport(t, dbDir, 'operational');
|
|
316
350
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 0);
|
|
317
351
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 4);
|
|
318
352
|
}
|
|
319
353
|
|
|
320
354
|
// first deletion
|
|
355
|
+
const expectedTruncatedExportData1 = Object.fromEntries(
|
|
356
|
+
Object.entries(expectedStoppedExportData).slice(0, -1),
|
|
357
|
+
);
|
|
321
358
|
{
|
|
322
359
|
// budget=1 will let it delete one span, the last one
|
|
323
360
|
const dc = transcriptStore.deleteVatTranscripts(vatID, 1);
|
|
324
361
|
t.false(dc.done);
|
|
325
362
|
t.is(dc.cleanups, 1);
|
|
326
363
|
await commit();
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
'
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
t.is(db.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 6);
|
|
364
|
+
t.deepEqual(stripHashes(currentExportData), expectedTruncatedExportData1);
|
|
365
|
+
t.is(
|
|
366
|
+
db.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(),
|
|
367
|
+
keepTranscripts ? 6 : 0,
|
|
368
|
+
);
|
|
333
369
|
t.is(db.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 3);
|
|
334
370
|
}
|
|
335
371
|
|
|
@@ -341,50 +377,53 @@ test('slow deletion of transcripts', async t => {
|
|
|
341
377
|
|
|
342
378
|
for (const mode of ['operational', 'replay', 'archival', 'debug']) {
|
|
343
379
|
const { exportData, artifactNames } = await getExport(dbDir, mode);
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
t.deepEqual(artifactNames, []);
|
|
357
|
-
}
|
|
380
|
+
t.deepEqual(
|
|
381
|
+
stripHashes(exportData),
|
|
382
|
+
expectedTruncatedExportData1,
|
|
383
|
+
`${mode} first-deletion export data`,
|
|
384
|
+
);
|
|
385
|
+
t.deepEqual(
|
|
386
|
+
artifactNames,
|
|
387
|
+
mode === 'debug' && keepTranscripts
|
|
388
|
+
? expectedArtifactNames.slice(0, -1)
|
|
389
|
+
: [],
|
|
390
|
+
`${mode} first-deletion export artifacts`,
|
|
391
|
+
);
|
|
358
392
|
const db2 = await reImport(t, dbDir, 'operational');
|
|
359
393
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 0);
|
|
360
394
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 3);
|
|
361
395
|
}
|
|
362
396
|
|
|
363
397
|
// second deletion
|
|
398
|
+
const expectedTruncatedExportData2 = Object.fromEntries(
|
|
399
|
+
Object.entries(expectedStoppedExportData).slice(0, -2),
|
|
400
|
+
);
|
|
364
401
|
{
|
|
365
402
|
const dc = transcriptStore.deleteVatTranscripts(vatID, 1);
|
|
366
403
|
t.false(dc.done);
|
|
367
404
|
t.is(dc.cleanups, 1);
|
|
368
405
|
await commit();
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
'
|
|
372
|
-
|
|
373
|
-
|
|
406
|
+
t.deepEqual(stripHashes(currentExportData), expectedTruncatedExportData2);
|
|
407
|
+
t.is(
|
|
408
|
+
db.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(),
|
|
409
|
+
keepTranscripts ? 4 : 0,
|
|
410
|
+
);
|
|
374
411
|
t.is(db.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 2);
|
|
375
412
|
}
|
|
376
|
-
|
|
377
413
|
for (const mode of ['operational', 'replay', 'archival', 'debug']) {
|
|
378
414
|
const { exportData, artifactNames } = await getExport(dbDir, mode);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
415
|
+
t.deepEqual(
|
|
416
|
+
stripHashes(exportData),
|
|
417
|
+
expectedTruncatedExportData2,
|
|
418
|
+
`${mode} second-deletion export data`,
|
|
419
|
+
);
|
|
420
|
+
t.deepEqual(
|
|
421
|
+
artifactNames,
|
|
422
|
+
mode === 'debug' && keepTranscripts
|
|
423
|
+
? expectedArtifactNames.slice(0, -2)
|
|
424
|
+
: [],
|
|
425
|
+
`${mode} second-deletion export artifacts`,
|
|
426
|
+
);
|
|
388
427
|
const db2 = await reImport(t, dbDir, 'operational');
|
|
389
428
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 0);
|
|
390
429
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 2);
|
|
@@ -396,15 +435,18 @@ test('slow deletion of transcripts', async t => {
|
|
|
396
435
|
t.true(dc.done);
|
|
397
436
|
t.is(dc.cleanups, 2);
|
|
398
437
|
await commit();
|
|
399
|
-
|
|
438
|
+
t.deepEqual(stripHashes(currentExportData), {});
|
|
400
439
|
t.is(db.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 0);
|
|
401
440
|
t.is(db.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 0);
|
|
402
441
|
}
|
|
403
|
-
|
|
404
442
|
for (const mode of ['operational', 'replay', 'archival', 'debug']) {
|
|
405
443
|
const { exportData, artifactNames } = await getExport(dbDir, mode);
|
|
406
|
-
|
|
407
|
-
|
|
444
|
+
t.deepEqual(
|
|
445
|
+
stripHashes(exportData),
|
|
446
|
+
{},
|
|
447
|
+
`${mode} final-deletion export data`,
|
|
448
|
+
);
|
|
449
|
+
t.deepEqual(artifactNames, [], `${mode} final-deletion export artifacts`);
|
|
408
450
|
const db2 = await reImport(t, dbDir, 'operational');
|
|
409
451
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptItems').pluck().get(), 0);
|
|
410
452
|
t.is(db2.prepare('SELECT COUNT(*) FROM transcriptSpans').pluck().get(), 0);
|
|
@@ -417,9 +459,18 @@ test('slow deletion of transcripts', async t => {
|
|
|
417
459
|
t.true(dc.done);
|
|
418
460
|
t.is(dc.cleanups, 0);
|
|
419
461
|
await commit();
|
|
420
|
-
t.
|
|
462
|
+
t.deepEqual(exportLog, []);
|
|
421
463
|
}
|
|
464
|
+
};
|
|
465
|
+
const testSlowTranscriptDeletion = test.macro({
|
|
466
|
+
title(extra = '', { keepTranscripts }) {
|
|
467
|
+
const detail = keepTranscripts ? 'with retention' : 'without retention';
|
|
468
|
+
return `slow deletion of transcripts ${detail}${extra.replace(/.$/, ' $&')}`;
|
|
469
|
+
},
|
|
470
|
+
exec: execSlowTranscriptDeletion,
|
|
422
471
|
});
|
|
472
|
+
test(testSlowTranscriptDeletion, { keepTranscripts: true });
|
|
473
|
+
test(testSlowTranscriptDeletion, { keepTranscripts: false });
|
|
423
474
|
|
|
424
475
|
test('slow deletion without stopUsingTranscript', async t => {
|
|
425
476
|
// slow deletion should work even without stopUsingTranscript
|
|
@@ -438,7 +489,7 @@ test('slow deletion without stopUsingTranscript', async t => {
|
|
|
438
489
|
const t0 = { vatID, startPos: 0, endPos: 2, isCurrent: 0, incarnation: 0 };
|
|
439
490
|
const t2 = { vatID, startPos: 2, endPos: 4, isCurrent: 0, incarnation: 0 };
|
|
440
491
|
const t4 = { vatID, startPos: 4, endPos: 6, isCurrent: 0, incarnation: 1 };
|
|
441
|
-
|
|
492
|
+
t.deepEqual(stripHashes(currentExportData), {
|
|
442
493
|
'transcript.v1.0': t0,
|
|
443
494
|
'transcript.v1.2': t2,
|
|
444
495
|
'transcript.v1.4': t4,
|
|
@@ -556,7 +607,7 @@ test('slow deletion of snapshots', async t => {
|
|
|
556
607
|
// stopUsingLastSnapshot is idempotent
|
|
557
608
|
snapStore.stopUsingLastSnapshot(vatID);
|
|
558
609
|
await commit();
|
|
559
|
-
t.
|
|
610
|
+
t.deepEqual(exportLog, []);
|
|
560
611
|
}
|
|
561
612
|
|
|
562
613
|
// first deletion
|
package/test/export.test.js
CHANGED
|
@@ -47,7 +47,7 @@ const exportTest = test.macro(async (t, mode) => {
|
|
|
47
47
|
// incarnation 0
|
|
48
48
|
ks.transcriptStore.addItem('v1', 'start-worker'); // 0
|
|
49
49
|
ks.transcriptStore.addItem('v1', 'shutdown-worker'); // 1
|
|
50
|
-
ks.transcriptStore.rolloverIncarnation('v1');
|
|
50
|
+
await ks.transcriptStore.rolloverIncarnation('v1');
|
|
51
51
|
const spanHash0 =
|
|
52
52
|
'5bee0f44eca02f23eab03703e84ed2647d5d117fed99e1c30a3b424b7f082ab9';
|
|
53
53
|
|
|
@@ -56,7 +56,7 @@ const exportTest = test.macro(async (t, mode) => {
|
|
|
56
56
|
ks.transcriptStore.addItem('v1', 'delivery1'); // 3
|
|
57
57
|
await ks.snapStore.saveSnapshot('v1', 4, getSnapshotStream(snapshotData));
|
|
58
58
|
ks.transcriptStore.addItem('v1', 'save-snapshot'); // 4
|
|
59
|
-
ks.transcriptStore.rolloverSpan('v1'); // range= 2..5
|
|
59
|
+
await ks.transcriptStore.rolloverSpan('v1'); // range= 2..5
|
|
60
60
|
const spanHash1 =
|
|
61
61
|
'57152efdd7fdf75c03371d2b4f1088d5bf3eae7fe643babce527ff81df38998c';
|
|
62
62
|
|
|
@@ -64,7 +64,7 @@ const exportTest = test.macro(async (t, mode) => {
|
|
|
64
64
|
ks.transcriptStore.addItem('v1', 'delivery2'); // 6
|
|
65
65
|
await ks.snapStore.saveSnapshot('v1', 7, getSnapshotStream(snapshotData));
|
|
66
66
|
ks.transcriptStore.addItem('v1', 'save-snapshot'); // 7
|
|
67
|
-
ks.transcriptStore.rolloverSpan('v1'); // range= 5..8
|
|
67
|
+
await ks.transcriptStore.rolloverSpan('v1'); // range= 5..8
|
|
68
68
|
const spanHash2 =
|
|
69
69
|
'1947001e78e01bd1e773feb22b4ffc530447373b9de9274d5d5fbda3f23dbf2b';
|
|
70
70
|
|
|
@@ -251,7 +251,7 @@ test('export omits pruned span artifacts', async t => {
|
|
|
251
251
|
ks.transcriptStore.addItem('v1', 'delivery1'); // 1
|
|
252
252
|
await ks.snapStore.saveSnapshot('v1', 2, getSnapshotStream(snapshotData));
|
|
253
253
|
ks.transcriptStore.addItem('v1', 'save-snapshot'); // 2
|
|
254
|
-
ks.transcriptStore.rolloverSpan('v1'); // range= 0..3
|
|
254
|
+
await ks.transcriptStore.rolloverSpan('v1'); // range= 0..3
|
|
255
255
|
const spanHash1 =
|
|
256
256
|
'57152efdd7fdf75c03371d2b4f1088d5bf3eae7fe643babce527ff81df38998c';
|
|
257
257
|
// rolloverSpan prunes the contents of the old span
|
|
@@ -87,7 +87,7 @@ async function fakeAVatSnapshot(vat, ks) {
|
|
|
87
87
|
await ks.snapStore.saveSnapshot(vat.vatID, vat.endPos, getSnapshotStream());
|
|
88
88
|
ks.transcriptStore.addItem(vat.vatID, 'save-snapshot');
|
|
89
89
|
vat.endPos += 1;
|
|
90
|
-
ks.transcriptStore.rolloverSpan(vat.vatID);
|
|
90
|
+
await ks.transcriptStore.rolloverSpan(vat.vatID);
|
|
91
91
|
ks.transcriptStore.addItem(vat.vatID, 'load-snapshot');
|
|
92
92
|
vat.endPos += 1;
|
|
93
93
|
}
|