Skip to content

Commit ef87997

Browse files
committed
feat: remove .jsonlines hack from simple swing store
1 parent 94c1688 commit ef87997

File tree

8 files changed

+61
-225
lines changed

8 files changed

+61
-225
lines changed

packages/agoric-cli/lib/open.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import crypto from 'crypto';
55
import path from 'path';
66
import os from 'os';
77

8-
import { openSwingStore } from '@agoric/swing-store-simple';
8+
import { openSwingStore } from '@agoric/swing-store-lmdb';
99

1010
import { assert, details as X } from '@agoric/assert';
1111

@@ -43,7 +43,7 @@ export async function getAccessToken(port, powers = {}) {
4343
await fs.mkdir(sharedStateDir, { mode: 0o700, recursive: true });
4444

4545
// Ensure an access token exists.
46-
const { kvStore, commit, close } = openSwingStore(sharedStateDir, 'state');
46+
const { kvStore, commit, close } = openSwingStore(sharedStateDir);
4747
const accessTokenKey = `accessToken/${port}`;
4848
if (!kvStore.has(accessTokenKey)) {
4949
kvStore.set(accessTokenKey, await generateAccessToken());

packages/agoric-cli/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"@agoric/install-ses": "^0.5.13",
3232
"@agoric/nat": "^4.0.0",
3333
"@agoric/promise-kit": "^0.2.13",
34-
"@agoric/swing-store-simple": "^0.3.9",
34+
"@agoric/swing-store-lmdb": "^0.4.11",
3535
"@iarna/toml": "^2.2.3",
3636
"anylogger": "^0.21.0",
3737
"chalk": "^2.4.2",

packages/solo/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
"@agoric/spawner": "^0.4.14",
4040
"@agoric/store": "^0.4.14",
4141
"@agoric/swing-store-lmdb": "^0.4.12",
42-
"@agoric/swing-store-simple": "^0.3.9",
4342
"@agoric/swingset-vat": "^0.17.2",
4443
"@agoric/vats": "^0.2.2",
4544
"anylogger": "^0.21.0",

packages/solo/src/access-token.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import crypto from 'crypto';
33
import os from 'os';
44
import path from 'path';
55

6-
import { openSwingStore } from '@agoric/swing-store-simple';
6+
import { openSwingStore } from '@agoric/swing-store-lmdb';
77

88
// Adapted from https://stackoverflow.com/a/43866992/14073862
99
export function generateAccessToken({
@@ -32,7 +32,7 @@ export async function getAccessToken(port) {
3232
await fs.promises.mkdir(sharedStateDir, { mode: 0o700, recursive: true });
3333

3434
// Ensure an access token exists.
35-
const { kvStore, commit, close } = openSwingStore(sharedStateDir, 'state');
35+
const { kvStore, commit, close } = openSwingStore(sharedStateDir);
3636
const accessTokenKey = `accessToken/${port}`;
3737
if (!kvStore.has(accessTokenKey)) {
3838
kvStore.set(accessTokenKey, await generateAccessToken());

packages/swing-store-lmdb/test/test-state.js

-12
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import 'node-lmdb';
44
import '@agoric/install-ses';
55

66
import fs from 'fs';
7-
import path from 'path';
87

98
import test from 'ava';
109
import { getAllState } from '@agoric/swing-store-simple';
@@ -145,14 +144,3 @@ test('streamStore mode interlock', t => {
145144
commit();
146145
close();
147146
});
148-
149-
test('rejectSimple under SES', t => {
150-
const simpleDir = 'testdb-simple';
151-
t.teardown(() => fs.rmdirSync(simpleDir, { recursive: true }));
152-
fs.mkdirSync(simpleDir, { recursive: true });
153-
fs.writeFileSync(
154-
path.resolve(simpleDir, 'swingset-kernel-state.jsonlines'),
155-
'some data\n',
156-
);
157-
t.is(isSwingStore(simpleDir), false);
158-
});

packages/swing-store-simple/src/simpleSwingStore.js

+48-139
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
// @ts-check
2-
import fs from 'fs';
3-
import path from 'path';
4-
import Readlines from 'n-readlines';
5-
62
import { assert, details as X, q } from '@agoric/assert';
73

84
/**
@@ -35,29 +31,14 @@ import { assert, details as X, q } from '@agoric/assert';
3531
* }} SwingStore
3632
*/
3733

38-
function safeUnlink(filePath) {
39-
try {
40-
fs.unlinkSync(filePath);
41-
} catch (e) {
42-
if (e.code !== 'ENOENT') {
43-
throw e;
44-
}
45-
}
46-
}
34+
const streamPeek = new WeakMap(); // for tests to get raw access to the streams
4735

4836
/**
49-
* Create a new instance of a RAM-based implementation of the Storage API.
50-
*
51-
* The "Storage API" is a set of functions { has, getKeys, get, set, delete }
52-
* that work on string keys and accept string values. A lot of kernel-side
53-
* code expects to get an object that implements the Storage API.
37+
* Do the work of `initSwingStore` and `openSwingStore`.
5438
*
55-
* @returns {{
56-
* kvStore: KVStore, // the storage API object itself
57-
* state: any, // the underlying map that holds the state in memory
58-
* }}
39+
* @returns {SwingStore}
5940
*/
60-
function makeStorageInMemory() {
41+
function makeSwingStore() {
6142
const state = new Map();
6243

6344
/**
@@ -153,78 +134,6 @@ function makeStorageInMemory() {
153134
delete: del,
154135
};
155136

156-
return harden({ kvStore, state });
157-
}
158-
159-
const streamPeek = new WeakMap(); // for tests to get raw access to the streams
160-
161-
/**
162-
* Do the work of `initSwingStore` and `openSwingStore`.
163-
*
164-
* @param {string} [dirPath] Path to a directory in which database files may be kept, or
165-
* null.
166-
* @param {boolean} [forceReset] If true, initialize the database to an empty state
167-
*
168-
* @returns {SwingStore}
169-
*/
170-
function makeSwingStore(dirPath, forceReset = false) {
171-
const { kvStore, state } = makeStorageInMemory();
172-
173-
let storeFile;
174-
if (dirPath) {
175-
fs.mkdirSync(dirPath, { recursive: true });
176-
storeFile = path.resolve(dirPath, 'swingset-kernel-state.jsonlines');
177-
if (forceReset) {
178-
safeUnlink(storeFile);
179-
} else {
180-
let lines;
181-
try {
182-
lines = new Readlines(storeFile);
183-
} catch (e) {
184-
// storeFile will be missing the first time we try to use it. That's OK;
185-
// commit will create it.
186-
if (e.code !== 'ENOENT') {
187-
throw e;
188-
}
189-
}
190-
if (lines) {
191-
let line = lines.next();
192-
while (line) {
193-
// @ts-ignore JSON.parse can take a Buffer
194-
const [key, value] = JSON.parse(line);
195-
kvStore.set(key, value);
196-
line = lines.next();
197-
}
198-
}
199-
}
200-
}
201-
202-
/**
203-
* Commit unsaved changes.
204-
*/
205-
function commit() {
206-
if (dirPath) {
207-
const tempFile = `${storeFile}.tmp`;
208-
const fd = fs.openSync(tempFile, 'w');
209-
210-
for (const [key, value] of state.entries()) {
211-
const line = JSON.stringify([key, value]);
212-
fs.writeSync(fd, line);
213-
fs.writeSync(fd, '\n');
214-
}
215-
fs.closeSync(fd);
216-
fs.renameSync(tempFile, storeFile);
217-
}
218-
}
219-
220-
/**
221-
* Close the "database", abandoning any changes made since the last commit
222-
* (if you want to save them, call commit() first).
223-
*/
224-
function close() {
225-
// Nothing to do here.
226-
}
227-
228137
/** @type {Map<string, Array<string>>} */
229138
const streams = new Map();
230139
/** @type {Map<string, string>} */
@@ -336,53 +245,75 @@ function makeSwingStore(dirPath, forceReset = false) {
336245
return harden({ itemCount: position.itemCount + 1 });
337246
}
338247

339-
const streamStore = harden({
248+
const streamStore = {
340249
readStream,
341250
writeStreamItem,
342251
closeStream,
343252
STREAM_START,
344-
});
253+
};
254+
255+
/**
256+
* Commit unsaved changes.
257+
*/
258+
function commit() {
259+
// Nothing to do here.
260+
}
261+
262+
/**
263+
* Close the "database", abandoning any changes made since the last commit
264+
* (if you want to save them, call commit() first).
265+
*/
266+
function close() {
267+
// Nothing to do here.
268+
}
345269

346270
streamPeek.set(streamStore, streams);
347271

348272
return harden({ kvStore, streamStore, commit, close });
349273
}
350274

351275
/**
352-
* Create a swingset store that is an in-memory map, normally backed by JSON
353-
* serialized to a text file. If there is an existing store at the given
354-
* `dirPath`, it will be reinitialized to an empty state.
276+
* Create a swingset store that is an in-memory map.
355277
*
356-
* @param {string=} dirPath Path to a directory in which database files may be kept.
357-
* This directory need not actually exist yet (if it doesn't it will be
358-
* created) but it is reserved (by the caller) for the exclusive use of this
359-
* swing store instance. If this is nullish, the swing store created will
360-
* have no backing store and thus be non-persistent.
278+
* @param {string=} dirPath Optional path to a directory in which database files
279+
* might be kept, if this were a persistent form of swingset store. If a path
280+
* is provided, a warning will be output to the console. This parameter is
281+
* provided so that the in-memory store may be substituted for a persistent
282+
* store for testing or debugging purposes without needing to change the
283+
* client.
361284
*
362285
* @returns {SwingStore}
363286
*/
364287
export function initSwingStore(dirPath) {
365-
if (dirPath !== null && dirPath !== undefined && `${dirPath}` !== dirPath) {
366-
throw new Error('dirPath must be a string or nullish');
288+
if (dirPath) {
289+
console.log(
290+
`Warning: initSwingStore ignoring dirPath, simpleStore is memory only`,
291+
);
367292
}
368-
return makeSwingStore(dirPath, true);
293+
return makeSwingStore();
369294
}
370295

371296
/**
372-
* Open a swingset store that is an in-memory map, backed by JSON serialized to
373-
* a text file. If there is no existing store at the given `dirPath`, a new,
374-
* empty store will be created.
297+
* Open a swingset store that is an in-memory map. Note that "open" is a
298+
* misnomer here, because you will always get a fresh, empty store. This entry
299+
* point is provided for testing purposes only.
375300
*
376-
* @param {string} dirPath Path to a directory in which database files may be kept.
377-
* This directory need not actually exist yet (if it doesn't it will be
378-
* created) but it is reserved (by the caller) for the exclusive use of this
379-
* swing store instance.
301+
* @param {string=} dirPath Optional path to a directory in which database files
302+
* might be kept, if this were a persistent form of swingset store. If a path
303+
* is provided, a warning will be output to the console. This parameter is
304+
* provided so that the in-memory store may be substituted for a persistent
305+
* store for testing or debugging purposes without needing to change the
306+
* client.
380307
*
381308
* @returns {SwingStore}
382309
*/
383310
export function openSwingStore(dirPath) {
384-
assert.typeof(dirPath, 'string');
385-
return makeSwingStore(dirPath, false);
311+
if (dirPath) {
312+
console.log(
313+
`Warning: openSwingStore ignoring dirPath, simpleStore is memory only`,
314+
);
315+
}
316+
return makeSwingStore();
386317
}
387318

388319
/**
@@ -445,25 +376,3 @@ export function setAllState(swingStore, stuff) {
445376
streamStore.closeStream(streamName);
446377
}
447378
}
448-
449-
/**
450-
* Is this directory a compatible swing store?
451-
*
452-
* @param {string} dirPath Path to a directory in which database files might be present.
453-
* This directory need not actually exist
454-
*
455-
* @returns {boolean}
456-
* If the directory is present and contains the files created by initSwingStore
457-
* or openSwingStore, returns true. Else returns false.
458-
*
459-
*/
460-
export function isSwingStore(dirPath) {
461-
assert.typeof(dirPath, 'string');
462-
if (fs.existsSync(dirPath)) {
463-
const storeFile = path.resolve(dirPath, 'swingset-kernel-state.jsonlines');
464-
if (fs.existsSync(storeFile)) {
465-
return true;
466-
}
467-
}
468-
return false;
469-
}

packages/swing-store-simple/test/test-state.js

+5-43
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
import '@agoric/install-ses';
22

3-
import fs from 'fs';
4-
import path from 'path';
5-
63
import test from 'ava';
7-
import {
8-
initSwingStore,
9-
openSwingStore,
10-
getAllState,
11-
isSwingStore,
12-
} from '../src/simpleSwingStore';
13-
14-
function testKVStore(t, store) {
4+
import { initSwingStore, getAllState } from '../src/simpleSwingStore';
5+
6+
test('kvStore read/write', t => {
7+
const store = initSwingStore();
158
const kvStore = store.kvStore;
9+
1610
t.falsy(kvStore.has('missing'));
1711
t.is(kvStore.get('missing'), undefined);
1812

@@ -44,38 +38,6 @@ function testKVStore(t, store) {
4438
streamStuff: new Map(),
4539
};
4640
t.deepEqual(getAllState(store), reference, 'check state after changes');
47-
}
48-
49-
test('storageInMemory', t => {
50-
const store = initSwingStore();
51-
testKVStore(t, store);
52-
});
53-
54-
test('storageInFile', t => {
55-
const dbDir = 'testdb';
56-
t.teardown(() => fs.rmdirSync(dbDir, { recursive: true }));
57-
fs.rmdirSync(dbDir, { recursive: true });
58-
t.is(isSwingStore(dbDir), false);
59-
const store = initSwingStore(dbDir);
60-
const { commit, close } = store;
61-
testKVStore(t, store);
62-
commit();
63-
const before = getAllState(store);
64-
close();
65-
t.is(isSwingStore(dbDir), true);
66-
67-
const afterStore = openSwingStore(dbDir);
68-
t.deepEqual(getAllState(afterStore), before, 'check state after reread');
69-
t.is(isSwingStore(dbDir), true);
70-
});
71-
72-
test('rejectLMDB', t => {
73-
const notSimpleDir = 'testdb-lmdb';
74-
t.teardown(() => fs.rmdirSync(notSimpleDir, { recursive: true }));
75-
fs.mkdirSync(notSimpleDir, { recursive: true });
76-
fs.writeFileSync(path.resolve(notSimpleDir, 'data.mdb'), 'some data\n');
77-
fs.writeFileSync(path.resolve(notSimpleDir, 'lock.mdb'), 'lock stuff\n');
78-
t.is(isSwingStore(notSimpleDir), false);
7941
});
8042

8143
test('streamStore read/write', t => {

0 commit comments

Comments
 (0)