Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.

Commit ceb5cfd

Browse files
authored
feat: parallel block writes (#97)
* feat: parallel block writes When using the `putMany` datastore method, write blocks in parallel up to a configurable limit. In local testing this almost doubles the throughput of this module for multi-block writes. * chore: use parallel for gets and deletes too
1 parent 5a1373a commit ceb5cfd

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"interface-datastore": "^5.1.1",
4444
"it-glob": "0.0.13",
4545
"it-map": "^1.0.5",
46+
"it-parallel-batch": "^1.0.9",
4647
"mkdirp": "^1.0.4"
4748
},
4849
"devDependencies": {

src/index.js

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const {
1212
Adapter, Key, Errors
1313
} = require('interface-datastore')
1414
const map = require('it-map')
15+
const parallel = require('it-parallel-batch')
1516

1617
const noop = () => {}
1718
const fsAccess = promisify(fs.access || noop)
@@ -25,6 +26,11 @@ const fsUnlink = promisify(fs.unlink || noop)
2526
* @typedef {import('interface-datastore').KeyQuery} KeyQuery
2627
*/
2728

29+
/**
30+
* @template TEntry
31+
* @typedef {import('interface-store').AwaitIterable<TEntry>} AwaitIterable
32+
*/
33+
2834
/**
2935
* Write a file atomically
3036
*
@@ -62,7 +68,7 @@ async function writeFile (path, contents) {
6268
class FsDatastore extends Adapter {
6369
/**
6470
* @param {string} location
65-
* @param {{ createIfMissing?: boolean; errorIfExists?: boolean; extension?: string; } | undefined} [opts]
71+
* @param {{ createIfMissing?: boolean, errorIfExists?: boolean, extension?: string, putManyConcurrency?: number } | undefined} [opts]
6672
*/
6773
constructor (location, opts) {
6874
super()
@@ -71,7 +77,10 @@ class FsDatastore extends Adapter {
7177
this.opts = Object.assign({}, {
7278
createIfMissing: true,
7379
errorIfExists: false,
74-
extension: '.data'
80+
extension: '.data',
81+
deleteManyConcurrency: 50,
82+
getManyConcurrency: 50,
83+
putManyConcurrency: 50
7584
}, opts)
7685
}
7786

@@ -175,6 +184,23 @@ class FsDatastore extends Adapter {
175184
}
176185
}
177186

187+
/**
188+
* @param {AwaitIterable<Pair>} source
189+
* @returns {AsyncIterable<Pair>}
190+
*/
191+
async * putMany (source) {
192+
yield * parallel(
193+
map(source, ({ key, value }) => {
194+
return async () => {
195+
await this.put(key, value)
196+
197+
return { key, value }
198+
}
199+
}),
200+
this.opts.putManyConcurrency
201+
)
202+
}
203+
178204
/**
179205
* Read from the file system without extension.
180206
*
@@ -215,6 +241,38 @@ class FsDatastore extends Adapter {
215241
return data
216242
}
217243

244+
/**
245+
* @param {AwaitIterable<Key>} source
246+
* @returns {AsyncIterable<Uint8Array>}
247+
*/
248+
async * getMany (source) {
249+
yield * parallel(
250+
map(source, key => {
251+
return async () => {
252+
return this.get(key)
253+
}
254+
}),
255+
this.opts.getManyConcurrency
256+
)
257+
}
258+
259+
/**
260+
* @param {AwaitIterable<Key>} source
261+
* @returns {AsyncIterable<Key>}
262+
*/
263+
async * deleteMany (source) {
264+
yield * parallel(
265+
map(source, key => {
266+
return async () => {
267+
await this.delete(key)
268+
269+
return key
270+
}
271+
}),
272+
this.opts.deleteManyConcurrency
273+
)
274+
}
275+
218276
/**
219277
* Check for the existence of the given key.
220278
*
@@ -223,6 +281,7 @@ class FsDatastore extends Adapter {
223281
*/
224282
async has (key) {
225283
const parts = this._encode(key)
284+
226285
try {
227286
await fsAccess(parts.file)
228287
} catch (err) {

0 commit comments

Comments
 (0)