Skip to content

Commit

Permalink
feat(swingset): get a small upgrade to work
Browse files Browse the repository at this point in the history
This enables a partial vat upgrade test to work. It makes a lot of
assumptions and is missing a lot of functionality, but it's a concrete
step forward. Limitations include:

* if the stopVat or startVat fail somehow, the vat will be left in an
unusable state, and the caller will not be notified
* stopVat does not clean up the non-durable objects
* stopVat does not reject the lingering promises
* startVat does not assert that userspace reconnects all Kinds
* nothing about reconnecting Kinds is tested at all
* nothing about remaining durable objects is tested at all
* the kernel does not unsubscribe the vat from remaining promises
* transcript pointers are cleared, but the contents are not deleted
* the snapshot decref/deletion is not tested
* stopVat is metered, but probably shouldn't be
* startVat metering needs more thought

refs #1848
  • Loading branch information
warner committed Mar 28, 2022
1 parent 84ff2e3 commit 25ecef5
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 25 deletions.
71 changes: 53 additions & 18 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,33 +640,68 @@ export default function buildKernel(
*/
async function processUpgradeVat(message) {
assert(vatAdminRootKref, `initializeKernel did not set vatAdminRootKref`);
// const { bundleID } = message;
const { vatID, upgradeID, vatParameters } = message;
const { vatID, upgradeID, bundleID, vatParameters } = message;
insistCapData(vatParameters);

// eslint-disable-next-line no-use-before-define
assert(vatWarehouse.lookup(vatID));
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
/** @type { import('../types-external.js').KernelDeliveryStopVat } */
const kd = harden(['stopVat']);
const kd1 = harden(['stopVat']);
// eslint-disable-next-line no-use-before-define
const vd = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd);
const status = await deliverAndLogToVat(vatID, kd, vd);
const vd1 = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd1);
const status1 = await deliverAndLogToVat(vatID, kd1, vd1);
if (status1.terminate) {
// TODO: if stopVat fails, stop now, arrange for everything to
// be unwound. TODO: we need to notify caller about the failure
console.log(`-- upgrade-vat stopVat failed: ${status1.terminate}`);
}

// TODO: if status.terminate then abort the crank, discard the
// upgrade event, and arrange to use vatUpgradeCallback to inform
// the caller
// stop the worker, delete the transcript and any snapshot
// eslint-disable-next-line no-use-before-define
await vatWarehouse.destroyWorker(vatID);
const source = { bundleID };
const { options } = vatKeeper.getSourceAndOptions();
vatKeeper.setSourceAndOptions(source, options);
// TODO: decref the bundleID once setSourceAndOptions increfs it

// for now, all attempts to upgrade will fail
// pause, take a deep breath, appreciate this moment of silence
// between the old and the new. this moment will never come again.

// TODO: decref the bundleID and vatParameters.slots
const args = {
body: JSON.stringify([upgradeID, false, { error: `not implemented` }]),
slots: [],
};
queueToKref(vatAdminRootKref, 'vatUpgradeCallback', args, 'logFailure');
// if stopVat fails, we want everything to be unwound. TODO: we
// need to notify caller about the failure
return { ...status, discardFailedDelivery: true };
// deliver a startVat with the new vatParameters
/** @type { import('../types-external.js').KernelDeliveryStartVat } */
const kd2 = harden(['startVat', vatParameters]);
// eslint-disable-next-line no-use-before-define
const vd2 = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd2);
// decref vatParameters now that translation did incref
for (const kref of vatParameters.slots) {
kernelKeeper.decrementRefCount(kref, 'upgrade-vat-event');
}
const status2 = await deliverAndLogToVat(vatID, kd2, vd2);
if (status2.terminate) {
console.log(`-- upgrade-vat startVat failed: ${status2.terminate}`);
}

