From 43f4e8eb761826703722bde0859ae6a91872c7ee Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Tue, 5 Dec 2023 17:12:25 -0600 Subject: [PATCH] feat: add exporter.getHostKV() API Add a new API to the exporter, `exporter.getHostKV(key)`, so that the cosmic-swingset state-sync exporter process can query `host.height` without accidentally creating a read-write transaction too. refs #8523 --- .../cosmic-swingset/src/import-kernel-db.js | 3 ++ packages/swing-store/src/exporter.js | 33 +++++++++++++++++++ packages/swing-store/test/test-bundles.js | 6 ++++ packages/swing-store/test/test-export.js | 9 +++++ packages/swing-store/test/test-import.js | 3 ++ 5 files changed, 54 insertions(+) diff --git a/packages/cosmic-swingset/src/import-kernel-db.js b/packages/cosmic-swingset/src/import-kernel-db.js index d41e6309cd1c..93f9f18ae510 100755 --- a/packages/cosmic-swingset/src/import-kernel-db.js +++ b/packages/cosmic-swingset/src/import-kernel-db.js @@ -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( diff --git a/packages/swing-store/src/exporter.js b/packages/swing-store/src/exporter.js index 777340c4d0fe..5d74bb92dd40 100644 --- a/packages/swing-store/src/exporter.js +++ b/packages/swing-store/src/exporter.js @@ -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} getExportData * * Get a full copy of the first-stage export data (key-value pairs) from the @@ -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 @@ -173,6 +205,7 @@ export function makeSwingStoreExporter(dirPath, options = {}) { } return harden({ + getHostKV, getExportData, getArtifactNames, getArtifact, diff --git a/packages/swing-store/test/test-bundles.js b/packages/swing-store/test/test-bundles.js index 928741d4ab7d..a26ed526e886 100644 --- a/packages/swing-store/test/test-bundles.js +++ b/packages/swing-store/test/test-bundles.js @@ -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]); }, @@ -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]); }, diff --git a/packages/swing-store/test/test-export.js b/packages/swing-store/test/test-export.js index fae8e295c003..ea7a88458a60 100644 --- a/packages/swing-store/test/test-export.js +++ b/packages/swing-store/test/test-export.js @@ -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) @@ -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(); diff --git a/packages/swing-store/test/test-import.js b/packages/swing-store/test/test-import.js index a5c25d620a86..288bc27745b3 100644 --- a/packages/swing-store/test/test-import.js +++ b/packages/swing-store/test/test-import.js @@ -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 } */