Skip to content

Commit

Permalink
adjust swingStore import expectations
Browse files Browse the repository at this point in the history
`importSwingStore()` has a consistency check which, for each vat,
compares the `endPos` of the current snapshot against the `startPos`
of the current transcript span.

Now that the `save-snapshot` pseudo-entry is the last one of a
transcript, these two values are expected to differ by one.

Fixed the check, updated swingstore's test-exportImport.js to behave
more like the real swingset, and added a new swingset test to exercise
the whole run/export/import/run cycle, so we can discover this sort of
thing locally in swingset's tests. Previously this only caused a
failure of the "deployment-test", which doesn't run until all other CI
has passed and the PR is at the front of the merge queue.
  • Loading branch information
warner committed Apr 26, 2023
1 parent c2edc8a commit b91f3ad
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 26 deletions.
117 changes: 117 additions & 0 deletions packages/SwingSet/test/transcript/test-state-sync-reload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import test from 'ava';
import '@endo/init/debug.js';
import tmp from 'tmp';
import {
initSwingStore,
makeSwingStoreExporter,
importSwingStore,
} from '@agoric/swing-store';
import { initializeSwingset, makeSwingsetController } from '../../src/index.js';
import { buildKernelBundle } from '../../src/controller/initializeSwingset.js';
import { kunser } from '../../src/lib/kmarshal.js';

/**
* @param {string} [prefix]
* @returns {Promise<[string, () => void]>}
*/
const tmpDir = prefix =>
new Promise((resolve, reject) => {
tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => {
if (err) {
reject(err);
} else {
resolve([name, removeCallback]);
}
});
});

const bfile = name => new URL(name, import.meta.url).pathname;

test.before(async t => {
const runtimeOptions = { kernelBundle: await buildKernelBundle() };
t.context.data = { runtimeOptions };
});

test('state-sync reload', async t => {
const [dbDir, cleanup] = await tmpDir('testdb');
t.teardown(cleanup);

const config = {
snapshotInitial: 2,
// the new pseudo-deliveries ('initialize-worker',
// 'save-snapshot', and 'load-snapshot' all count against the
// snapshotInterval. So setting it to 7 will get us 5 actual
// deliveries between the two snapshot events.
snapshotInterval: 7,
defaultReapInterval: 'never',
defaultManagerType: 'xsnap',
bundles: {
bundle: { sourceSpec: bfile('vat-bootstrap-transcript.js') },
},
vats: {
bootstrap: { bundleName: 'bundle' },
},
bootstrap: 'bootstrap',
};

const { kernelStorage, hostStorage } = initSwingStore(dbDir);
const { commit } = hostStorage;
const initOpts = { addComms: false, addVattp: false, addTimer: false };
await initializeSwingset(config, [], kernelStorage, initOpts);
commit();

const { runtimeOptions } = t.context.data;
const c1 = await makeSwingsetController(kernelStorage, {}, runtimeOptions);
t.teardown(c1.shutdown);
c1.pinVatRoot('bootstrap');
// const vatID = c1.vatNameToID('bootstrap');

const doCount = async c => {
const kpid = c.queueToVatRoot('bootstrap', 'count', [], 'panic');
await c.run();
t.is(c.kpStatus(kpid), 'fulfilled');
return kunser(c.kpResolution(kpid));
};

// this should result in a final snapshot on d19, then a short
// current transcript of just d20 = 'load-snapshot' and d21 =
// 'message' (count -> 7)
for (let i = 1; i <= 7; i += 1) {
// eslint-disable-next-line no-await-in-loop
const res = await doCount(c1);
t.is(res, i);
}

commit();
await c1.shutdown();

const exporter = makeSwingStoreExporter(dbDir);
const exportData = new Map();
for await (const [k, v] of exporter.getExportData()) {
if (v !== undefined) {
exportData.set(k, v);
} else {
exportData.delete(k);
}
}
const artifacts = new Map();
for await (const name of exporter.getArtifactNames()) {
artifacts.set(name, exporter.getArtifact(name));
}

const datasetExporter = {
getExportData: () => [...exportData.entries()],
getArtifactNames: () => [...artifacts.keys()],
getArtifact: name => artifacts.get(name),
close: () => 0,
};
const ss2 = await importSwingStore(datasetExporter);
const c2 = await makeSwingsetController(
ss2.kernelStorage,
{},
runtimeOptions,
);
t.teardown(c2.shutdown);

t.is(await doCount(c2), 8);
});
5 changes: 5 additions & 0 deletions packages/SwingSet/test/transcript/vat-bootstrap-transcript.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { E, Far } from '@endo/far';

