Skip to content

Commit

Permalink
feat: Add consensus-independent vat snapshot archiving configuration …
Browse files Browse the repository at this point in the history
…to AG_COSMOS_INIT

Fixes #10036
  • Loading branch information
gibson042 committed Sep 11, 2024
1 parent f1586df commit ffc594f
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 21 deletions.
13 changes: 13 additions & 0 deletions golang/cosmos/x/swingset/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
const (
ConfigPrefix = "swingset"
FlagSlogfile = ConfigPrefix + ".slogfile"
FlagVatSnapshotArchiveDir = ConfigPrefix + ".vat-snapshot-archive-dir"
FlagVatTranscriptArchiveDir = ConfigPrefix + ".vat-transcript-archive-dir"

SnapshotRetentionOptionDebug = "debug"
Expand Down Expand Up @@ -75,6 +76,9 @@ vat-snapshot-retention = "{{ .Swingset.VatSnapshotRetention }}"
# otherwise "operational")
vat-transcript-retention = "{{ .Swingset.VatTranscriptRetention }}"
# Archival of gzipped vat snapshots.
vat-snapshot-archive-dir = "{{ .Swingset.VatSnapshotArchiveDir }}"
# Archival of historical (i.e., closed) vat transcript spans to gzipped files.
vat-transcript-archive-dir = "{{ .Swingset.VatTranscriptArchiveDir }}"
`
Expand Down Expand Up @@ -111,6 +115,9 @@ type SwingsetConfig struct {
// "nothing", otherwise "operational")
VatTranscriptRetention string `mapstructure:"vat-transcript-retention" json:"vatTranscriptRetention,omitempty"`

// VatSnapshotArchiveDir controls archival of gzipped vat snapshots.
VatSnapshotArchiveDir string `mapstructure:"vat-snapshot-archive-dir" json:"vatSnapshotArchiveDir,omitempty"`

// VatTranscriptArchiveDir controls archival of historical (i.e., closed) vat
// transcript spans to gzipped files.
VatTranscriptArchiveDir string `mapstructure:"vat-transcript-archive-dir" json:"vatTranscriptArchiveDir,omitempty"`
Expand Down Expand Up @@ -210,6 +217,12 @@ func SwingsetConfigFromViper(resolvedConfig servertypes.AppOptions) (*SwingsetCo
}
ssConfig.SlogFile = resolvedSlogFile

resolvedSnapshotDir, err := resolvePath(ssConfig.VatSnapshotArchiveDir, FlagVatSnapshotArchiveDir)
if err != nil {
return nil, err
}
ssConfig.VatSnapshotArchiveDir = resolvedSnapshotDir

resolvedTranscriptDir, err := resolvePath(ssConfig.VatTranscriptArchiveDir, FlagVatTranscriptArchiveDir)
if err != nil {
return nil, err
Expand Down
3 changes: 3 additions & 0 deletions packages/SwingSet/test/snapshots/xsnap-store.test.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Generated by [AVA](https://avajs.dev).
> initial snapshot
{
archiveWriteSeconds: undefined,
compressSeconds: 0,
dbSaveSeconds: 0,
hash: 'bee3b82eebdde4c5c3774fb95b7efe88382f7dc4afab90b4e0e58add54d6b81c',
Expand All @@ -18,6 +19,7 @@ Generated by [AVA](https://avajs.dev).
> after SES boot - sensitive to SES-shim, XS, and supervisor
{
archiveWriteSeconds: undefined,
compressSeconds: 0,
dbSaveSeconds: 0,
hash: '5433501987ce52b3bd9ab47956195669c5adea89b050e8c787eb9da431ce1a6e',
Expand All @@ -27,6 +29,7 @@ Generated by [AVA](https://avajs.dev).
> after use of harden() - sensitive to SES-shim, XS, and supervisor
{
archiveWriteSeconds: undefined,
compressSeconds: 0,
dbSaveSeconds: 0,
hash: '4cdc352b710f0719bc6f541631315652b5da19093e18ce844ec274340a37efd5',
Expand Down
Binary file modified packages/SwingSet/test/snapshots/xsnap-store.test.js.snap
Binary file not shown.
19 changes: 13 additions & 6 deletions packages/cosmic-swingset/src/chain-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ import { makeShutdown } from '@agoric/internal/src/node/shutdown.js';
import * as STORAGE_PATH from '@agoric/internal/src/chain-storage-paths.js';
import * as ActionType from '@agoric/internal/src/action-types.js';
import { BridgeId, CosmosInitKeyToBridgeId } from '@agoric/internal';
import { makeArchiveTranscript } from '@agoric/swing-store';
import {
makeArchiveSnapshot,
makeArchiveTranscript,
} from '@agoric/swing-store';
import {
makeBufferedStorage,
makeReadCachingStorage,
Expand Down Expand Up @@ -77,6 +80,7 @@ const toNumber = specimen => {
* @property {number} [maxVatsOnline]
* @property {'debug' | 'operational'} [vatSnapshotRetention]
* @property {'archival' | 'operational'} [vatTranscriptRetention]
* @property {string} [vatSnapshotArchiveDir]
* @property {string} [vatTranscriptArchiveDir]
*/
const SwingsetConfigShape = M.splitRecord(
Expand All @@ -87,6 +91,7 @@ const SwingsetConfigShape = M.splitRecord(
maxVatsOnline: M.number(),
vatSnapshotRetention: M.or('debug', 'operational'),
vatTranscriptRetention: M.or('archival', 'operational'),
vatSnapshotArchiveDir: M.string(),
vatTranscriptArchiveDir: M.string(),
},
{},
Expand Down Expand Up @@ -322,6 +327,7 @@ export default async function main(progname, args, { env, homedir, agcc }) {
slogfile,
vatSnapshotRetention,
vatTranscriptRetention,
vatSnapshotArchiveDir,
vatTranscriptArchiveDir,
} = swingsetConfig;
const keepSnapshots = vatSnapshotRetention
Expand Down Expand Up @@ -544,12 +550,12 @@ export default async function main(progname, args, { env, homedir, agcc }) {
}
};

const fsPowers = { fs, path, tmp };
const archiveSnapshot = vatSnapshotArchiveDir
? makeArchiveSnapshot(vatSnapshotArchiveDir, fsPowers)
: undefined;
const archiveTranscript = vatTranscriptArchiveDir
? makeArchiveTranscript(vatTranscriptArchiveDir, {
fs,
path,
tmp,
})
? makeArchiveTranscript(vatTranscriptArchiveDir, fsPowers)
: undefined;

const s = await launch({
Expand All @@ -571,6 +577,7 @@ export default async function main(progname, args, { env, homedir, agcc }) {
swingStoreTraceFile,
keepSnapshots,
keepTranscripts,
archiveSnapshot,
archiveTranscript,
afterCommitCallback,
swingsetConfig,
Expand Down
2 changes: 2 additions & 0 deletions packages/cosmic-swingset/src/launch-chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ export async function launch({
swingStoreExportCallback,
keepSnapshots,
keepTranscripts,
archiveSnapshot,
archiveTranscript,
afterCommitCallback = async () => ({}),
swingsetConfig,
Expand Down Expand Up @@ -376,6 +377,7 @@ export async function launch({
exportCallback: swingStoreExportSyncCallback,
keepSnapshots,
keepTranscripts,
archiveSnapshot,
archiveTranscript,
});
const { kvStore, commit } = hostStorage;
Expand Down
36 changes: 36 additions & 0 deletions packages/swing-store/src/archiver.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,42 @@ import { withDeferredCleanup } from '@agoric/internal/src/node/utils.js';

const streamFinished = promisify(streamFinishedCallback);

/*
* @param {string} dirPath
* @param {object} powers
* @param {Pick<import('fs'), 'createWriteStream' | 'mkdirSync' | 'renameSync'>} powers.fs
* @param {Pick<import('path'), 'join'>} powers.path
* @param {Pick<import('tmp'), 'fileSync'>} powers.tmp
*/
export const makeArchiveSnapshot = (dirPath, powers) => {
const { fs, path, tmp } = powers;
fs.mkdirSync(dirPath, { recursive: true });
const archiveSnapshot = (name, gzData) => {
const destPath = path.join(dirPath, `${name}.gz`);
return withDeferredCleanup(async addCleanup => {
const {
name: tmpName,
fd,
removeCallback,
} = tmp.fileSync({
prefix: name,
postfix: '.gz',
detachDescriptor: true,
});
addCleanup(() => removeCallback());
const writer = fs.createWriteStream('', { fd, flush: true });
const reader = Readable.from(gzData);
const destroyReader = promisify(reader.destroy.bind(reader));
addCleanup(() => destroyReader(null));
reader.pipe(writer);
await streamFinished(writer);
fs.renameSync(tmpName, destPath);
});
};
return archiveSnapshot;
};
harden(makeArchiveSnapshot);

/*
* @param {string} dirPath
* @param {object} powers
Expand Down
2 changes: 1 addition & 1 deletion packages/swing-store/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export { initSwingStore, openSwingStore, isSwingStore } from './swingStore.js';
export { makeSwingStoreExporter } from './exporter.js';
export { importSwingStore } from './importer.js';

export { makeArchiveTranscript } from './archiver.js';
export { makeArchiveSnapshot, makeArchiveTranscript } from './archiver.js';

// temporary, for the benefit of SwingSet/misc-tools/replay-transcript.js
export { makeSnapStore } from './snapStore.js';
Expand Down
21 changes: 17 additions & 4 deletions packages/swing-store/src/snapStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { buffer } from './util.js';
* @property {number} dbSaveSeconds time to write snapshot in DB
* @property {number} compressedSize size of (compressed) snapshot
* @property {number} compressSeconds time to generate and compress the snapshot
* @property {number} [archiveWriteSeconds] time to write an archive to disk (if applicable)
*/

/**
Expand Down Expand Up @@ -73,14 +74,15 @@ const finished = promisify(finishedCallback);
* @param {(key: string, value: string | undefined) => void} noteExport
* @param {object} [options]
* @param {boolean | undefined} [options.keepSnapshots]
* @param {(name: string, compressedData: Parameters<import('stream').Readable.from>[0]) => Promise<void>} [options.archiveSnapshot]
* @returns {SnapStore & SnapStoreInternal & SnapStoreDebug}
*/
export function makeSnapStore(
db,
ensureTxn,
{ measureSeconds },
noteExport = () => {},
{ keepSnapshots = false } = {},
{ keepSnapshots = false, archiveSnapshot } = {},
) {
db.exec(`
CREATE TABLE IF NOT EXISTS snapshots (
Expand Down Expand Up @@ -196,7 +198,8 @@ export function makeSnapStore(

/**
* Generates a new XS heap snapshot, stores a gzipped copy of it into the
* snapshots table, and reports information about the process, including
* snapshots table (and also to an archiveSnapshot callback if provided for
* e.g. disk archival), and reports information about the process, including
* snapshot size and timing metrics.
*
* @param {string} vatID
Expand Down Expand Up @@ -236,6 +239,8 @@ export function makeSnapStore(
return compressedSnapshotData;
});
const hash = hashStream.digest('hex');
const rec = snapshotRec(vatID, snapPos, hash, 1);
const exportKey = snapshotMetadataKey(rec);

const { duration: dbSaveSeconds } = await measureSeconds(async () => {
ensureTxn();
Expand All @@ -250,20 +255,28 @@ export function makeSnapStore(
compressedSize,
compressedSnapshot,
);
const rec = snapshotRec(vatID, snapPos, hash, 1);
const exportKey = snapshotMetadataKey(rec);
noteExport(exportKey, JSON.stringify(rec));
noteExport(
currentSnapshotMetadataKey(rec),
snapshotArtifactName(rec),
);
});

let archiveWriteSeconds;
if (archiveSnapshot) {
({ duration: archiveWriteSeconds } = await measureSeconds(
async () => {
await archiveSnapshot(exportKey, compressedSnapshot);
},
));
}

return harden({
hash,
uncompressedSize,
compressSeconds,
dbSaveSeconds,
archiveWriteSeconds,
compressedSize,
});
},
Expand Down
10 changes: 8 additions & 2 deletions packages/swing-store/src/swingStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,13 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
filePath = ':memory:';
}

const { traceFile, keepSnapshots, keepTranscripts, archiveTranscript } =
options;
const {
traceFile,
keepSnapshots,
keepTranscripts,
archiveSnapshot,
archiveTranscript,
} = options;

let traceOutput = traceFile
? fs.createWriteStream(path.resolve(traceFile), {
Expand Down Expand Up @@ -308,6 +313,7 @@ export function makeSwingStore(dirPath, forceReset, options = {}) {
noteExport,
{
keepSnapshots,
archiveSnapshot,
},
);
const { dumpBundles, ...bundleStore } = makeBundleStore(
Expand Down
11 changes: 5 additions & 6 deletions packages/swing-store/test/deletion.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import tmp from 'tmp';
import { arrayIsLike } from '@agoric/internal/tools/ava-assertions.js';
import { tmpDir } from './util.js';
import { initSwingStore } from '../src/swingStore.js';
import { makeArchiveTranscript } from '../src/archiver.js';
import { makeArchiveSnapshot, makeArchiveTranscript } from '../src/archiver.js';
import { makeSwingStoreExporter } from '../src/exporter.js';
import { importSwingStore } from '../src/importer.js';

Expand Down Expand Up @@ -174,14 +174,13 @@ const setupTranscript = async (t, keepTranscripts) => {
t.teardown(cleanup);
const [archiveDir, cleanupArchives] = await tmpDir('archives');
t.teardown(cleanupArchives);
const archiveTranscript = makeArchiveTranscript(archiveDir, {
fs,
path,
tmp,
});
const fsPowers = { fs, path, tmp };
const archiveSnapshot = makeArchiveSnapshot(archiveDir, fsPowers);
const archiveTranscript = makeArchiveTranscript(archiveDir, fsPowers);
const store = initSwingStore(dbDir, {
exportCallback,
keepTranscripts,
archiveSnapshot,
archiveTranscript,
});
const { kernelStorage, hostStorage } = store;
Expand Down
Loading

0 comments on commit ffc594f

Please sign in to comment.