From 4380b99118f21863d78a9ea67034f7f8cf629fb9 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:28:11 -0700 Subject: [PATCH 1/5] tmp: testing concurrent writes with worker threads --- packages/blockstore-fs/package.json | 3 +- .../test/fixtures/writer-worker.ts | 18 +++++++++++ packages/blockstore-fs/test/index.spec.ts | 30 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 packages/blockstore-fs/test/fixtures/writer-worker.ts diff --git a/packages/blockstore-fs/package.json b/packages/blockstore-fs/package.json index 469a2e91..78abc2ac 100644 --- a/packages/blockstore-fs/package.json +++ b/packages/blockstore-fs/package.json @@ -87,6 +87,7 @@ }, "devDependencies": { "aegir": "^42.2.3", - "interface-blockstore-tests": "^6.0.0" + "interface-blockstore-tests": "^6.0.0", + "threads": "^1.7.0" } } diff --git a/packages/blockstore-fs/test/fixtures/writer-worker.ts b/packages/blockstore-fs/test/fixtures/writer-worker.ts new file mode 100644 index 00000000..eb694d6d --- /dev/null +++ b/packages/blockstore-fs/test/fixtures/writer-worker.ts @@ -0,0 +1,18 @@ +import { expose } from 'threads/worker' +import { FsBlockstore } from '../../src/index.js' +import { CID } from 'multiformats/cid' + +let fs: FsBlockstore +expose({ + isReady(path) { + fs = new FsBlockstore(path) + return fs.open() + }, + put(cidString, value) { + const key = CID.parse(cidString) + return fs.put(key, value) + }, + close() { + return fs.close() + } +}) diff --git a/packages/blockstore-fs/test/index.spec.ts b/packages/blockstore-fs/test/index.spec.ts index 696bacbc..73c31fd5 100644 --- a/packages/blockstore-fs/test/index.spec.ts +++ b/packages/blockstore-fs/test/index.spec.ts @@ -8,6 +8,7 @@ import { base32 } from 'multiformats/bases/base32' import { CID } from 'multiformats/cid' import { FsBlockstore } from '../src/index.js' import { FlatDirectory, NextToLast } from '../src/sharding.js' +import { spawn, Thread, Worker } from "threads" const utf8Encoder = new TextEncoder() @@ -158,4 +159,33 @@ describe('FsBlockstore', () => { expect(res).to.deep.equal(value) }) + + it('can survive concurrent writes with worker threads', async () => { + const dir = path.join(os.tmpdir(), `test-${Math.random()}`) + const key = CID.parse('QmeimKZyjcBnuXmAD9zMnSjM9JodTbgGT3gutofkTqz9rE') + + const workers = await Promise.all(Array.from({ length: 10 }, async () => { + const worker = await spawn(new Worker('./fixtures/writer-worker.js')) + await worker.isReady(dir) + return worker + })) + + const value = utf8Encoder.encode('Hello world') + await Promise.all(workers.map(async (worker) => { + return Promise.all(Array.from({ length: 100 }, async () => { + try { + await worker.put(key.toString(), value) + } catch (e) { + console.error(e) + } + })) + })) + + const fs = new FsBlockstore(dir) + await fs.open() + const res = await fs.get(key) + + expect(res).to.deep.equal(value) + await Promise.all(workers.map(async (worker) => Thread.terminate(worker))) + }) }) From 43726ef3bd641008f2651e6c1979f169393e1f31 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:04:15 -0700 Subject: [PATCH 2/5] test: add separate-thread concurrency test --- packages/blockstore-fs/src/index.ts | 3 +- .../test/fixtures/writer-worker.ts | 25 ++++++++---- packages/blockstore-fs/test/index.spec.ts | 38 ++++++++++--------- packages/datastore-fs/package.json | 3 +- packages/datastore-fs/src/index.ts | 2 +- .../test/fixtures/writer-worker.ts | 27 +++++++++++++ packages/datastore-fs/test/index.spec.ts | 32 ++++++++++++++++ 7 files changed, 100 insertions(+), 30 deletions(-) create mode 100644 packages/datastore-fs/test/fixtures/writer-worker.ts diff --git a/packages/blockstore-fs/src/index.ts b/packages/blockstore-fs/src/index.ts index b2e0af65..85d47bd9 100644 --- a/packages/blockstore-fs/src/index.ts +++ b/packages/blockstore-fs/src/index.ts @@ -37,7 +37,7 @@ async function writeFile (file: string, contents: Uint8Array): Promise { try { await writeAtomic(file, contents) } catch (err: any) { - if (err.code === 'EPERM' && err.syscall === 'rename') { + if (err.syscall === 'rename' && ['ENOENT', 'EPERM'].includes(err.code)) { // fast-write-atomic writes a file to a temp location before renaming it. // On Windows, if the final file already exists this error is thrown. // No such error is thrown on Linux/Mac @@ -48,7 +48,6 @@ async function writeFile (file: string, contents: Uint8Array): Promise { // attempts to write the same block by two different function calls return } - throw err } } diff --git a/packages/blockstore-fs/test/fixtures/writer-worker.ts b/packages/blockstore-fs/test/fixtures/writer-worker.ts index eb694d6d..e587a0d4 100644 --- a/packages/blockstore-fs/test/fixtures/writer-worker.ts +++ b/packages/blockstore-fs/test/fixtures/writer-worker.ts @@ -1,18 +1,27 @@ +import { CID } from 'multiformats/cid' import { expose } from 'threads/worker' import { FsBlockstore } from '../../src/index.js' -import { CID } from 'multiformats/cid' let fs: FsBlockstore expose({ - isReady(path) { + async isReady (path) { fs = new FsBlockstore(path) - return fs.open() + try { + await fs.open() + return true + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error opening blockstore', err) + return false + } }, - put(cidString, value) { + async put (cidString, value) { const key = CID.parse(cidString) - return fs.put(key, value) - }, - close() { - return fs.close() + try { + return await fs.put(key, value) + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error putting block', err) + } } }) diff --git a/packages/blockstore-fs/test/index.spec.ts b/packages/blockstore-fs/test/index.spec.ts index 73c31fd5..575004da 100644 --- a/packages/blockstore-fs/test/index.spec.ts +++ b/packages/blockstore-fs/test/index.spec.ts @@ -6,9 +6,9 @@ import { expect } from 'aegir/chai' import { interfaceBlockstoreTests } from 'interface-blockstore-tests' import { base32 } from 'multiformats/bases/base32' import { CID } from 'multiformats/cid' +import { spawn, Thread, Worker } from 'threads' import { FsBlockstore } from '../src/index.js' import { FlatDirectory, NextToLast } from '../src/sharding.js' -import { spawn, Thread, Worker } from "threads" const utf8Encoder = new TextEncoder() @@ -160,32 +160,34 @@ describe('FsBlockstore', () => { expect(res).to.deep.equal(value) }) - it('can survive concurrent writes with worker threads', async () => { + /** + * This test spawns 10 workers that concurrently write to the same blockstore. + * it's different from the previous test because it uses workers to write to the blockstore + * which means that the writes are happening in parallel in different threads. + */ + it('can survive concurrent worker writes', async () => { const dir = path.join(os.tmpdir(), `test-${Math.random()}`) const key = CID.parse('QmeimKZyjcBnuXmAD9zMnSjM9JodTbgGT3gutofkTqz9rE') - - const workers = await Promise.all(Array.from({ length: 10 }, async () => { + const workers = await Promise.all(new Array(10).fill(0).map(async () => { const worker = await spawn(new Worker('./fixtures/writer-worker.js')) await worker.isReady(dir) return worker })) - const value = utf8Encoder.encode('Hello world') - await Promise.all(workers.map(async (worker) => { - return Promise.all(Array.from({ length: 100 }, async () => { - try { - await worker.put(key.toString(), value) - } catch (e) { - console.error(e) - } + try { + const value = utf8Encoder.encode('Hello world') + // 100 iterations of looping over all workers and putting the same key value pair + await Promise.all(new Array(100).fill(0).map(async () => { + return Promise.all(workers.map(async (worker) => worker.put(key.toString(), value))) })) - })) - const fs = new FsBlockstore(dir) - await fs.open() - const res = await fs.get(key) + const fs = new FsBlockstore(dir) + await fs.open() + const res = await fs.get(key) - expect(res).to.deep.equal(value) - await Promise.all(workers.map(async (worker) => Thread.terminate(worker))) + expect(res).to.deep.equal(value) + } finally { + await Promise.all(workers.map(async (worker) => Thread.terminate(worker))) + } }) }) diff --git a/packages/datastore-fs/package.json b/packages/datastore-fs/package.json index db04aeeb..2cb1d12b 100644 --- a/packages/datastore-fs/package.json +++ b/packages/datastore-fs/package.json @@ -67,6 +67,7 @@ "@types/mkdirp": "^2.0.0", "aegir": "^42.2.3", "interface-datastore-tests": "^5.0.0", - "ipfs-utils": "^9.0.14" + "ipfs-utils": "^9.0.14", + "threads": "^1.7.0" } } diff --git a/packages/datastore-fs/src/index.ts b/packages/datastore-fs/src/index.ts index bd19b657..fa771408 100644 --- a/packages/datastore-fs/src/index.ts +++ b/packages/datastore-fs/src/index.ts @@ -37,7 +37,7 @@ async function writeFile (path: string, contents: Uint8Array): Promise { try { await writeAtomic(path, contents) } catch (err: any) { - if (err.code === 'EPERM' && err.syscall === 'rename') { + if (err.syscall === 'rename' && ['ENOENT', 'EPERM'].includes(err.code)) { // fast-write-atomic writes a file to a temp location before renaming it. // On Windows, if the final file already exists this error is thrown. // No such error is thrown on Linux/Mac diff --git a/packages/datastore-fs/test/fixtures/writer-worker.ts b/packages/datastore-fs/test/fixtures/writer-worker.ts new file mode 100644 index 00000000..0edb5f4e --- /dev/null +++ b/packages/datastore-fs/test/fixtures/writer-worker.ts @@ -0,0 +1,27 @@ +import { Key } from 'interface-datastore' +import { expose } from 'threads/worker' +import { FsDatastore } from '../../src/index.js' + +let fs: FsDatastore +expose({ + async isReady (path) { + fs = new FsDatastore(path) + try { + await fs.open() + return true + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error opening blockstore', err) + return false + } + }, + async put (keyString, value) { + const key = new Key(keyString) + try { + return await fs.put(key, value) + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error putting block', err) + } + } +}) diff --git a/packages/datastore-fs/test/index.spec.ts b/packages/datastore-fs/test/index.spec.ts index 273c30d8..9a9f2e86 100644 --- a/packages/datastore-fs/test/index.spec.ts +++ b/packages/datastore-fs/test/index.spec.ts @@ -6,6 +6,7 @@ import { ShardingDatastore, shard } from 'datastore-core' import { Key } from 'interface-datastore' import { interfaceDatastoreTests } from 'interface-datastore-tests' import tempdir from 'ipfs-utils/src/temp-dir.js' +import { spawn, Thread, Worker } from 'threads' import { FsDatastore } from '../src/index.js' const utf8Encoder = new TextEncoder() @@ -170,4 +171,35 @@ describe('FsDatastore', () => { expect(res).to.deep.equal(value) }) + + /** + * This test spawns 10 workers that concurrently write to the same blockstore. + * it's different from the previous test because it uses workers to write to the blockstore + * which means that the writes are happening in parallel in different threads. + */ + it('can survive concurrent worker writes', async () => { + const dir = tempdir() + const key = new Key('CIQGFTQ7FSI2COUXWWLOQ45VUM2GUZCGAXLWCTOKKPGTUWPXHBNIVOY') + const workers = await Promise.all(new Array(10).fill(0).map(async () => { + const worker = await spawn(new Worker('./fixtures/writer-worker.js')) + await worker.isReady(dir) + return worker + })) + + try { + const value = utf8Encoder.encode('Hello world') + // 100 iterations of looping over all workers and putting the same key value pair + await Promise.all(new Array(100).fill(0).map(async () => { + return Promise.all(workers.map(async (worker) => worker.put(key.toString(), value))) + })) + + const fs = new FsDatastore(dir) + await fs.open() + const res = await fs.get(key) + + expect(res).to.deep.equal(value) + } finally { + await Promise.all(workers.map(async (worker) => Thread.terminate(worker))) + } + }) }) From f435854516c370b73e10b37aaaf239227f8462b5 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:18:23 -0700 Subject: [PATCH 3/5] chore: re-throw errors from writer-worker --- packages/blockstore-fs/test/fixtures/writer-worker.ts | 3 ++- packages/datastore-fs/test/fixtures/writer-worker.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/blockstore-fs/test/fixtures/writer-worker.ts b/packages/blockstore-fs/test/fixtures/writer-worker.ts index e587a0d4..67a7d9cc 100644 --- a/packages/blockstore-fs/test/fixtures/writer-worker.ts +++ b/packages/blockstore-fs/test/fixtures/writer-worker.ts @@ -12,7 +12,7 @@ expose({ } catch (err) { // eslint-disable-next-line no-console console.error('Error opening blockstore', err) - return false + throw err } }, async put (cidString, value) { @@ -22,6 +22,7 @@ expose({ } catch (err) { // eslint-disable-next-line no-console console.error('Error putting block', err) + throw err } } }) diff --git a/packages/datastore-fs/test/fixtures/writer-worker.ts b/packages/datastore-fs/test/fixtures/writer-worker.ts index 0edb5f4e..49cdfef2 100644 --- a/packages/datastore-fs/test/fixtures/writer-worker.ts +++ b/packages/datastore-fs/test/fixtures/writer-worker.ts @@ -12,7 +12,7 @@ expose({ } catch (err) { // eslint-disable-next-line no-console console.error('Error opening blockstore', err) - return false + throw err } }, async put (keyString, value) { @@ -22,6 +22,7 @@ expose({ } catch (err) { // eslint-disable-next-line no-console console.error('Error putting block', err) + throw err } } }) From c6df8dd81794f3b07b8c032bf1c841915cf7f5aa Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Fri, 9 Aug 2024 05:02:28 -0700 Subject: [PATCH 4/5] fix: replace fast-atomic-write with steno (#285) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #284 fast-write-atomic hasn't been updated in 5 years, is CJS, and is slower than steno (updated 2 months ago). Benchmarks for various content-types & libraries (though we only use Uint8Arrays) can be found at https://github.com/SgtPooki/fast-write-atomic#benchmarks However, there may be further room for improvement by moving to [fs.createWriteStream](https://nodejs.org/api/fs.html#fscreatewritestreampath-options) ``` ╰─ ✔ ❯ hyperfine --parameter-list branch 284-chore-replace-fast-write-atomic-with-steno,main --setup "git switch {branch} && npm run reset && npm i && npm run build" --runs 20 -w 1 "npm run test:node" Benchmark 1: npm run test:node (branch = 284-chore-replace-fast-write-atomic-with-steno) Time (mean ± σ): 27.212 s ± 0.832 s [User: 34.810 s, System: 6.051 s] Range (min … max): 25.927 s … 29.324 s 20 runs Benchmark 2: npm run test:node (branch = main) Time (mean ± σ): 42.971 s ± 0.637 s [User: 35.297 s, System: 7.534 s] Range (min … max): 42.178 s … 44.796 s 20 runs Summary npm run test:node (branch = 284-chore-replace-fast-write-atomic-with-steno) ran 1.58 ± 0.05 times faster than npm run test:node (branch = main) ``` --- ### Updated benchmarks of `npm run test` as of 2024-04-19 ``` ╭─    ~/code/work/protocol.ai/ipfs/js-stores    main ?1 ╰─ ✔ ❯ hyperfine --parameter-list branch main,test/not-same-event-loop-concurrency,284-chore-replace-fast-write-atomic-with-steno --setup "git switch {branch} && npm run reset && npm i && npm run build && cd packages/datastore-fs" "npm run test" Benchmark 1: npm run test (branch = main) Time (mean ± σ): 99.415 s ± 2.918 s [User: 69.659 s, System: 23.361 s] Range (min … max): 96.134 s … 105.200 s 10 runs Benchmark 2: npm run test (branch = test/not-same-event-loop-concurrency) Time (mean ± σ): 103.456 s ± 3.186 s [User: 74.442 s, System: 25.261 s] Range (min … max): 98.813 s … 108.429 s 10 runs Benchmark 3: npm run test (branch = 284-chore-replace-fast-write-atomic-with-steno) Time (mean ± σ): 80.308 s ± 2.107 s [User: 74.331 s, System: 22.228 s] Range (min … max): 78.219 s … 84.277 s 10 runs Summary npm run test (branch = 284-chore-replace-fast-write-atomic-with-steno) ran 1.24 ± 0.05 times faster than npm run test (branch = main) 1.29 ± 0.05 times faster than npm run test (branch = test/not-same-event-loop-concurrency) [49m1.944s] ╭─    ~/code/work/protocol.ai/ipfs/js-stores    284-chore-re…c-with-steno ?1 ╰─ ✔ ❯ hyperfine --parameter-list branch main,test/not-same-event-loop-concurrency,284-chore-replace-fast-write-atomic-with-steno --setup "git switch {branch} && npm run reset && npm i && npm run build && cd packages/blockstore-fs" "npm run test" Benchmark 1: npm run test (branch = main) Time (mean ± σ): 98.840 s ± 2.612 s [User: 68.486 s, System: 22.585 s] Range (min … max): 97.005 s … 104.396 s 10 runs Benchmark 2: npm run test (branch = test/not-same-event-loop-concurrency) Time (mean ± σ): 105.307 s ± 2.335 s [User: 72.442 s, System: 24.766 s] Range (min … max): 101.167 s … 109.007 s 10 runs Benchmark 3: npm run test (branch = 284-chore-replace-fast-write-atomic-with-steno) Time (mean ± σ): 77.012 s ± 1.829 s [User: 74.442 s, System: 21.938 s] Range (min … max): 75.258 s … 80.825 s 10 runs Summary npm run test (branch = 284-chore-replace-fast-write-atomic-with-steno) ran 1.28 ± 0.05 times faster than npm run test (branch = main) 1.37 ± 0.04 times faster than npm run test (branch = test/not-same-event-loop-concurrency) ``` --- packages/blockstore-fs/package.json | 4 ++-- packages/blockstore-fs/src/index.ts | 14 +++++--------- packages/datastore-fs/package.json | 4 ++-- packages/datastore-fs/src/index.ts | 14 +++++--------- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/packages/blockstore-fs/package.json b/packages/blockstore-fs/package.json index 78abc2ac..87a53f60 100644 --- a/packages/blockstore-fs/package.json +++ b/packages/blockstore-fs/package.json @@ -77,13 +77,13 @@ }, "dependencies": { "blockstore-core": "^4.0.0", - "fast-write-atomic": "^0.2.1", "interface-blockstore": "^5.0.0", "interface-store": "^5.0.0", "it-glob": "^2.0.6", "it-map": "^3.0.5", "it-parallel-batch": "^3.0.4", - "multiformats": "^13.0.1" + "multiformats": "^13.0.1", + "steno": "^4.0.2" }, "devDependencies": { "aegir": "^42.2.3", diff --git a/packages/blockstore-fs/src/index.ts b/packages/blockstore-fs/src/index.ts index 85d47bd9..242a88e5 100644 --- a/packages/blockstore-fs/src/index.ts +++ b/packages/blockstore-fs/src/index.ts @@ -14,33 +14,29 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { promisify } from 'node:util' import { Errors } from 'blockstore-core' -// @ts-expect-error no types -import fwa from 'fast-write-atomic' import glob from 'it-glob' import map from 'it-map' import parallelBatch from 'it-parallel-batch' +import { Writer } from 'steno' import { NextToLast, type ShardingStrategy } from './sharding.js' import type { Blockstore, Pair } from 'interface-blockstore' import type { AwaitIterable } from 'interface-store' import type { CID } from 'multiformats/cid' -const writeAtomic = promisify(fwa) - /** * Write a file atomically */ async function writeFile (file: string, contents: Uint8Array): Promise { try { - await writeAtomic(file, contents) + const writer = new Writer(file) + await writer.write(contents) } catch (err: any) { if (err.syscall === 'rename' && ['ENOENT', 'EPERM'].includes(err.code)) { - // fast-write-atomic writes a file to a temp location before renaming it. - // On Windows, if the final file already exists this error is thrown. - // No such error is thrown on Linux/Mac + // steno writes a file to a temp location before renaming it. + // If the final file already exists this error is thrown. // Make sure we can read & write to this file await fs.access(file, fs.constants.F_OK | fs.constants.W_OK) diff --git a/packages/datastore-fs/package.json b/packages/datastore-fs/package.json index 2cb1d12b..72502e77 100644 --- a/packages/datastore-fs/package.json +++ b/packages/datastore-fs/package.json @@ -56,12 +56,12 @@ }, "dependencies": { "datastore-core": "^9.0.0", - "fast-write-atomic": "^0.2.1", "interface-datastore": "^8.0.0", "interface-store": "^5.0.0", "it-glob": "^2.0.6", "it-map": "^3.0.5", - "it-parallel-batch": "^3.0.4" + "it-parallel-batch": "^3.0.4", + "steno": "^4.0.2" }, "devDependencies": { "@types/mkdirp": "^2.0.0", diff --git a/packages/datastore-fs/src/index.ts b/packages/datastore-fs/src/index.ts index fa771408..c2eff371 100644 --- a/packages/datastore-fs/src/index.ts +++ b/packages/datastore-fs/src/index.ts @@ -14,33 +14,29 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { promisify } from 'util' import { BaseDatastore, Errors } from 'datastore-core' -// @ts-expect-error no types -import fwa from 'fast-write-atomic' import { Key, type KeyQuery, type Pair, type Query } from 'interface-datastore' import glob from 'it-glob' import map from 'it-map' import parallel from 'it-parallel-batch' +import { Writer } from 'steno' import type { AwaitIterable } from 'interface-store' -const writeAtomic = promisify(fwa) - /** * Write a file atomically */ async function writeFile (path: string, contents: Uint8Array): Promise { try { - await writeAtomic(path, contents) + const writer = new Writer(path) + await writer.write(contents) } catch (err: any) { if (err.syscall === 'rename' && ['ENOENT', 'EPERM'].includes(err.code)) { - // fast-write-atomic writes a file to a temp location before renaming it. - // On Windows, if the final file already exists this error is thrown. - // No such error is thrown on Linux/Mac + // steno writes a file to a temp location before renaming it. + // If the final file already exists this error is thrown. // Make sure we can read & write to this file await fs.access(path, fs.constants.F_OK | fs.constants.W_OK) From f39b42288213157964d8b26142250c635a5d77c5 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:16:26 -0500 Subject: [PATCH 5/5] chore: fix lint error --- packages/blockstore-fs/src/index.ts | 2 +- packages/datastore-fs/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blockstore-fs/src/index.ts b/packages/blockstore-fs/src/index.ts index cc1ff832..9b725ee8 100644 --- a/packages/blockstore-fs/src/index.ts +++ b/packages/blockstore-fs/src/index.ts @@ -14,11 +14,11 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { Writer } from 'steno' import { OpenFailedError, type AwaitIterable, PutFailedError, NotFoundError, DeleteFailedError } from 'interface-store' import glob from 'it-glob' import map from 'it-map' import parallelBatch from 'it-parallel-batch' +import { Writer } from 'steno' import { NextToLast } from './sharding.js' import type { ShardingStrategy } from './sharding.js' import type { Blockstore, Pair } from 'interface-blockstore' diff --git a/packages/datastore-fs/src/index.ts b/packages/datastore-fs/src/index.ts index 61680940..7461368c 100644 --- a/packages/datastore-fs/src/index.ts +++ b/packages/datastore-fs/src/index.ts @@ -14,13 +14,13 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { Writer } from 'steno' import { BaseDatastore } from 'datastore-core' import { Key } from 'interface-datastore' import { OpenFailedError, NotFoundError, PutFailedError, DeleteFailedError } from 'interface-store' import glob from 'it-glob' import map from 'it-map' import parallel from 'it-parallel-batch' +import { Writer } from 'steno' import type { KeyQuery, Pair, Query } from 'interface-datastore' import type { AwaitIterable } from 'interface-store'