export const buildRootObject = harden(() => {
let vas;
let counter = 0;
return Far('root', {
bootstrap: async (vats, devices) => {
vas = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
Expand All @@ -11,5 +12,9 @@ export const buildRootObject = harden(() => {
return root;
},
nothing: () => {},
count: () => {
counter += 1;
return counter;
},
});
});
2 changes: 1 addition & 1 deletion packages/swing-store/src/swingStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ export async function importSwingStore(exporter, dirPath = null, options = {}) {
transcriptInfo.startPos === 0 ||
Fail`missing current snapshot for vat ${q(vatID)}`;
} else {
snapshotInfo.endPos === transcriptInfo.startPos ||
snapshotInfo.endPos + 1 === transcriptInfo.startPos ||
Fail`current transcript for vat ${q(vatID)} doesn't go with snapshot`;
fetchedArtifacts.add(vatInfo.snapshotKey);
}
Expand Down
54 changes: 29 additions & 25 deletions packages/swing-store/test/test-exportImport.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ async function fakeAVatSnapshot(vat, ks) {
`snapshot of vat ${vat.vatID} as of ${vat.endPos}`,
);
});
ks.transcriptStore.addItem(vat.vatID, 'save-snapshot');
vat.endPos += 1;
ks.transcriptStore.rolloverSpan(vat.vatID);
ks.transcriptStore.addItem(vat.vatID, 'load-snapshot');
vat.endPos += 1;
}