if (status1.terminate || status2.terminate) {
// TODO: if status.terminate then abort the crank, discard the
// upgrade event, and arrange to use vatUpgradeCallback to inform
// the caller
console.log(`-- upgrade-vat delivery failed`);

// TODO: this is the message we want to send on failure, but we
// need to queue it after the crank was unwound, else this
// message will be unwound too
const args = {
body: JSON.stringify([upgradeID, false, { error: `not implemented` }]),
slots: [],
};
queueToKref(vatAdminRootKref, 'vatUpgradeCallback', args, 'logFailure');
} else {
const args = { body: JSON.stringify([upgradeID, true]), slots: [] };
queueToKref(vatAdminRootKref, 'vatUpgradeCallback', args, 'logFailure');
}
// return { ...status1, ...status2, discardFailedDelivery: true };
return {};
}

function legibilizeMessage(message) {
Expand Down
22 changes: 22 additions & 0 deletions packages/SwingSet/src/kernel/state/vatKeeper.js
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,27 @@ export function makeVatKeeper(
return true;
}

function removeSnapshotAndTranscript() {
const skey = `local.${vatID}.lastSnapshot`;
const epkey = `${vatID}.t.endPosition`;
if (snapStore) {
const notation = kvStore.get(skey);
if (notation) {
const { snapshotID } = JSON.parse(notation);
if (removeFromSnapshot(snapshotID) === 0) {
// TODO: if we roll back (because the upgrade failed), we must
// not really delete the snapshot
snapStore.prepareToDelete(snapshotID);
}
kvStore.delete(skey);
}
}
// TODO: same rollback concern
// TODO: streamStore.deleteStream(transcriptStream);
const newStart = streamStore.STREAM_START;
kvStore.set(epkey, `${JSON.stringify(newStart)}`);
}

function vatStats() {
function getCount(key, first) {
const id = Nat(BigInt(getRequired(key)));
Expand Down Expand Up @@ -638,5 +659,6 @@ export function makeVatKeeper(
saveSnapshot,
getLastSnapshot,
removeFromSnapshot,
removeSnapshotAndTranscript,
});
}
9 changes: 9 additions & 0 deletions packages/SwingSet/src/kernel/vat-warehouse.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,13 @@ export function makeVatWarehouse(kernelKeeper, vatLoader, policyOptions) {
}
}

async function destroyWorker(vatID) {
// stop any existing worker, delete transcript and any snapshot
await evict(vatID);
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
vatKeeper.removeSnapshotAndTranscript();
}

// mostly used by tests, only needed with thread/process-based workers
function shutdown() {
const work = Array.from(ephemeral.vats.values(), ({ manager }) =>
Expand All @@ -378,6 +385,8 @@ export function makeVatWarehouse(kernelKeeper, vatLoader, policyOptions) {
deliverToVat,
maybeSaveSnapshot,

destroyWorker,

// mostly for testing?
activeVatsInfo: () =>
[...ephemeral.vats].map(([id, { options }]) => ({ id, options })),
Expand Down
2 changes: 2 additions & 0 deletions packages/SwingSet/src/liveslots/liveslots.js
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,8 @@ function build(
assert(didStartVat);
assert(!didStopVat);
didStopVat = true;
// eslint-disable-next-line no-use-before-define
await bringOutYourDead();
// empty for now
}

Expand Down
10 changes: 3 additions & 7 deletions packages/SwingSet/test/upgrade/test-upgrade.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,9 @@ async function testUpgrade(t, defaultManagerType) {

// upgrade should work
const [v2status, v2capdata] = await run('upgradeV2', []);
// t.is(v2status, 'fulfilled');
// t.deepEqual(JSON.parse(v2capdata.body), ['v2', { youAre: 'v2', marker }]);
// t.deepEqual(v2capdata.slots, [markerKref]);

// but for now, upgrade is just a stub
t.is(v2status, 'rejected');
t.deepEqual(JSON.parse(v2capdata.body), { error: 'not implemented' });
t.is(v2status, 'fulfilled');
t.deepEqual(JSON.parse(v2capdata.body), ['v2', { youAre: 'v2', marker }]);
t.deepEqual(v2capdata.slots, [markerKref]);
}

test('vat upgrade - local', async t => {
Expand Down

0 comments on commit 25ecef5

Please sign in to comment.