Skip to content

Commit

Permalink
Merge pull request #8619 from Agoric/warner/8523-export-without-write…
Browse files Browse the repository at this point in the history
…lock

fix: export state-sync snapshot without a DB write-lock
  • Loading branch information
mergify[bot] authored and mhofman committed Dec 6, 2023
2 parents 601e960 + 06b8c86 commit 0aad7a2
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 7 deletions.
9 changes: 2 additions & 7 deletions packages/cosmic-swingset/src/export-kernel-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { makePromiseKit } from '@endo/promise-kit';
import { Fail, q } from '@agoric/assert';
import { makeAggregateError } from '@agoric/internal';
import { makeShutdown } from '@agoric/internal/src/node/shutdown.js';
import { openSwingStore, makeSwingStoreExporter } from '@agoric/swing-store';
import { makeSwingStoreExporter } from '@agoric/swing-store';

import { isEntrypoint } from './helpers/is-entrypoint.js';
import { makeProcessValue } from './helpers/process-value.js';
Expand Down Expand Up @@ -144,7 +144,6 @@ export const validateExporterOptions = options => {
* @param {Pick<import('fs/promises'), 'open' | 'writeFile'>} powers.fs
* @param {import('path')['resolve']} powers.pathResolve
* @param {typeof import('@agoric/swing-store')['makeSwingStoreExporter']} [powers.makeSwingStoreExporter]
* @param {typeof import('@agoric/swing-store')['openSwingStore']} [powers.openSwingStore]
* @param {null | ((...args: any[]) => void)} [powers.log]
* @returns {StateSyncExporter}
*/
Expand All @@ -154,7 +153,6 @@ export const initiateSwingStoreExport = (
fs: { open, writeFile },
pathResolve,
makeSwingStoreExporter: makeExporter = makeSwingStoreExporter,
openSwingStore: openDB = openSwingStore,
log = console.log,
},
) => {
Expand Down Expand Up @@ -183,10 +181,7 @@ export const initiateSwingStoreExport = (
});
cleanup.push(async () => swingStoreExporter.close());

const { hostStorage } = openDB(stateDir);

savedBlockHeight = Number(hostStorage.kvStore.get('host.height')) || 0;
await hostStorage.close();
savedBlockHeight = Number(swingStoreExporter.getHostKV('host.height')) || 0;

if (blockHeight) {
blockHeight === savedBlockHeight ||
Expand Down
3 changes: 3 additions & 0 deletions packages/cosmic-swingset/src/import-kernel-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ export const performStateSyncImport = async (
// Represent the data in `exportDir` as a SwingSetExporter object.
/** @type {import('@agoric/swing-store').SwingStoreExporter} */
const exporter = harden({
getHostKV(_key) {
return undefined;
},
async *getExportData() {
log?.('importing export data');
const exportData = createReadStream(
Expand Down
33 changes: 33 additions & 0 deletions packages/swing-store/src/exporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ import { validateArtifactMode } from './internal.js';
* the concurrent activity of other swingStore instances, the data representing
* the commit point will stay consistent and available.
*
* @property {(key: string) => string | undefined} getHostKV
*
* Retrieve a value from the "host" portion of the kvStore, just like
* hostStorage.hostKVStore.get() would do.
*
* @property {() => AnyIterableIterator<KVPair>} getExportData
*
* Get a full copy of the first-stage export data (key-value pairs) from the
Expand Down Expand Up @@ -112,6 +117,33 @@ export function makeSwingStoreExporter(dirPath, options = {}) {
assertComplete(internal, artifactMode);
}

const sqlKVGet = db.prepare(`
SELECT value
FROM kvStore
WHERE key = ?
`);
sqlKVGet.pluck(true);

/**
* Obtain the value stored for a given host key. This is for the
* benefit of clients who need to briefly query the DB to ensure
* they are exporting the right thing, and need to avoid modifying
* anything (or creating a read-write DB lock) in the process.
*
* @param {string} key The key whose value is sought.
*
* @returns {string | undefined} the (string) value for the given key, or
* undefined if there is no such value.
*
* @throws if key is not a string, or the key is not in the host
* section
*/
function getHostKV(key) {
typeof key === 'string' || Fail`key must be a string`;
getKeyType(key) === 'host' || Fail`getHostKV requires host keys`;
return sqlKVGet.get(key);
}

const sqlGetAllKVData = db.prepare(`
SELECT key, value
FROM kvStore
Expand Down Expand Up @@ -173,6 +205,7 @@ export function makeSwingStoreExporter(dirPath, options = {}) {
}

return harden({
getHostKV,
getExportData,
getArtifactNames,
getArtifact,
Expand Down
6 changes: 6 additions & 0 deletions packages/swing-store/test/test-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ test('b0 import', async t => {
const idA = makeB0ID(b0A);
const nameA = `bundle.${idA}`;
const exporter = {
getHostKV(_key) {
return undefined;
},
async *getExportData() {
yield /** @type {const} */ ([nameA, idA]);
},
Expand Down Expand Up @@ -135,6 +138,9 @@ test('b0 bad import', async t => {
const idA = makeB0ID(b0A);
const nameA = `bundle.${idA}`;
const exporter = {
getHostKV(_key) {
return undefined;
},
async *getExportData() {
yield /** @type {const} */ ([nameA, idA]);
},
Expand Down
9 changes: 9 additions & 0 deletions packages/swing-store/test/test-export.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const exportTest = test.macro(async (t, mode) => {
const ss1 = initSwingStore(dbDir, options);
const ks = ss1.kernelStorage;

ss1.hostStorage.kvStore.set('host.h1', 'hostvalue1');

// build a DB with four spans (one in an old incarnation, two
// historical but current incarnation, only one inUse) and two
// snapshots (only one inUSe)
Expand Down Expand Up @@ -88,6 +90,13 @@ const exportTest = test.macro(async (t, mode) => {
}
const exporter = makeSwingStoreExporter(dbDir, { artifactMode });

// hostKV
t.is(exporter.getHostKV('host.h1'), 'hostvalue1');
t.is(exporter.getHostKV('host.hmissing'), undefined);
t.throws(() => exporter.getHostKV('nonhost'), {
message: 'getHostKV requires host keys',
});

// exportData
{
const exportData = new Map();
Expand Down
3 changes: 3 additions & 0 deletions packages/swing-store/test/test-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ function convert(orig) {
*/
export function makeExporter(exportData, artifacts) {
return {
getHostKV(_key) {
return undefined;
},
async *getExportData() {
for (const [key, value] of exportData.entries()) {
/** @type { KVPair } */
Expand Down

0 comments on commit 0aad7a2

Please sign in to comment.