const compareElems = (a, b) => a[0].localeCompare(b[0]);
Expand Down Expand Up @@ -267,34 +271,34 @@ async function testExportImport(
'{"vatID":"vatA","endPos":2,"hash":"6c7e452ee3eaec849c93234d933af4300012e4ff161c328d3c088ec3deef76a6","inUse":0}',
],
[
'snapshot.vatA.6',
'{"vatID":"vatA","endPos":6,"hash":"36afc9e2717c395759e308c4a877d491f967e9768d73520bde758ff4fac5d8b9","inUse":1}',
'snapshot.vatA.8',
'{"vatID":"vatA","endPos":8,"hash":"f010b0f3e7d48e378cc59b678388d2aae6667c4ff233454a6d8f08caebfda681","inUse":1}',
],
['snapshot.vatA.current', 'snapshot.vatA.6'],
['snapshot.vatA.current', 'snapshot.vatA.8'],
[
'snapshot.vatB.4',
'{"vatID":"vatB","endPos":4,"hash":"afd477014db678fbc1aa58beab50f444deb653b8cc8e8583214a363fd12ed57a","inUse":1}',
],
['snapshot.vatB.current', 'snapshot.vatB.4'],
[
'transcript.vatA.0',
'{"vatID":"vatA","startPos":0,"endPos":2,"hash":"ea8ac1a751712ad66e4a9182adc65afe9bb0f4cd0ee0b828c895c63fbd2e3157","isCurrent":0,"incarnation":0}',
'{"vatID":"vatA","startPos":0,"endPos":3,"hash":"86a0ba16ef38704dea3d04f8d8e4b104f162e6396249bf153f3808b0f9c0e36e","isCurrent":0,"incarnation":0}',
],
[
'transcript.vatA.2',
'{"vatID":"vatA","startPos":2,"endPos":6,"hash":"88f299ca67b8acdf6023a83bb8e899af5adcf3271c7a1a2a495dcd6f1fbaac9f","isCurrent":0,"incarnation":0}',
'transcript.vatA.3',
'{"vatID":"vatA","startPos":3,"endPos":9,"hash":"e71df351455b00971357c15d86e35556d7ee77a8a13149bd06bff80822238daa","isCurrent":0,"incarnation":0}',
],
[
'transcript.vatA.current',
'{"vatID":"vatA","startPos":6,"endPos":8,"hash":"fe5d692b24a32d53bf617ba9ed3391b60c36a402c70a07a6aa984fc316e4efcc","isCurrent":1,"incarnation":0}',
'{"vatID":"vatA","startPos":9,"endPos":12,"hash":"17ef3fbe1a34ba8c8145fe21c16bee5ef6ec7b9132b740cc848849b67f449320","isCurrent":1,"incarnation":0}',
],
[
'transcript.vatB.0',
'{"vatID":"vatB","startPos":0,"endPos":4,"hash":"41dbf60cdec066106c7030517cb9f9f34a50fe2259705cf5fdbdd0b39ae12e46","isCurrent":0,"incarnation":0}',
'{"vatID":"vatB","startPos":0,"endPos":5,"hash":"0de691725ef5d53c8016f6a064e1106c25e29f659d13bb8a72fcc21a5d1cd67c","isCurrent":0,"incarnation":0}',
],
[
'transcript.vatB.current',
'{"vatID":"vatB","startPos":4,"endPos":8,"hash":"34fa09207bfb7af5fc3e65acb07f13b60834d0fbd2c6b9708f794c4397bd865d","isCurrent":1,"incarnation":0}',
'{"vatID":"vatB","startPos":5,"endPos":10,"hash":"69f5d2db6891b35fb992b9c42fc39db15f1bb8066fada3d697826e703fad994b","isCurrent":1,"incarnation":0}',
],
]);

Expand All @@ -318,7 +322,7 @@ async function testExportImport(
});
} catch (e) {
if (failureMode === 'transcript') {
t.is(e.message, 'artifact "transcript.vatA.0.2" is not available');
t.is(e.message, 'artifact "transcript.vatA.0.3" is not available');
return;
} else if (failureMode === 'snapshot') {
t.is(e.message, 'artifact "snapshot.vatA.2" is not available');
Expand All @@ -337,31 +341,31 @@ async function testExportImport(
}

const expectedCurrentArtifacts = [
'snapshot.vatA.6',
'snapshot.vatA.8',
'snapshot.vatB.4',
'transcript.vatA.6.8',
'transcript.vatB.4.8',
'transcript.vatA.9.12',
'transcript.vatB.5.10',
];

const expectedArchivalArtifacts = [
'snapshot.vatA.6',
'snapshot.vatA.8',
'snapshot.vatB.4',
'transcript.vatA.0.2',
'transcript.vatA.2.6',
'transcript.vatA.6.8',
'transcript.vatB.0.4',
'transcript.vatB.4.8',
'transcript.vatA.0.3',
'transcript.vatA.3.9',
'transcript.vatA.9.12',
'transcript.vatB.0.5',
'transcript.vatB.5.10',
];

const expectedDebugArtifacts = [
'snapshot.vatA.6',
'snapshot.vatA.8',
'snapshot.vatB.4',
'snapshot.vatA.2',
'transcript.vatA.0.2',
'transcript.vatA.2.6',
'transcript.vatA.6.8',
'transcript.vatB.0.4',
'transcript.vatB.4.8',
'transcript.vatA.0.3',
'transcript.vatA.3.9',
'transcript.vatA.9.12',
'transcript.vatB.0.5',
'transcript.vatB.5.10',
];

const C = 'current';
Expand Down

0 comments on commit b91f3ad

Please sign in to comment.