Skip to content

Commit

Permalink
Merge pull request #6442 from Agoric/6433-preload-fewer-vats
Browse files Browse the repository at this point in the history
fix(swingset): only preload maxVatsOnline/2 vats
  • Loading branch information
mergify[bot] authored Oct 11, 2022
2 parents a0be301 + ff696dc commit 194e153
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 9 deletions.
17 changes: 15 additions & 2 deletions packages/SwingSet/src/kernel/vat-warehouse.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-await-in-loop,@jessie.js/no-nested-await */
// @ts-check
import { assert, details as X, quote as q } from '@agoric/assert';
import { isNat } from '@agoric/nat';
Expand Down Expand Up @@ -162,21 +163,33 @@ export function makeVatWarehouse(kernelKeeper, vatLoader, policyOptions) {
/** @param {typeof console.log} logStartup */
async function start(logStartup) {
const recreate = true; // note: PANIC on failure to recreate
const maxPreload = maxVatsOnline / 2;
let numPreloaded = 0;

// NOTE: OPTIMIZATION OPPORTUNITY: replay vats in parallel

// instantiate all static vats
// instantiate all static vats, in lexicographic order, up to the
// maxPreload limit
for (const [name, vatID] of kernelKeeper.getStaticVats()) {
if (numPreloaded >= maxPreload) {
break;
}
logStartup(`provideVatKeeper for vat ${name} as vat ${vatID}`);
// eslint-disable-next-line no-await-in-loop
await ensureVatOnline(vatID, recreate);
numPreloaded += 1;
}

// instantiate all dynamic vats
// then instantiate all dynamic vats, in creation order, also
// subject to maxPreload
for (const vatID of kernelKeeper.getDynamicVats()) {
if (numPreloaded >= maxPreload) {
break;
}
logStartup(`provideVatKeeper for dynamic vat ${vatID}`);
// eslint-disable-next-line no-await-in-loop
await ensureVatOnline(vatID, recreate);
numPreloaded += 1;
}
}

Expand Down
126 changes: 126 additions & 0 deletions packages/SwingSet/test/vat-warehouse/test-preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// eslint-disable-next-line import/order
import { test } from '../../tools/prepare-test-env-ava.js';

// import * as proc from 'child_process';
import tmp from 'tmp';
import { makeSnapStore, makeSnapStoreIO } from '@agoric/swing-store';
import { provideHostStorage } from '../../src/controller/hostStorage.js';
import { initializeSwingset, makeSwingsetController } from '../../src/index.js';

test('only preload maxVatsOnline vats', async t => {
const bpath = new URL('vat-preload-bootstrap.js', import.meta.url).pathname;
const tpath = new URL('vat-preload-extra.js', import.meta.url).pathname;
// this combination of config and initOpts means we have only two
// initial vats: v1-bootstrap and v2-vatAdmin
const config = {
defaultManagerType: 'xs-worker',
defaultReapInterval: 'never',
bootstrap: 'bootstrap',
vats: {
bootstrap: {
sourceSpec: bpath,
},
},
bundles: { extra: { sourceSpec: tpath } },
};
const initOpts = {
addComms: false,
addVattp: false,
addTimer: false,
};
const argv = [];

const snapstorePath = tmp.dirSync({ unsafeCleanup: true }).name;
const snapStore = makeSnapStore(snapstorePath, makeSnapStoreIO());
const hostStorage = { snapStore, ...provideHostStorage() };

await initializeSwingset(config, argv, hostStorage, initOpts);

/* we use vat-warehouse's built-in instrumentation, but I'll leave
* this alternative here in case it's ever useful
// this lets us snoop on which workers are still alive
const workers = new Map();
function spawn(...args) {
const worker = proc.spawn(...args);
const name = args[1][0]; // e.g. 'v1:bootstrap'
workers.set(name, worker);
return worker;
}
const isAlive = name =>
workers.has(name) && workers.get(name).exitCode === null;
*/

// we'll create a 'canary' vat, and ten extras
const all = ['bootstrap', 'vatAdmin', 'canary'];
for (let count = 0; count < 10; count += 1) {
all.push(`extra-${count}`);
}

function areLiving(c, ...expected) {
expected = new Set(expected);
const living = c.getStatus().activeVats.map(info => info.options.name);
for (const name of all) {
t.is(living.indexOf(name) !== -1, expected.has(name), name);
}
}

const maxVatsOnline = 10;
const warehousePolicy = { maxVatsOnline };
const runtimeOptions = { warehousePolicy /* , spawn */ };

const c1 = await makeSwingsetController(hostStorage, null, runtimeOptions);
c1.pinVatRoot('bootstrap');
await c1.run();
areLiving(c1, 'bootstrap', 'vatAdmin');

// launch v3-canary
c1.queueToVatRoot('bootstrap', 'launchCanary', []);
await c1.run();
areLiving(c1, 'bootstrap', 'vatAdmin', 'canary');

// then launch v4-extra-0 through v13-extra-9, which knocks the
// v3-canary and v4/v5 workers offline
c1.queueToVatRoot('bootstrap', 'launchExtra', []);
await c1.run();
areLiving(
c1,
'bootstrap',
'vatAdmin',
// 'canary', -- evicted
// 'extra-0', -- evicted
// 'extra-1', -- evicted
'extra-2',
'extra-3',
'extra-4',
'extra-5',
'extra-6',
'extra-7',
'extra-8',
'extra-9',
);

// shut down that controller, then start a new one
await c1.shutdown();
const c2 = await makeSwingsetController(hostStorage, null, runtimeOptions);

// we only preload maxVatsOnline/2 vats: the static vats in
// lexicographic order (e.g. 1,10,11,2,3,4, except here we only have
// v1 and v2 as static vats), and the dynamic vats in creation order
areLiving(c2, 'bootstrap', 'vatAdmin', 'canary', 'extra-0', 'extra-1');

// send a message to an offline extra vat, assert it came online
c2.queueToVatRoot('bootstrap', 'ping', ['extra-6']);
await c2.run();

areLiving(
c2,
'bootstrap',
'vatAdmin',
'canary',
'extra-0',
'extra-1',
'extra-6', // now online
);

await c2.shutdown();
});
24 changes: 17 additions & 7 deletions packages/SwingSet/test/vat-warehouse/test-warehouse.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,26 @@ import { test } from '../../tools/prepare-test-env-ava.js';
import fs from 'fs';
import tmp from 'tmp';
import { initSwingStore } from '@agoric/swing-store';
import { loadBasedir, buildVatController } from '../../src/index.js';
import { buildVatController } from '../../src/index.js';
import { makeLRU } from '../../src/kernel/vat-warehouse.js';

async function makeController(managerType, runtimeOptions, snapshotInterval) {
const config = await loadBasedir(new URL('./', import.meta.url).pathname);
if (snapshotInterval) {
config.snapshotInterval = snapshotInterval;
}
assert(config.vats);
config.vats.target.creationOptions = { managerType, enableDisavow: true };
const bpath = new URL('bootstrap.js', import.meta.url).pathname;
const tpath = new URL('vat-target.js', import.meta.url).pathname;
const config = {
snapshotInterval,
bootstrap: 'bootstrap',
vats: {
bootstrap: {
sourceSpec: bpath,
},
target: {
creationOptions: { managerType },
sourceSpec: tpath,
},
},
};

config.vats.target2 = config.vats.target;
config.vats.target3 = config.vats.target;
config.vats.target4 = config.vats.target;
Expand Down
42 changes: 42 additions & 0 deletions packages/SwingSet/test/vat-warehouse/vat-preload-bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable no-await-in-loop */
import { E } from '@endo/eventual-send';
import { Far } from '@endo/marshal';

export function buildRootObject() {
const extras = new Map(); // count -> root
let vatAdminSvc;
let bcap;

async function start(name) {
const opts = { name, vatParameters: { name } };
const { root } = await E(vatAdminSvc).createVat(bcap, opts);
return root;
}

return Far('root', {
async bootstrap(vats, devices) {
vatAdminSvc = await E(vats.vatAdmin).createVatAdminService(
devices.vatAdmin,
);
bcap = await E(vatAdminSvc).getNamedBundleCap('extra');
},

async launchCanary() {
const canary = await start('canary');
extras.set('canary', canary);
},

async launchExtra() {
for (let count = 0; count < 10; count += 1) {
const name = `extra-${count}`;
const root = await start(name);
extras.set(name, root);
}
},

ping(which) {
console.log(`ping ${which}`);
return E(extras.get(which)).ping();
},
});
}
8 changes: 8 additions & 0 deletions packages/SwingSet/test/vat-warehouse/vat-preload-extra.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Far } from '@endo/marshal';

export function buildRootObject(_vatPowers, _vatParameters) {
// console.log(`extra: ${vatParameters.name}`);
return Far('root', {
ping() {},
});
}

0 comments on commit 194e153

Please sign in to comment.