Skip to content

Commit

Permalink
fix: remove one layer of caching (the mailbox state)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Aug 27, 2020
1 parent bc765da commit 50b1d7e
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 86 deletions.
54 changes: 40 additions & 14 deletions packages/SwingSet/src/devices/mailbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,36 @@ import Nat from '@agoric/nat';
// replace it with one that tracks which parts of the state have been
// modified, to build more efficient Merkle proofs.

export function buildMailboxStateMap() {
const state = harden(new Map());
export function importMailbox(data, inout = {}) {
const outbox = new Map();
data.outbox.forEach(m => {
outbox.set(Nat(m[0]), m[1]);
});
inout.ack = data.ack;
inout.outbox = outbox;
return inout;
}

export function exportMailbox(inout) {
const messages = [];
inout.outbox.forEach((body, msgnum) => {
messages.push([msgnum, body]);
});
messages.sort((a, b) => a[0] - b[0]);
return {
ack: inout.ack,
outbox: messages,
};
}

export function buildMailboxStateMap(state = harden(new Map())) {
function getOrCreatePeer(peer) {
if (!state.has(peer)) {
state.set(peer, { outbox: harden(new Map()), inboundAck: 0 });
const inout = {
outbox: harden(new Map()),
ack: 0,
};
state.set(peer, inout);
}
return state.get(peer);
}
Expand All @@ -92,18 +116,17 @@ export function buildMailboxStateMap() {
}

function setAcknum(peer, msgnum) {
getOrCreatePeer(`${peer}`).inboundAck = Nat(msgnum);
getOrCreatePeer(`${peer}`).ack = Nat(msgnum);
}

function exportToData() {
const data = {};
state.forEach((inout, peer) => {
const messages = [];
inout.outbox.forEach((body, msgnum) => {
messages.push([msgnum, body]);
});
messages.sort((a, b) => a[0] - b[0]);
data[peer] = { outbox: messages, inboundAck: inout.inboundAck };
const exported = exportMailbox(inout);
data[peer] = {
inboundAck: inout.ack,
outbox: exported.outbox,
};
});
return harden(data);
}
Expand All @@ -115,10 +138,13 @@ export function buildMailboxStateMap() {
for (const peer of Object.getOwnPropertyNames(data)) {
const inout = getOrCreatePeer(peer);
const d = data[peer];
d.outbox.forEach(m => {
inout.outbox.set(Nat(m[0]), m[1]);
});
inout.inboundAck = d.inboundAck;
importMailbox(
{
ack: d.inboundAck,
outbox: d.outbox,
},
inout,
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cosmic-swingset/lib/ag-solo/chain-cosmos-sdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ export async function connectToChain(
log(`helper said: ${stdout}`);
try {
// Try to parse the stdout.
return JSON.parse(JSON.parse(JSON.parse(stdout).value));
return JSON.parse(JSON.parse(stdout).value);
} catch (e) {
log(`failed to parse output:`, e);
}
Expand Down
6 changes: 3 additions & 3 deletions packages/cosmic-swingset/lib/block-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ export default function makeBlockManager({

// Always commit all the keeper state live.
const start = Date.now();
const { mailboxSize } = saveChainState();
const mbTime = Date.now() - start;
saveChainState();
const chainTime = Date.now() - start;

// Advance our saved state variables.
savedActions = currentActions;
Expand All @@ -167,7 +167,7 @@ export default function makeBlockManager({
const saveTime = Date.now() - start2;

log.debug(
`wrote SwingSet checkpoint (mailbox=${mailboxSize}), [run=${runTime}ms, mb=${mbTime}ms, save=${saveTime}ms]`,
`wrote SwingSet checkpoint [run=${runTime}ms, chainSave=${chainTime}ms, outsideSave=${saveTime}ms]`,
);
currentActions = [];

Expand Down
109 changes: 66 additions & 43 deletions packages/cosmic-swingset/lib/chain-main.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,69 @@
import stringify from '@agoric/swingset-vat/src/kernel/json-stable-stringify';
import {
importMailbox,
exportMailbox,
} from '@agoric/swingset-vat/src/devices/mailbox';

import { launch } from './launch-chain';
import makeBlockManager from './block-manager';

const AG_COSMOS_INIT = 'AG_COSMOS_INIT';

const makeChainStorage = (call, prefix = '', imp = x => x, exp = x => x) => {
let cache = new Map();
let changedKeys = new Set();
const storage = {
has(key) {
// It's more efficient just to get the value.
const val = storage.get(key);
return !!val;
},
set(key, obj) {
if (cache.get(key) !== obj) {
cache.set(key, obj);
changedKeys.add(key);
}
},
get(key) {
if (cache.has(key)) {
// Our cache has the value.
return cache.get(key);
}
const retStr = call(stringify({ method: 'get', key: `${prefix}${key}` }));
const ret = JSON.parse(retStr);
const value = ret && JSON.parse(ret);
// console.log(` value=${value}`);
const obj = value && imp(value);
cache.set(key, obj);
// We need to add this in case the caller mutates the state, as in
// mailbox.js, which mutates on basically every get.
changedKeys.add(key);
return obj;
},
commit() {
for (const key of changedKeys.keys()) {
const obj = cache.get(key);
const value = stringify(exp(obj));
call(
stringify({
method: 'set',
key: `${prefix}${key}`,
value,
}),
);
}
// Reset our state.
storage.abort();
},
abort() {
// Just reset our state.
cache = new Map();
changedKeys = new Set();
},
};
return storage;
};

export default async function main(progname, args, { path, env, agcc }) {
const portNums = {};

Expand Down Expand Up @@ -118,49 +177,13 @@ export default async function main(progname, args, { path, env, agcc }) {
// so the 'externalStorage' object can close over the single mutable
// instance, and we update the 'portNums.storage' value each time toSwingSet is called
async function launchAndInitializeSwingSet() {
// this object is used to store the mailbox state. we only ever use
// key='mailbox'
const mailboxStorage = {
has(key) {
// x/swingset/storage.go returns "true" or "false"
const retStr = chainSend(
portNums.storage,
stringify({ method: 'has', key }),
);
const ret = JSON.parse(retStr);
if (Boolean(ret) !== ret) {
throw new Error(`chainSend(has) returned ${ret} not Boolean`);
}
return ret;
},
set(key, value) {
if (value !== `${value}`) {
throw new Error(
`golang storage API only takes string values, not '${JSON.stringify(
value,
)}'`,
);
}
const encodedValue = stringify(value);
chainSend(
portNums.storage,
stringify({ method: 'set', key, value: encodedValue }),
);
},
get(key) {
const retStr = chainSend(
portNums.storage,
stringify({ method: 'get', key }),
);
// console.log(`s.get(${key}) retstr=${retstr}`);
const encodedValue = JSON.parse(retStr);
// console.log(` encodedValue=${encodedValue}`);
const value = JSON.parse(encodedValue);
// console.log(` value=${value}`);
return value;
},
};

// this object is used to store the mailbox state.
const mailboxStorage = makeChainStorage(
msg => chainSend(portNums.storage, msg),
'mailbox.',
importMailbox,
exportMailbox,
);
function doOutboundBridge(dstID, obj) {
const portNum = portNums[dstID];
if (portNum === undefined) {
Expand Down
31 changes: 6 additions & 25 deletions packages/cosmic-swingset/lib/launch-chain.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import path from 'path';
import anylogger from 'anylogger';

import djson from 'deterministic-json';
import {
buildMailbox,
buildMailboxStateMap,
Expand All @@ -18,7 +17,7 @@ const log = anylogger('launch-chain');
const SWING_STORE_META_KEY = 'cosmos/meta';

async function buildSwingset(
mailboxState,
mailboxStorage,
bridgeOutbound,
storage,
vatsDir,
Expand All @@ -30,8 +29,7 @@ async function buildSwingset(
if (config === null) {
config = loadBasedir(vatsDir);
}
const mbs = buildMailboxStateMap();
mbs.populateFromData(mailboxState);
const mbs = buildMailboxStateMap(mailboxStorage);
const timer = buildTimer();
const mb = buildMailbox(mbs);
const bd = buildBridge(bridgeOutbound);
Expand All @@ -48,7 +46,7 @@ async function buildSwingset(
await controller.run();

const bridgeInbound = bd.deliverInbound;
return { controller, mb, mbs, bridgeInbound, timer };
return { controller, mb, bridgeInbound, timer };
}

export async function launch(
Expand All @@ -62,11 +60,6 @@ export async function launch(
) {
log.info('Launching SwingSet kernel');

log(`checking for saved mailbox state`, mailboxStorage.has('mailbox'));
const mailboxState = mailboxStorage.has('mailbox')
? JSON.parse(mailboxStorage.get('mailbox'))
: {};

const tempdir = path.resolve(kernelStateDBDir, 'check-lmdb-tempdir');
const { openSwingStore } = getBestSwingStore(tempdir);
const { storage, commit } = openSwingStore(kernelStateDBDir);
Expand All @@ -76,8 +69,8 @@ export async function launch(
return doOutboundBridge(dstID, obj);
}
log.debug(`buildSwingset`);
const { controller, mb, mbs, bridgeInbound, timer } = await buildSwingset(
mailboxState,
const { controller, mb, bridgeInbound, timer } = await buildSwingset(
mailboxStorage,
bridgeOutbound,
storage,
vatsDir,
Expand All @@ -86,20 +79,8 @@ export async function launch(
);

function saveChainState() {
// now check mbs
const newState = mbs.exportToData();
const newData = djson.stringify(newState);

// Save the mailbox state.
for (const peer of Object.getOwnPropertyNames(newState)) {
const data = {
outbox: newState[peer].outbox,
ack: newState[peer].inboundAck,
};
mailboxStorage.set(`mailbox.${peer}`, djson.stringify(data));
}
mailboxStorage.set('mailbox', newData);
return { mailboxSize: newData.length };
mailboxStorage.commit();
}

function saveOutsideState(savedHeight, savedActions) {
Expand Down

0 comments on commit 50b1d7e

Please sign in to comment.