diff --git a/packages/SwingSet/docs/run-policy.md b/packages/SwingSet/docs/run-policy.md index d53da5186aa..0eba4d940d2 100644 --- a/packages/SwingSet/docs/run-policy.md +++ b/packages/SwingSet/docs/run-policy.md @@ -88,7 +88,7 @@ while(1) { processInboundIO(); const policy = make100CrankPolicy(); await controller.run(policy); - commit(); + await commit(); processOutboundIO(); } ``` diff --git a/packages/SwingSet/misc-tools/db-delete.js b/packages/SwingSet/misc-tools/db-delete.js index 0ec7b8e8bf7..e330359a7c1 100755 --- a/packages/SwingSet/misc-tools/db-delete.js +++ b/packages/SwingSet/misc-tools/db-delete.js @@ -26,7 +26,7 @@ function fail(message, printUsage) { process.exit(1); } -function run() { +async function run() { const argv = process.argv.slice(2); let range = false; @@ -56,7 +56,10 @@ function run() { } else { kvStore.delete(key); } - commit(); + await commit(); } -run(); +run().then( + () => 0, + e => console.error(`${e}`, e), +); diff --git a/packages/SwingSet/misc-tools/db-set.js b/packages/SwingSet/misc-tools/db-set.js index 522af1cbfb4..44378d81d8c 100755 --- a/packages/SwingSet/misc-tools/db-set.js +++ b/packages/SwingSet/misc-tools/db-set.js @@ -23,7 +23,7 @@ function fail(message, printUsage) { process.exit(1); } -function run() { +async function run() { const argv = process.argv.slice(2); if (argv.length !== 3) { @@ -36,7 +36,10 @@ function run() { const { kvStore, commit } = openSwingStore(stateDBDir); kvStore.set(key, value); - commit(); + await commit(); } -run(); +run().then( + () => 0, + e => console.error(`${e}`, e), +); diff --git a/packages/SwingSet/misc-tools/extract-transcript-from-kerneldb.js b/packages/SwingSet/misc-tools/extract-transcript-from-kerneldb.js index eacf1594c1e..59309826c68 100644 --- a/packages/SwingSet/misc-tools/extract-transcript-from-kerneldb.js +++ b/packages/SwingSet/misc-tools/extract-transcript-from-kerneldb.js @@ -1,6 +1,6 @@ // XXX this is wrong; it needs to use the swingstore instead of opening the LMDB // file directly, then use stream store reads to get the transcript entries. -import lmdb from 'node-lmdb'; +import lmdb from 'lmdb'; import process from 'process'; import fs from 'fs'; diff --git a/packages/SwingSet/misc-tools/rekernelize.js b/packages/SwingSet/misc-tools/rekernelize.js index 2b58f9b4dc2..b9cac1a78a0 100755 --- a/packages/SwingSet/misc-tools/rekernelize.js +++ b/packages/SwingSet/misc-tools/rekernelize.js @@ -1,7 +1,7 @@ #!/usr/bin/env node import '@endo/init/pre-bundle-source.js'; -import 'node-lmdb'; +import 'lmdb'; import '@endo/init'; import fs from 'fs'; @@ -65,8 +65,8 @@ async function main() { ); kvStore.set('kernelBundle', JSON.stringify(kernelBundle)); - swingStore.commit(); - swingStore.close(); + await swingStore.commit(); + await swingStore.close(); } main().then( diff --git a/packages/SwingSet/misc-tools/replace-bundle.js b/packages/SwingSet/misc-tools/replace-bundle.js index b3da55cb984..5b02acdbd31 100755 --- a/packages/SwingSet/misc-tools/replace-bundle.js +++ b/packages/SwingSet/misc-tools/replace-bundle.js @@ -59,7 +59,7 @@ async function run() { const newBundleStr = JSON.stringify(bundle); log(`new bundle is ${newBundleStr.length} bytes`); kvStore.set(bundleName, newBundleStr); - commit(); + await commit(); log(`bundle ${bundleName} replaced`); } diff --git a/packages/SwingSet/package.json b/packages/SwingSet/package.json index c8df3014bb1..efa3d317e17 100644 --- a/packages/SwingSet/package.json +++ b/packages/SwingSet/package.json @@ -45,7 +45,7 @@ "@endo/zip": "^0.2.25", "anylogger": "^0.21.0", "import-meta-resolve": "^1.1.1", - "node-lmdb": "^0.9.5", + "lmdb": "^2.4.5", "semver": "^6.3.0" }, "peerDependencies": { diff --git a/packages/SwingSet/src/kernel/kernelSyscall.js b/packages/SwingSet/src/kernel/kernelSyscall.js index dcf45d822be..8d041773aba 100644 --- a/packages/SwingSet/src/kernel/kernelSyscall.js +++ b/packages/SwingSet/src/kernel/kernelSyscall.js @@ -19,6 +19,7 @@ export function makeKernelSyscallHandler(tools) { deviceHooks, } = tools; + /** @type {{kvStore: KVStore}} */ const { kvStore } = kernelKeeper; function send(target, msg) { @@ -46,13 +47,25 @@ export function makeKernelSyscallHandler(tools) { let workingPriorKey; let workingLowerBound; let workingUpperBound; + /** @type {IterableIterator | undefined} */ let workingKeyIterator; - function clearVatStoreIteration() { - workingPriorKey = undefined; - workingLowerBound = undefined; - workingUpperBound = undefined; - workingKeyIterator = undefined; + /** @param {boolean} [done] */ + function clearVatStoreIteration(done = false) { + try { + if ( + !done && + workingKeyIterator && + typeof workingKeyIterator.return === 'function' + ) { + workingKeyIterator.return(); + } + } finally { + workingKeyIterator = undefined; + workingPriorKey = undefined; + workingLowerBound = undefined; + workingUpperBound = undefined; + } } /** @@ -80,8 +93,8 @@ export function makeKernelSyscallHandler(tools) { const actualKey = vatstoreKeyKey(vatID, key); kernelKeeper.incStat('syscalls'); kernelKeeper.incStat('syscallVatstoreSet'); - kvStore.set(actualKey, value); clearVatStoreIteration(); + kvStore.set(actualKey, value); return OKNULL; } @@ -160,6 +173,7 @@ export function makeKernelSyscallHandler(tools) { } kernelKeeper.incStat('syscalls'); kernelKeeper.incStat('syscallVatstoreGetAfter'); + /** @type {IteratorResult} */ let nextIter; // Note that the working key iterator will be invalidated if the parameters // to `vatstoreGetAfter` don't correspond to the working key iterator's @@ -180,6 +194,7 @@ export function makeKernelSyscallHandler(tools) { ) { nextIter = workingKeyIterator.next(); } else { + clearVatStoreIteration(); let startKey; if (priorKey === '') { startKey = actualLowerBound; @@ -198,7 +213,7 @@ export function makeKernelSyscallHandler(tools) { } } if (nextIter.done) { - clearVatStoreIteration(); + clearVatStoreIteration(true); return harden(['ok', [undefined, undefined]]); } else { const nextKey = nextIter.value; @@ -220,8 +235,8 @@ export function makeKernelSyscallHandler(tools) { const actualKey = vatstoreKeyKey(vatID, key); kernelKeeper.incStat('syscalls'); kernelKeeper.incStat('syscallVatstoreDelete'); - kvStore.delete(actualKey); clearVatStoreIteration(); + kvStore.delete(actualKey); return OKNULL; } diff --git a/packages/SwingSet/src/kernel/state/storageWrapper.js b/packages/SwingSet/src/kernel/state/storageWrapper.js index 334c231d03a..dcffd42d5cd 100644 --- a/packages/SwingSet/src/kernel/state/storageWrapper.js +++ b/packages/SwingSet/src/kernel/state/storageWrapper.js @@ -1,4 +1,6 @@ -import { assert } from '@agoric/assert'; +// @ts-check + +import { assert, details as X } from '@agoric/assert'; import { insistStorageAPI } from '../../lib/storageAPI.js'; // We wrap a provided object implementing StorageAPI methods { has, getKeys, @@ -24,30 +26,74 @@ import { insistStorageAPI } from '../../lib/storageAPI.js'; * @yields any */ function* mergeUtf16SortedIterators(it1, it2) { - let v1 = it1.next(); - let v2 = it2.next(); - while (!v1.done && !v2.done) { - if (v1.value < v2.value) { - const result = v1.value; - v1 = it1.next(); - yield result; - } else if (v1.value === v2.value) { - const result = v1.value; - v1 = it1.next(); - v2 = it2.next(); - yield result; - } else { - const result = v2.value; - v2 = it2.next(); + /** @type {IteratorResult | null} */ + let v1 = null; + /** @type {IteratorResult | null} */ + let v2 = null; + /** @type {IteratorResult | null} */ + let vrest = null; + /** @type {Iterator | null} */ + let itrest = null; + + try { + v1 = it1.next(); + v2 = it2.next(); + while (!v1.done && !v2.done) { + if (v1.value < v2.value) { + const result = v1.value; + v1 = it1.next(); + yield result; + } else if (v1.value === v2.value) { + const result = v1.value; + v1 = it1.next(); + v2 = it2.next(); + yield result; + } else { + const result = v2.value; + v2 = it2.next(); + yield result; + } + } + + itrest = v1.done ? it2 : it1; + vrest = v1.done ? v2 : v1; + v1 = null; + v2 = null; + + while (!vrest.done) { + const result = vrest.value; + vrest = itrest.next(); yield result; } - } - const itrest = v1.done ? it2 : it1; - let v = v1.done ? v2 : v1; - while (!v.done) { - const result = v.value; - v = itrest.next(); - yield result; + } finally { + const errors = []; + try { + if (vrest && !vrest.done && itrest && itrest.return) { + itrest.return(); + } + } catch (e) { + errors.push(e); + } + try { + if (v1 && !v1.done && it1.return) { + it1.return(); + } + } catch (e) { + errors.push(e); + } + try { + if (v2 && !v2.done && it2.return) { + it2.return(); + } + } catch (e) { + errors.push(e); + } + if (errors.length) { + const err = assert.error(X`Merged iterator failed to close cleanly`); + for (const e of errors) { + assert.note(err, X`Caused by ${e}`); + } + } } } @@ -56,7 +102,7 @@ function* mergeUtf16SortedIterators(it1, it2) { * that buffers any mutations until told to commit them. * * @param {KVStore} kvStore The StorageAPI object that this crank buffer will be based on. - * @param {CreateSHA256} createSHA256 + * @param {import('../../lib-nodejs/hasher.js').CreateSHA256} createSHA256 * @param { (key: string) => 'consensus' | 'local' | 'invalid' } getKeyType * @returns {*} an object { * crankBuffer, // crank buffer as described, wrapping `kvStore` diff --git a/packages/SwingSet/src/lib-nodejs/hasher.js b/packages/SwingSet/src/lib-nodejs/hasher.js index 491596158de..32b92a5d3ae 100644 --- a/packages/SwingSet/src/lib-nodejs/hasher.js +++ b/packages/SwingSet/src/lib-nodejs/hasher.js @@ -3,7 +3,7 @@ import { assert } from '@agoric/assert'; import { createHash } from 'crypto'; /** - * @typedef { (initial: string?) => { + * @typedef { (initial?: string) => { * add: (more: string) => void, * finish: () => string, * } diff --git a/packages/SwingSet/src/liveslots/collectionManager.js b/packages/SwingSet/src/liveslots/collectionManager.js index a1903173eb0..fe98e31479f 100644 --- a/packages/SwingSet/src/liveslots/collectionManager.js +++ b/packages/SwingSet/src/liveslots/collectionManager.js @@ -17,8 +17,11 @@ import { Far, passStyleOf } from '@endo/marshal'; import { decodeToJustin } from '@endo/marshal/src/marshal-justin.js'; import { parseVatSlot } from '../lib/parseVatSlots.js'; -// The maximum length of an LMDB key is 254 characters, which puts an upper -// bound on the post-encoding size of keys than can be used to index entries in +// The maximum length of an LMDB key used to be 511 bytes which corresponded to +// 254 UTF-16 code units when using JS strings directly as keys. While we now +// have larger key sizes and use a different unicode encoding, the prior +// collections key constraints remain the same. They put an upper bound on the +// post-encoding (stringified) size of keys than can be used to index entries in // collections. In addition to the encoding of the collection entry key, the // storage key will also be prefixed with additional indexing information that // includes the collection ID (an integer that will grow over time as more diff --git a/packages/SwingSet/test/test-state.js b/packages/SwingSet/test/test-state.js index 867a9eae300..4456ede1dc7 100644 --- a/packages/SwingSet/test/test-state.js +++ b/packages/SwingSet/test/test-state.js @@ -36,7 +36,7 @@ function checkState(t, getState, expected) { t.deepEqual(got.sort(compareStrings), expected.sort(compareStrings)); } -function testStorage(t, s, getState, commit) { +async function testStorage(t, s, getState, commit) { t.falsy(s.has('missing')); t.is(s.get('missing'), undefined); @@ -60,7 +60,7 @@ function testStorage(t, s, getState, commit) { if (commit) { checkState(t, getState, []); - commit(); + await commit(); } checkState(t, getState, [ ['foo', 'f'], @@ -69,18 +69,23 @@ function testStorage(t, s, getState, commit) { ]); } -test('storageInMemory', t => { +test('storageInMemory', async t => { const store = initSwingStore(null); - testStorage(t, store.kvStore, () => getAllState(store).kvStuff, null); + await testStorage(t, store.kvStore, () => getAllState(store).kvStuff, null); }); -test('crankBuffer fulfills storage API', t => { +test('crankBuffer fulfills storage API', async t => { const store = initSwingStore(null); const { crankBuffer, commitCrank } = buildCrankBuffer( store.kvStore, createSHA256, ); - testStorage(t, crankBuffer, () => getAllState(store).kvStuff, commitCrank); + await testStorage( + t, + crankBuffer, + () => getAllState(store).kvStuff, + commitCrank, + ); }); test('crankBuffer handles key iteration properly even with intra-crank data changes', t => { diff --git a/packages/SwingSet/test/vat-warehouse/test-warehouse.js b/packages/SwingSet/test/vat-warehouse/test-warehouse.js index d63c744ee2b..848704ffcc1 100644 --- a/packages/SwingSet/test/vat-warehouse/test-warehouse.js +++ b/packages/SwingSet/test/vat-warehouse/test-warehouse.js @@ -1,5 +1,10 @@ // @ts-check +import '@endo/init/pre-bundle-source.js'; + +// import lmdb early to work around SES incompatibility +import 'lmdb'; + // eslint-disable-next-line import/order import { test } from '../../tools/prepare-test-env-ava.js'; import fs from 'fs'; @@ -124,7 +129,7 @@ test('snapshot after deliveries', async t => { t.teardown(c.shutdown); await runSteps(c, t); - commit(); + await commit(); const { inUse, onDisk, extra } = unusedSnapshotsOnDisk( kvStore, diff --git a/packages/access-token/package.json b/packages/access-token/package.json index e5f2a22efb5..f9019d96e6a 100644 --- a/packages/access-token/package.json +++ b/packages/access-token/package.json @@ -17,7 +17,8 @@ }, "dependencies": { "@agoric/assert": "^0.4.0", - "n-readlines": "^1.0.0" + "n-readlines": "^1.0.0", + "tmp": "^0.2.1" }, "devDependencies": { "ava": "^3.12.1", diff --git a/packages/access-token/src/access-token.js b/packages/access-token/src/access-token.js index 142b38e7f33..37cc0acf026 100644 --- a/packages/access-token/src/access-token.js +++ b/packages/access-token/src/access-token.js @@ -43,9 +43,9 @@ export async function getAccessToken(port) { const accessTokenKey = `accessToken/${port}`; if (!storage.has(accessTokenKey)) { storage.set(accessTokenKey, await generateAccessToken()); - commit(); + await commit(); } const accessToken = storage.get(accessTokenKey); - close(); + await close(); return accessToken; } diff --git a/packages/access-token/src/json-store.js b/packages/access-token/src/json-store.js index e38b32beed2..a55ba842970 100644 --- a/packages/access-token/src/json-store.js +++ b/packages/access-token/src/json-store.js @@ -159,8 +159,8 @@ function makeStorageInMemory() { * * @returns {{ * storage: JSONStore, // a storage API object to load and store data - * commit: () => void, // commit changes made since the last commit - * close: () => void, // shutdown the store, abandoning any uncommitted changes + * commit: () => Promise, // commit changes made since the last commit + * close: () => Promise, // shutdown the store, abandoning any uncommitted changes * }} */ function makeJSONStore(dirPath, forceReset = false) { @@ -198,7 +198,7 @@ function makeJSONStore(dirPath, forceReset = false) { /** * Commit unsaved changes. */ - function commit() { + async function commit() { if (dirPath) { const tempFile = `${storeFile}-${process.pid}.tmp`; const fd = fs.openSync(tempFile, 'w'); @@ -217,7 +217,7 @@ function makeJSONStore(dirPath, forceReset = false) { * Close the "database", abandoning any changes made since the last commit * (if you want to save them, call commit() first). */ - function close() { + async function close() { // Nothing to do here. } diff --git a/packages/access-token/test/test-state.js b/packages/access-token/test/test-state.js index 67c4189fe57..c57b14b4590 100644 --- a/packages/access-token/test/test-state.js +++ b/packages/access-token/test/test-state.js @@ -1,4 +1,4 @@ -import fs from 'fs'; +import tmp from 'tmp'; import test from 'ava'; import { @@ -8,22 +8,20 @@ import { isJSONStore, } from '../src/json-store.js'; -function rimraf(dirPath) { - try { - // Node.js 16.8.0 warns: - // In future versions of Node.js, fs.rmdir(path, { recursive: true }) will - // be removed. Use fs.rm(path, { recursive: true }) instead - if (fs.rmSync) { - fs.rmSync(dirPath, { recursive: true }); - } else { - fs.rmdirSync(dirPath, { recursive: true }); - } - } catch (e) { - if (e.code !== 'ENOENT') { - throw e; - } - } -} +/** + * @param {string} [prefix] + * @returns {Promise<[string, () => void]>} + */ +const tmpDir = prefix => + new Promise((resolve, reject) => { + tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => { + if (err) { + reject(err); + } else { + resolve([name, removeCallback]); + } + }); + }); function testStorage(t, storage) { t.falsy(storage.has('missing')); @@ -56,16 +54,15 @@ function testStorage(t, storage) { t.deepEqual(getAllState(storage), reference, 'check state after changes'); } -test('storageInFile', t => { - const dbDir = 'testdb'; - t.teardown(() => rimraf(dbDir)); - rimraf(dbDir); +test('storageInFile', async t => { + const [dbDir, cleanup] = await tmpDir('testdb'); + t.teardown(cleanup); t.is(isJSONStore(dbDir), false); const { storage, commit, close } = initJSONStore(dbDir); testStorage(t, storage); - commit(); + await commit(); const before = getAllState(storage); - close(); + await close(); t.is(isJSONStore(dbDir), true); const { storage: after } = openJSONStore(dbDir); diff --git a/packages/cosmic-swingset/package.json b/packages/cosmic-swingset/package.json index 4204c06fb0b..5015c5e36ca 100644 --- a/packages/cosmic-swingset/package.json +++ b/packages/cosmic-swingset/package.json @@ -41,7 +41,7 @@ "anylogger": "^0.21.0", "deterministic-json": "^1.0.5", "import-meta-resolve": "^1.1.1", - "node-lmdb": "^0.9.5", + "lmdb": "^2.4.5", "tmp": "^0.2.1" }, "devDependencies": { diff --git a/packages/cosmic-swingset/src/entrypoint.js b/packages/cosmic-swingset/src/entrypoint.js index bdc2cd4cc08..514f3535900 100755 --- a/packages/cosmic-swingset/src/entrypoint.js +++ b/packages/cosmic-swingset/src/entrypoint.js @@ -2,8 +2,8 @@ import '@endo/init/pre-bundle-source.js'; -// import node-lmdb early to work around SES incompatibility -import 'node-lmdb'; +// import lmdb early to work around SES incompatibility +import 'lmdb'; import agcc from '@agoric/cosmos'; diff --git a/packages/solo/package.json b/packages/solo/package.json index 393ba422bd8..0016bda92b0 100644 --- a/packages/solo/package.json +++ b/packages/solo/package.json @@ -48,7 +48,7 @@ "minimist": "^1.2.0", "morgan": "^1.9.1", "node-fetch": "^2.6.0", - "node-lmdb": "^0.9.5", + "lmdb": "^2.4.5", "temp": "^0.9.1", "tmp": "^0.2.1", "ws": "^7.2.0" diff --git a/packages/solo/src/entrypoint.js b/packages/solo/src/entrypoint.js index f6a8ff28fa6..31867b98b05 100755 --- a/packages/solo/src/entrypoint.js +++ b/packages/solo/src/entrypoint.js @@ -2,8 +2,8 @@ import '@endo/init/pre-bundle-source.js'; -// import node-lmdb early to work around SES incompatibility -import 'node-lmdb'; +// import lmdb early to work around SES incompatibility +import 'lmdb'; // Needed for legacy plugin support (dapp-oracle, for one). import 'esm'; diff --git a/packages/solo/src/pipe-entrypoint.js b/packages/solo/src/pipe-entrypoint.js index ac5ba254df0..97082771ad9 100644 --- a/packages/solo/src/pipe-entrypoint.js +++ b/packages/solo/src/pipe-entrypoint.js @@ -1,7 +1,7 @@ /* global process */ // @ts-check import '@endo/init/pre-bundle-source.js'; -import 'node-lmdb'; +import 'lmdb'; import '@endo/init'; import { parse, stringify } from '@endo/marshal'; diff --git a/packages/solo/src/reset-state.js b/packages/solo/src/reset-state.js index ab6dd7efda1..0421499bb15 100644 --- a/packages/solo/src/reset-state.js +++ b/packages/solo/src/reset-state.js @@ -11,6 +11,6 @@ export default async function resetState(basedir) { fs.writeFileSync(mailboxStateFile, `{}\n`); const kernelStateDBDir = path.join(basedir, 'swingset-kernel-state'); const { commit, close } = initSwingStore(kernelStateDBDir); - commit(); - close(); + await commit(); + await close(); } diff --git a/packages/solo/src/start.js b/packages/solo/src/start.js index b6566d31d0f..d10e2f71f17 100644 --- a/packages/solo/src/start.js +++ b/packages/solo/src/start.js @@ -248,7 +248,7 @@ const buildSwingset = async ( async function saveState() { const ms = JSON.stringify(mbs.exportToData()); await atomicReplaceFile(mailboxStateFile, ms); - commit(); + await commit(); } function deliverOutbound() { diff --git a/packages/swing-store/package.json b/packages/swing-store/package.json index b402bbc3ac0..4755f881a79 100644 --- a/packages/swing-store/package.json +++ b/packages/swing-store/package.json @@ -20,7 +20,7 @@ "dependencies": { "@agoric/assert": "^0.4.0", "better-sqlite3": "^7.5.0", - "node-lmdb": "^0.9.5", + "lmdb": "^2.4.5", "tmp": "^0.2.1" }, "devDependencies": { diff --git a/packages/swing-store/src/ephemeralSwingStore.js b/packages/swing-store/src/ephemeralSwingStore.js index 2c7a7fe6c0c..93b6694e189 100644 --- a/packages/swing-store/src/ephemeralSwingStore.js +++ b/packages/swing-store/src/ephemeralSwingStore.js @@ -11,6 +11,10 @@ import { assert, details as X, q } from '@agoric/assert'; const streamPeek = new WeakMap(); // for tests to get raw access to the streams +function* empty() { + // Yield nothing +} + /** * Create a non-persistent swing store based on an in-memory map. * @@ -150,7 +154,7 @@ export function initEphemeralSwingStore() { * @param {object} startPosition The position to start reading from * @param {object} endPosition The position of the end of the stream * - * @returns {Iterable} an iterable for the items in the named stream + * @returns {IterableIterator} an iterable for the items in the named stream */ function readStream(streamName, startPosition, endPosition) { insistStreamName(streamName); @@ -164,7 +168,7 @@ export function initEphemeralSwingStore() { assert(startPosition.itemCount <= endPosition.itemCount); if (endPosition.itemCount === 0) { - return []; + return empty(); } else { const readStatus = `read-${statusCounter}`; statusCounter += 1; @@ -227,7 +231,7 @@ export function initEphemeralSwingStore() { /** * Commit unsaved changes. */ - function commit() { + async function commit() { // Nothing to do here. } @@ -235,7 +239,7 @@ export function initEphemeralSwingStore() { * Close the "database", abandoning any changes made since the last commit * (if you want to save them, call commit() first). */ - function close() { + async function close() { // Nothing to do here. } diff --git a/packages/swing-store/src/sqlStreamStore.js b/packages/swing-store/src/sqlStreamStore.js index d7f34fa3839..81f24c635e7 100644 --- a/packages/swing-store/src/sqlStreamStore.js +++ b/packages/swing-store/src/sqlStreamStore.js @@ -8,6 +8,10 @@ const STREAM_START = { itemCount: 0 }; * @typedef { import('./swingStore').StreamPosition } StreamPosition */ +function* empty() { + // Yield nothing +} + /** * @param { unknown } streamName * @returns { asserts streamName is string } @@ -97,7 +101,7 @@ export function sqlStreamStore(dbDir, io) { ); if (startPosition.itemCount === endPosition.itemCount) { - return []; + return empty(); } streamStatus.set(streamName, 'reading'); diff --git a/packages/swing-store/src/swingStore.js b/packages/swing-store/src/swingStore.js index 166cac53f42..d51db6b1ec0 100644 --- a/packages/swing-store/src/swingStore.js +++ b/packages/swing-store/src/swingStore.js @@ -1,10 +1,9 @@ // @ts-check import fs from 'fs'; -import os from 'os'; import path from 'path'; import { tmpName } from 'tmp'; -import lmdb from 'node-lmdb'; +import { open as lmdbOpen, ABORT as lmdbAbort } from 'lmdb'; import sqlite3 from 'better-sqlite3'; import { assert } from '@agoric/assert'; @@ -34,7 +33,7 @@ export function makeSnapStoreIO() { /** * @typedef {{ * has: (key: string) => boolean, - * getKeys: (start: string, end: string) => Iterable, + * getKeys: (start: string, end: string) => IterableIterator, * get: (key: string) => string | undefined, * set: (key: string, value: string) => void, * delete: (key: string) => void, @@ -44,7 +43,7 @@ export function makeSnapStoreIO() { * * @typedef {{ * writeStreamItem: (streamName: string, item: string, position: StreamPosition) => StreamPosition, - * readStream: (streamName: string, startPosition: StreamPosition, endPosition: StreamPosition) => Iterable, + * readStream: (streamName: string, startPosition: StreamPosition, endPosition: StreamPosition) => IterableIterator, * closeStream: (streamName: string) => void, * STREAM_START: StreamPosition, * }} StreamStore @@ -52,8 +51,8 @@ export function makeSnapStoreIO() { * @typedef {{ * kvStore: KVStore, // a key-value StorageAPI object to load and store data * streamStore: StreamStore, // a stream-oriented API object to append and read streams of data - * commit: () => void, // commit changes made since the last commit - * close: () => void, // shutdown the store, abandoning any uncommitted changes + * commit: () => Promise, // commit changes made since the last commit + * close: () => Promise, // shutdown the store, abandoning any uncommitted changes * diskUsage?: () => number, // optional stats method * }} SwingStore * @@ -111,7 +110,11 @@ export function makeSnapStoreIO() { * @returns {SwingAndSnapStore} */ function makeSwingStore(dirPath, forceReset, options) { - let txn = null; + /** @type {((result?: typeof import("lmdb").ABORT) => void) | null} */ + let txnFinish = null; + + /** @type {Promise | null} */ + let txnDone = null; if (forceReset) { try { @@ -150,34 +153,39 @@ function makeSwingStore(dirPath, forceReset, options) { traceOutput = null; } - let lmdbEnv = new lmdb.Env(); - - // Use the new mapSize, persisting if it's bigger than before and there's a - // write txn commit. - // http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5 - lmdbEnv.resize(mapSize); - - lmdbEnv.open({ + /** @type {import('lmdb').RootDatabase | null} */ + let db = lmdbOpen({ path: dirPath, + // TODO: mapSize seem to be ignored / treated as informative by lmbd-js mapSize, - // Turn off useWritemap on the Mac. The useWritemap option is currently - // required for LMDB to function correctly on Linux running under WSL, but - // we don't yet have a convenient recipe to probe our environment at - // runtime to distinguish that species of Linux from the others. For now - // we're running our benchmarks on Mac, so this will do for the time being. - useWritemap: os.platform() !== 'darwin', - }); - - let dbi = lmdbEnv.openDbi({ + // Turn off useWritemap. It can theoretically cause corruption by stray + // pointers, it seems incompatible with our usage of sync transactions. + // While we've checked it wasn't the (sole) cause of + // https://github.com/Agoric/agoric-sdk/issues/5031, it is no longer + // necessary if using WSL2 on Windows. + useWritemap: false, name: 'swingset-kernel-state', - create: true, + // TODO: lmdb-js is using a UTF-8 encoding for strings (both keys and values) + // The biggest impact is on key sizes. A key with unicode points in the range + // 0x0800 - 0xFFFF ends up encoded as 3 bytes where for UTF-16 they'd be + // encoded as 2 bytes. lmbd-js also bumps the maxKeySize to 1791 bytes, + // making this a non-issue for now. + encoding: 'string', }); function ensureTxn() { - if (!txn) { + assert(db); + if (!txnDone && !txnFinish) { trace('begin-tx'); - txn = lmdbEnv.beginTxn(); + txnDone = db.transactionSync( + () => + new Promise(resolve => { + txnFinish = resolve; + }), + ); } + assert(txnDone && txnFinish); + return db; } function diskUsage() { @@ -198,9 +206,8 @@ function makeSwingStore(dirPath, forceReset, options) { */ function get(key) { assert.typeof(key, 'string'); - ensureTxn(); - let result = txn.getString(dbi, key); - if (result === null) { + let result = ensureTxn().get(key); + if (result == null) { result = undefined; } return result; @@ -224,14 +231,18 @@ function makeSwingStore(dirPath, forceReset, options) { assert.typeof(start, 'string'); assert.typeof(end, 'string'); - ensureTxn(); - const cursor = new lmdb.Cursor(txn, dbi); - let key = start === '' ? cursor.goToFirst() : cursor.goToRange(start); - while (key && (end === '' || key < end)) { - yield key; - key = cursor.goToNext(); + /** @type {import("lmdb").RangeOptions} */ + const rangeOptions = {}; + if (start) { + rangeOptions.start = start; } - cursor.close(); + if (end) { + rangeOptions.end = end; + } + + const keys = ensureTxn().getKeys(rangeOptions); + + yield* keys; } /** @@ -260,8 +271,7 @@ function makeSwingStore(dirPath, forceReset, options) { function set(key, value) { assert.typeof(key, 'string'); assert.typeof(value, 'string'); - ensureTxn(); - txn.putString(dbi, key, value); + ensureTxn().put(key, value); trace('set', key, value); } @@ -276,8 +286,7 @@ function makeSwingStore(dirPath, forceReset, options) { function del(key) { assert.typeof(key, 'string'); if (has(key)) { - ensureTxn(); - txn.del(dbi, key); + ensureTxn().remove(key); trace('del', key); } } @@ -298,11 +307,17 @@ function makeSwingStore(dirPath, forceReset, options) { /** * Commit unsaved changes. */ - function commit() { - if (txn) { - txn.commit(); - trace(`commit-tx`); - txn = null; + async function commit() { + assert(db); + if (txnFinish) { + txnFinish(); + try { + await txnDone; + trace(`commit-tx`); + } finally { + txnDone = null; + txnFinish = null; + } } // NOTE: The kvstore (which used to contain vatA -> snapshot1, and @@ -317,16 +332,20 @@ function makeSwingStore(dirPath, forceReset, options) { * Close the database, abandoning any changes made since the last commit (if you want to save them, call * commit() first). */ - function close() { - if (txn) { - txn.abort(); - trace(`abort-tx`); - txn = null; + async function close() { + assert(db); + if (txnFinish) { + txnFinish(lmdbAbort); + try { + await txnDone; + trace(`abort-tx`); + } finally { + txnDone = null; + txnFinish = null; + } } - dbi.close(); - dbi = null; - lmdbEnv.close(); - lmdbEnv = null; + await db.close(); + db = null; stopTrace(); } diff --git a/packages/swing-store/test/test-state.js b/packages/swing-store/test/test-state.js index b04dcb3142c..30416ef5faa 100644 --- a/packages/swing-store/test/test-state.js +++ b/packages/swing-store/test/test-state.js @@ -1,10 +1,11 @@ +// @ts-check + // import LMDB before SES lockdown, as workaround for // https://github.com/Agoric/SES-shim/issues/308 -import 'node-lmdb'; +import 'lmdb'; import '@endo/init'; -import fs from 'fs'; - +import tmp from 'tmp'; import test from 'ava'; import { @@ -14,22 +15,20 @@ import { isSwingStore, } from '../src/swingStore.js'; -function rimraf(dirPath) { - try { - // Node.js 16.8.0 warns: - // In future versions of Node.js, fs.rmdir(path, { recursive: true }) will - // be removed. Use fs.rm(path, { recursive: true }) instead - if (fs.rmSync) { - fs.rmSync(dirPath, { recursive: true }); - } else { - fs.rmdirSync(dirPath, { recursive: true }); - } - } catch (e) { - if (e.code !== 'ENOENT') { - throw e; - } - } -} +/** + * @param {string} [prefix] + * @returns {Promise<[string, () => void]>} + */ +const tmpDir = prefix => + new Promise((resolve, reject) => { + tmp.dir({ unsafeCleanup: true, prefix }, (err, name, removeCallback) => { + if (err) { + reject(err); + } else { + resolve([name, removeCallback]); + } + }); + }); function testKVStore(t, store) { const kvStore = store.kvStore; @@ -70,27 +69,44 @@ test('in-memory kvStore read/write', t => { testKVStore(t, initSwingStore(null)); }); -test('persistent kvStore read/write/re-open', t => { - const dbDir = 'testdb'; - t.teardown(() => rimraf(dbDir)); - rimraf(dbDir); +test('persistent kvStore read/write/re-open', async t => { + const [dbDir, cleanup] = await tmpDir('testdb'); + t.teardown(cleanup); t.is(isSwingStore(dbDir), false); const store = initSwingStore(dbDir); const { commit, close } = store; testKVStore(t, store); - commit(); + await commit(); const before = getAllState(store); - close(); + await close(); t.is(isSwingStore(dbDir), true); const store2 = openSwingStore(dbDir); const { close: close2 } = store2; t.deepEqual(getAllState(store2), before, 'check state after reread'); t.is(isSwingStore(dbDir), true); - close2(); + await close2(); +}); + +test('persistent kvStore maxKeySize write', async t => { + // Vat collections assume they have 220 characters for their key space. + // This is based on previous math where LMDB's key size was 511 bytes max + // and the native UTF-16 encoding of JS strings, minus some overhead + // for vat store prefixes. + // However some unicode codepoints may end up serialized as 3 bytes with UTF-8 + // This tests that no matter what, we can write 254 unicode characters in the + // 0x0800 - 0xFFFF range (single UTF-16 codepoint, but 3 byte UTF-8). + + const [dbDir, cleanup] = await tmpDir('testdb'); + t.teardown(cleanup); + t.is(isSwingStore(dbDir), false); + const store = initSwingStore(dbDir); + store.kvStore.set('€'.repeat(254), 'Money!'); + await store.commit(); + await store.close(); }); -function testStreamStore(t, dbDir) { +async function testStreamStore(t, dbDir) { const { streamStore, commit, close } = initSwingStore(dbDir); const start = streamStore.STREAM_START; @@ -125,23 +141,22 @@ function testStreamStore(t, dbDir) { const readerEmpty2 = streamStore.readStream('empty', start, start); t.deepEqual(Array.from(readerEmpty2), []); - commit(); - close(); + await commit(); + await close(); } -test('in-memory streamStore read/write', t => { - testStreamStore(t, null); +test('in-memory streamStore read/write', async t => { + await testStreamStore(t, null); }); -test('persistent streamStore read/write', t => { - const dbDir = 'testdb'; - t.teardown(() => rimraf(dbDir)); - rimraf(dbDir); +test('persistent streamStore read/write', async t => { + const [dbDir, cleanup] = await tmpDir('testdb'); + t.teardown(cleanup); t.is(isSwingStore(dbDir), false); - testStreamStore(t, dbDir); + await testStreamStore(t, dbDir); }); -function testStreamStoreModeInterlock(t, dbDir) { +async function testStreamStoreModeInterlock(t, dbDir) { const { streamStore, commit, close } = initSwingStore(dbDir); const start = streamStore.STREAM_START; @@ -152,7 +167,7 @@ function testStreamStoreModeInterlock(t, dbDir) { t.throws(() => streamStore.readStream('st1', start, s1pos), { message: `can't read stream "st1" because it's already in use`, }); - t.throws(() => streamStore.writeStreamItem('st1', start, s1pos), { + t.throws(() => streamStore.writeStreamItem('st1', 'second', s1pos), { message: `can't write stream "st1" because it's already in use`, }); streamStore.closeStream('st1'); @@ -162,18 +177,17 @@ function testStreamStoreModeInterlock(t, dbDir) { streamStore.closeStream('nonexistent'); - commit(); - close(); + await commit(); + await close(); } -test('in-memory streamStore mode interlock', t => { - testStreamStoreModeInterlock(t, null); +test('in-memory streamStore mode interlock', async t => { + await testStreamStoreModeInterlock(t, null); }); -test('persistent streamStore mode interlock', t => { - const dbDir = 'testdb'; - t.teardown(() => rimraf(dbDir)); - rimraf(dbDir); +test('persistent streamStore mode interlock', async t => { + const [dbDir, cleanup] = await tmpDir('testdb'); + t.teardown(cleanup); t.is(isSwingStore(dbDir), false); - testStreamStoreModeInterlock(t, dbDir); + await testStreamStoreModeInterlock(t, dbDir); }); diff --git a/packages/swingset-runner/package.json b/packages/swingset-runner/package.json index 4112fc6bf4b..110ebf01137 100644 --- a/packages/swingset-runner/package.json +++ b/packages/swingset-runner/package.json @@ -34,7 +34,7 @@ "@endo/marshal": "^0.6.7", "expose-gc": "^1.0.0", "n-readlines": "^1.0.1", - "node-lmdb": "^0.9.5", + "lmdb": "^2.4.5", "yargs": "^16.1.0" }, "devDependencies": { diff --git a/packages/swingset-runner/src/kerneldump-entrypoint.js b/packages/swingset-runner/src/kerneldump-entrypoint.js index afd523fe67e..b95f796b06a 100755 --- a/packages/swingset-runner/src/kerneldump-entrypoint.js +++ b/packages/swingset-runner/src/kerneldump-entrypoint.js @@ -6,7 +6,7 @@ */ // LMDB bindings need to be imported before lockdown. -import 'node-lmdb'; +import 'lmdb'; // Now do lockdown. import '@endo/init'; diff --git a/packages/swingset-runner/src/main.js b/packages/swingset-runner/src/main.js index d410b7335bf..ffd6757cd67 100644 --- a/packages/swingset-runner/src/main.js +++ b/packages/swingset-runner/src/main.js @@ -406,9 +406,9 @@ export async function main() { { verbose }, runtimeOptions, ); - swingStore.commit(); + await swingStore.commit(); if (initOnly) { - swingStore.close(); + await swingStore.close(); return; } } @@ -462,8 +462,8 @@ export async function main() { if (activityHash) { log(`activityHash: ${controller.getActivityhash()}`); } - swingStore.commit(); - swingStore.close(); + await swingStore.commit(); + await swingStore.close(); log(`runner stepped ${steps} crank${steps === 1 ? '' : 's'}`); } catch (err) { kernelFailure(err); @@ -481,8 +481,8 @@ export async function main() { cli.context.dump2 = () => controller.dump(); cli.defineCommand('commit', { help: 'Commit current kernel state to persistent storage', - action: () => { - swingStore.commit(); + action: async () => { + await swingStore.commit(); log('committed'); cli.displayPrompt(); }, @@ -626,7 +626,7 @@ export async function main() { } const commitStartTime = readClock(); if (doCommit) { - swingStore.commit(); + await swingStore.commit(); } const blockEndTime = readClock(); if (forceGC) { @@ -692,7 +692,7 @@ export async function main() { let [totalSteps, deltaT] = await runBatch(stepLimit, runInBlockMode); if (!runInBlockMode) { - swingStore.commit(); + await swingStore.commit(); } const cranks = getCrankNumber(); const rawStats = controller.getStats(); @@ -720,7 +720,7 @@ export async function main() { bootstrapResult = null; } } - swingStore.close(); + await swingStore.close(); if (statsFile) { outputStats(statsFile, mainStats, benchmarkStats); } diff --git a/packages/swingset-runner/src/runner-debug-entrypoint.js b/packages/swingset-runner/src/runner-debug-entrypoint.js index 0c8e9d7f5ec..98a9d13441b 100755 --- a/packages/swingset-runner/src/runner-debug-entrypoint.js +++ b/packages/swingset-runner/src/runner-debug-entrypoint.js @@ -7,9 +7,9 @@ import '@endo/init/pre.js'; -// Initialize trasitive dependencies that run afoul of the property override +// Initialize transitive dependencies that run afoul of the property override // after SES lockdown hazard. -import 'node-lmdb'; +import 'lmdb'; // Now do lockdown. import '@endo/init'; diff --git a/packages/swingset-runner/src/runner-entrypoint.js b/packages/swingset-runner/src/runner-entrypoint.js index e78e5c7af58..b13b31ad58c 100755 --- a/packages/swingset-runner/src/runner-entrypoint.js +++ b/packages/swingset-runner/src/runner-entrypoint.js @@ -9,9 +9,9 @@ import '@endo/init/pre-bundle-source.js'; -// Initialize trasitive dependencies that run afoul of the property override +// Initialize transitive dependencies that run afoul of the property override // after SES lockdown hazard. -import 'node-lmdb'; +import 'lmdb'; // Now do lockdown. import '@endo/init'; diff --git a/packages/telemetry/src/kv-string-store.js b/packages/telemetry/src/kv-string-store.js index c893593ebff..22d5b27e19c 100644 --- a/packages/telemetry/src/kv-string-store.js +++ b/packages/telemetry/src/kv-string-store.js @@ -34,8 +34,8 @@ export const makeKVStringStore = (kind, db = makeTempKVDatabase()) => { .prepare(`SELECT value FROM kind_kv WHERE kind = ? AND key = ?`) .iterate(kind, key); const { done, value } = it.next(); - if (!done) { - it.return && it.return(); + if (!done && it.return) { + it.return(); } // console.log({ serialised: value }); return value.value; @@ -46,8 +46,10 @@ export const makeKVStringStore = (kind, db = makeTempKVDatabase()) => { `SELECT COUNT(value) AS cnt FROM kind_kv WHERE kind = ? AND key = ?`, ) .iterate(kind, key); - const { value } = it.next(); - it.return && it.return(); + const { done, value } = it.next(); + if (!done && it.return) { + it.return(); + } // console.log({ count: value }); return value && value.cnt > 0; }, diff --git a/yarn.lock b/yarn.lock index 33822cb7379..1c0b2db9763 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2782,6 +2782,36 @@ resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-1.0.1.tgz#853cacd4d78d79059f33f66f8e7b0e5c34bee294" integrity sha512-nSD5AA2AZkKuXuvGs8IK7K5ZczLAogfDd26zT9l6S7WzvqALdVWcW5vMUiTnZyj5SPcNwNNANj0koeV1ieqTFQ== +"@lmdb/lmdb-darwin-arm64@2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.4.5.tgz#b611d9717f03e7fd75f2ea48ea54cbcf505b6535" + integrity sha512-JlYSjhyPUQI2dyH497WkZ+AqPQZZ0v3xyAmjMpKrucgnoPGoNbSYg/LNvfVx9WE2iQunZ7vUkofunFZxuQLDcQ== + +"@lmdb/lmdb-darwin-x64@2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.4.5.tgz#01cc2a1b709cdd754defa463a80ed6692ff1517b" + integrity sha512-n4oKTzr/UsBn5IhSDm0NvJzE341nhJcZ2RCAS0z2bo7j33bR5bc1i70DXctsQRK4YxeqPH/8fU3OLqORxCfM4A== + +"@lmdb/lmdb-linux-arm64@2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.4.5.tgz#a0a4833df288206494c62aa174e167ef65d26309" + integrity sha512-e9Fc6+g4mf3JyJWFtGMSUGHxjkq3QhyEjMnQcnYdbpLzDk7QGMx3epAbx4QU6xJlalINibYXG9cpVlMXMOEKbw== + +"@lmdb/lmdb-linux-arm@2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.4.5.tgz#84788c591f84998011a6f716a0cbe982df27a5dc" + integrity sha512-Xjlk7NNxTDUDsfWkyheX+uvzwM7uhsk9cMF52S1QLwEcE4FL5EV8/7vMHxDH02BH/NU1pmUvMVnILRlMjDI1zg== + +"@lmdb/lmdb-linux-x64@2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.4.5.tgz#30522627de522a822b24cd44fe77b84d1771bff3" + integrity sha512-p2jWhERu6Mdrm7HmPwucbvrVYNCo+NZNz9YRFGc/91zuslqCr12jzNYpXy9j+ZtxqEC/dbZUkta29FESzO3FZA== + +"@lmdb/lmdb-win32-x64@2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.4.5.tgz#fbfe693ebf8767a85d689b0eaa100dedf6c9f688" + integrity sha512-Fs8ErMb6h1lwgTZwHdKXf8gn/K/dMTuP3/jPrOg+CYVy/gto/F+ZpnayG3uulNdwdm+f8IwxRvUHpgDmHsCBYQ== + "@mapbox/node-pre-gyp@1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" @@ -2869,6 +2899,36 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.0.2.tgz#01e3669b8b2dc01f6353f2c87e1ec94faf52c587" + integrity sha512-FMX5i7a+ojIguHpWbzh5MCsCouJkwf4z4ejdUY/fsgB9Vkdak4ZnoIEskOyOUMMB4lctiZFGszFQJXUeFL8tRg== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.0.2.tgz#5ca32f16e6f1b7854001a1a2345b61d4e26a0931" + integrity sha512-DznYtF3lHuZDSRaIOYeif4JgO0NtO2Xf8DsngAugMx/bUdTFbg86jDTmkVJBNmV+cxszz6OjGvinnS8AbJ342g== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.0.2.tgz#ff629f94379981bf476dffb1439a7c1d3dba2d72" + integrity sha512-b0jMEo566YdM2K+BurSed7bswjo3a6bcdw5ETqoIfSuxKuRLPfAiOjVbZyZBgx3J/TAM/QrvEQ/VN89A0ZAxSg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.0.2.tgz#5f6fd30d266c4a90cf989049c7f2e50e5d4fcd4c" + integrity sha512-Gy9+c3Wj+rUlD3YvCZTi92gs+cRX7ZQogtwq0IhRenloTTlsbpezNgk6OCkt59V4ATEWSic9rbU92H/l7XsRvA== + +"@msgpackr-extract/msgpackr-extract-linux-x64@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.0.2.tgz#167faa553b9dbffac8b03bf27de9b6f846f0e1bc" + integrity sha512-zrBHaePwcv4cQXxzYgNj0+A8I1uVN97E7/3LmkRocYZ+rMwUsnPpp4RuTAHSRoKlTQV3nSdCQW4Qdt4MXw/iHw== + +"@msgpackr-extract/msgpackr-extract-win32-x64@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.0.2.tgz#baea7764b1adf201ce4a792fe971fd7211dad2e4" + integrity sha512-fpnI00dt+yO1cKx9qBXelKhPBdEgvc8ZPav1+0r09j0woYQU2N79w/jcGawSY5UGlgQ3vjaJsFHnGbGvvqdLzg== + "@mui/base@5.0.0-alpha.58": version "5.0.0-alpha.58" resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.58.tgz#01ab59a028f314e2f9a79f903a8336ac45853652" @@ -11464,6 +11524,24 @@ lit@^2.0.0-rc.1, lit@^2.0.2: lit-element "^3.0.0" lit-html "^2.0.0" +lmdb@^2.4.5: + version "2.4.5" + resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.4.5.tgz#2d7f6003148de72df433fcefd656443c6f1f8f8b" + integrity sha512-zhdysJRAy3hftB6zzNO6uqGag5XV4oYMGHuiOsFptZBvpZW6S5FJO1zGnHFeSHF9gBQudD3wI4W0ez6/KURrhg== + dependencies: + msgpackr "^1.5.4" + node-addon-api "^4.3.0" + node-gyp-build-optional-packages "5.0.3" + ordered-binary "^1.2.4" + weak-lru-cache "^1.2.2" + optionalDependencies: + "@lmdb/lmdb-darwin-arm64" "2.4.5" + "@lmdb/lmdb-darwin-x64" "2.4.5" + "@lmdb/lmdb-linux-arm" "2.4.5" + "@lmdb/lmdb-linux-arm64" "2.4.5" + "@lmdb/lmdb-linux-x64" "2.4.5" + "@lmdb/lmdb-win32-x64" "2.4.5" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -12327,6 +12405,27 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msgpackr-extract@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.0.2.tgz#201a8d7ade47e99b3ba277c45736b00e195d4670" + integrity sha512-coskCeJG2KDny23zWeu+6tNy7BLnAiOGgiwzlgdm4oeSsTpqEJJPguHIuKZcCdB7tzhZbXNYSg6jZAXkZErkJA== + dependencies: + node-gyp-build-optional-packages "5.0.2" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "2.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "2.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "2.0.2" + +msgpackr@^1.5.4: + version "1.6.1" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.6.1.tgz#4f3c94d6a5b819b838ffc736eddaf60eba436d20" + integrity sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA== + optionalDependencies: + msgpackr-extract "^2.0.2" + multicast-dns@^7.2.4: version "7.2.5" resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" @@ -12369,11 +12468,6 @@ n-readlines@^1.0.0, n-readlines@^1.0.1: resolved "https://registry.yarnpkg.com/n-readlines/-/n-readlines-1.0.1.tgz#bbb7364d38bc31a170a199f986fcacfa76b95f6e" integrity sha512-z4SyAIVgMy7CkgsoNw7YVz40v0g4+WWvvqy8+ZdHrCtgevcEO758WQyrYcw3XPxcLxF+//RszTz/rO48nzD0wQ== -nan@^2.14.1: - version "2.15.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" - integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== - nanocolors@^0.2.1: version "0.2.13" resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.2.13.tgz#dfd1ed0bfab05e9fe540eb6874525f0a1684099b" @@ -12472,6 +12566,11 @@ node-addon-api@^1.7.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.1.tgz#cf813cd69bb8d9100f6bdca6755fc268f54ac492" integrity sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ== +node-addon-api@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" + integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== + node-environment-flags@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" @@ -12501,10 +12600,15 @@ node-forge@^1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" - integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== +node-gyp-build-optional-packages@5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.2.tgz#3de7d30bd1f9057b5dfbaeab4a4442b7fe9c5901" + integrity sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g== + +node-gyp-build-optional-packages@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" + integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== node-gyp@8.1.0: version "8.1.0" @@ -12544,14 +12648,6 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-lmdb@^0.9.5: - version "0.9.5" - resolved "https://registry.yarnpkg.com/node-lmdb/-/node-lmdb-0.9.5.tgz#8360b90f288a4445d8f10dad202f61390b1db56c" - integrity sha512-NvRoe7nU0qNuKJuIiyJ38T2H2kj8nJumY36envJFwFEOIs4+uff21dtLJaR9ExuOVh03Xqdl0wvvuYfDwKjT5g== - dependencies: - nan "^2.14.1" - node-gyp-build "^4.2.3" - node-releases@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" @@ -12962,6 +13058,11 @@ ora@^5.2.0, ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +ordered-binary@^1.2.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.2.5.tgz#6208c45067eae9d14b8f44791a1d7037adad9147" + integrity sha512-djRmZoEpOGvIRW7ufsCDHtvcUa18UC9TxnPbHhSVFZHsoyg0dtut1bWtBZ/fmxdPN62oWXrV6adM7NoWU+CneA== + os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -17107,6 +17208,11 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +weak-lru-cache@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19" + integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"