From 4d8c94a91fa375d82f7b014f37d0bfbf92f6951d Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Thu, 18 Jan 2024 22:13:08 +0000 Subject: [PATCH] feat: add method to unlink storage This adds `MultiCoreIndexer.prototype.unlink()` which unlinks all the index storage. Addresses #26. --- README.md | 4 ++++ index.js | 7 +++++++ lib/core-index-stream.js | 23 ++++++++++++++++++----- lib/multi-core-index-stream.js | 12 ++++++++++++ test/multi-core-indexer.test.js | 30 ++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2a3502d..fa25157 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,10 @@ hypercores already in the indexer. Stop the indexer and flush index state to storage. This will not close the underlying storage - it is up to the consumer to do that. +### indexer.unlink() + +Unlink all index files. This should only be called after `close()` has resolved. + ### indexer.on('index-state', onState) #### onState diff --git a/index.js b/index.js index 2001a78..00dedba 100644 --- a/index.js +++ b/index.js @@ -116,6 +116,13 @@ class MultiCoreIndexer extends TypedEmitter { ]) } + /** + * Unlink all index files. This should only be called after `close()` has resolved. + */ + async unlink() { + await this.#indexStream.unlink() + } + /** @param {Entry[]} entries */ async #handleEntries(entries) { this.#emitState() diff --git a/lib/core-index-stream.js b/lib/core-index-stream.js index 6ef9615..6ef8fe8 100644 --- a/lib/core-index-stream.js +++ b/lib/core-index-stream.js @@ -55,9 +55,15 @@ class CoreIndexStream extends Readable { byteLength: () => 1, }) this.#core = core - this.#createStorage = createStorage this.#handleAppendBound = this.#handleAppend.bind(this) this.#handleDownloadBound = this.#handleDownload.bind(this) + this.#createStorage = async () => { + await this.#core.ready() + const { discoveryKey } = this.#core + /* istanbul ignore next: just to keep TS happy - after core.ready() this is set */ + if (!discoveryKey) throw new Error('Missing discovery key') + return createStorage(getStorageName(discoveryKey)) + } } get remaining() { @@ -105,6 +111,11 @@ class CoreIndexStream extends Readable { this.#inProgressBitfield?.set(index, false) } + async unlink() { + this.#storage ??= await this.#createStorage() + await unlinkStorage(this.#storage) + } + async #destroy() { this.#core.removeListener('append', this.#handleAppendBound) this.#core.removeListener('download', this.#handleDownloadBound) @@ -115,10 +126,7 @@ class CoreIndexStream extends Readable { async #open() { await this.#core.ready() await this.#core.update({ wait: true }) - const { discoveryKey } = this.#core - /* istanbul ignore next: just to keep TS happy - after core.ready() this is set */ - if (!discoveryKey) throw new Error('Missing discovery key') - this.#storage = this.#createStorage(getStorageName(discoveryKey)) + this.#storage ??= await this.#createStorage() this.#indexedBitfield = await Bitfield.open(this.#storage) this.#inProgressBitfield = await new Bitfield() this.#core.on('append', this.#handleAppendBound) @@ -212,3 +220,8 @@ function getStorageName(discoveryKey) { function closeStorage(storage) { return promisify(storage.close.bind(storage))() } + +/** @param {import('random-access-storage')} storage */ +function unlinkStorage(storage) { + return promisify(storage.unlink.bind(storage))() +} diff --git a/lib/multi-core-index-stream.js b/lib/multi-core-index-stream.js index 51fd0cd..9c6a747 100644 --- a/lib/multi-core-index-stream.js +++ b/lib/multi-core-index-stream.js @@ -103,6 +103,18 @@ class MultiCoreIndexStream extends Readable { stream.on('drained', this.#handleDrainedBound) } + /** + * Unlink all index files. This should only be called after close. + */ + async unlink() { + /** @type {Array>} */ + const unlinkPromises = [] + for (const stream of this.#streams.keys()) { + unlinkPromises.push(stream.unlink()) + } + await Promise.all(unlinkPromises) + } + /** @param {any} cb */ _open(cb) { cb() diff --git a/test/multi-core-indexer.test.js b/test/multi-core-indexer.test.js index 62696dc..b2ff9c8 100644 --- a/test/multi-core-indexer.test.js +++ b/test/multi-core-indexer.test.js @@ -329,6 +329,36 @@ test('Entries are re-indexed if index storage reset', async (t) => { t.pass('Indexer closed') }) +test('Entries are re-indexed if index storage unlinked', async (t) => { + const cores = await createMultiple(5) + + const createRAM = ram.reusable() + + const indexer1 = new MultiCoreIndexer(cores, { + batch: async () => {}, + storage: createRAM, + }) + const expected = await generateFixtures(cores, 3) + await indexer1.idle() + await indexer1.close() + await indexer1.unlink() + + /** @type {Entry[]} */ + const entries = [] + const indexer2 = new MultiCoreIndexer(cores, { + batch: async (data) => { + entries.push(...data) + }, + storage: createRAM, + }) + await indexer2.idle() + + t.same(sortEntries(entries), sortEntries(expected)) + + await indexer2.close() + t.pass('Indexer closed') +}) + test('Entries are batched to batchMax when indexing is slower than Hypercore reads', async (t) => { const cores = await createMultiple(5) await generateFixtures(cores, 500)