Skip to content

Commit 65d7596

Browse files
authored
feat: use streaming API for blockstore access (#232)
Uses the streaming APIs from interface-datastore to work with data being sent/fetched from the blockstore. The previous behaviour was to use batches for putting streams of blocks - this was bad because we'd end up buffering the whole stream before adding blocks to the blockstore. Instead now we stream the blocks as we get them. * Adds `repo.blockstore.getMany()` function * Passes `options` to underlying datastores to allow passing AbortSignals BREAKING CHANGES: * `repo.blockstore.putMany()` used to return a promise that resolved when all the deletes were done, now it returns an AsyncIterator that yields `{key, value}` objects as the put progresses * `repo.blockstore.deleteMany()` used to return a promise that resolved when all the deletes were done, now it returns an AsyncIterator that yields CID objects as the delete progresses
1 parent 3677213 commit 65d7596

File tree

4 files changed

+266
-117
lines changed

4 files changed

+266
-117
lines changed

README.md

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,22 @@ This is the implementation of the [IPFS repo spec](https://github.com/ipfs/specs
3030
- [API](#api)
3131
- [Setup](#setup)
3232
- [`new Repo(path[, options])`](#new-repopath-options)
33-
- [`Promise repo.init ()`](#promise-repoinit-)
34-
- [`Promise repo.open ()`](#promise-repoopen-)
35-
- [`Promise repo.close ()`](#promise-repoclose-)
36-
- [`Promise<boolean> repo.exists ()`](#promiseboolean-repoexists-)
33+
- [`Promise repo.init()`](#promise-repoinit)
34+
- [`Promise repo.open()`](#promise-repoopen)
35+
- [`Promise repo.close()`](#promise-repoclose)
36+
- [`Promise<boolean> repo.exists()`](#promiseboolean-repoexists)
37+
- [`Promise<Boolean> repo.isInitialized()`](#promiseboolean-repoisinitialized)
3738
- [Repos](#repos)
38-
- [`Promise repo.put (key, value:Buffer)`](#promise-repoput-key-valuebuffer)
39-
- [`Promise<Buffer> repo.get (key)`](#promisebuffer-repoget-key)
40-
- [`Promise<Boolean> repo.isInitialized ()`](#promiseboolean-repoisinitialized-)
41-
- [`Promise repo.blocks.put (block:Block)`](#promise-repoblocksput-blockblock)
42-
- [`Promise repo.blocks.putMany (blocks)`](#promise-repoblocksputmany-blocks)
43-
- [`Promise<Buffer> repo.blocks.get (cid)`](#promisebuffer-repoblocksget-cid)
44-
- [`Promise repo.blocks.delete (cid:CID)`](#promise-repoblocksdelete-cidcid)
45-
- [`Promise repo.blocks.deleteMany (cids)`](#promise-repoblocksdeletemany-cids)
39+
- [`Promise repo.put(key, value:Buffer)`](#promise-repoputkey-valuebuffer)
40+
- [`Promise<Buffer> repo.get(key)`](#promisebuffer-repogetkey)
41+
- [Blocks](#blocks)
42+
- [`Promise<Block> repo.blocks.put(block:Block)`](#promiseblock-repoblocksputblockblock)
43+
- [`AsyncIterator<Block> repo.blocks.putMany(source)`](#asynciteratorblock-repoblocksputmanysource)
44+
- [`Promise<Buffer> repo.blocks.get(cid)`](#promisebuffer-repoblocksgetcid)
45+
- [`AsyncIterable<Buffer> repo.blocks.getMany(source)`](#asynciterablebuffer-repoblocksgetmanysource)
46+
- [`Promise<CID> repo.blocks.delete(cid:CID)`](#promisecid-repoblocksdeletecidcid)
47+
- [`AsyncIterator<CID> repo.blocks.deleteMany(source)`](#asynciteratorcid-repoblocksdeletemanysource)
48+
- [Datastore](#datastore)
4649
- [`repo.datastore`](#repodatastore)
4750
- [Config](#config)
4851
- [`Promise repo.config.set(key:string, value)`](#promise-repoconfigsetkeystring-value)
@@ -51,17 +54,17 @@ This is the implementation of the [IPFS repo spec](https://github.com/ipfs/specs
5154
- [`Promise<Object> repo.config.getAll()`](#promiseobject-repoconfiggetall)
5255
- [`Promise<boolean> repo.config.exists()`](#promiseboolean-repoconfigexists)
5356
- [Version](#version)
54-
- [`Promise<Number> repo.version.get ()`](#promisenumber-repoversionget-)
57+
- [`Promise<Number> repo.version.get()`](#promisenumber-repoversionget)
5558
- [`Promise repo.version.set (version:Number)`](#promise-repoversionset-versionnumber)
5659
- [API Addr](#api-addr)
57-
- [`Promise<String> repo.apiAddr.get ()`](#promisestring-repoapiaddrget-)
58-
- [`Promise repo.apiAddr.set (value)`](#promise-repoapiaddrset-value)
60+
- [`Promise<String> repo.apiAddr.get()`](#promisestring-repoapiaddrget)
61+
- [`Promise repo.apiAddr.set(value)`](#promise-repoapiaddrsetvalue)
5962
- [Status](#status)
60-
- [`Promise<Object> repo.stat ()`](#promiseobject-repostat-)
63+
- [`Promise<Object> repo.stat()`](#promiseobject-repostat)
6164
- [Lock](#lock)
62-
- [`Promise lock.lock (dir)`](#promise-locklock-dir)
63-
- [`Promise closer.close ()`](#promise-closerclose-)
64-
- [`Promise<boolean> lock.locked (dir)`](#promiseboolean-locklocked-dir)
65+
- [`Promise lock.lock(dir)`](#promise-locklockdir)
66+
- [`Promise closer.close()`](#promise-closerclose)
67+
- [`Promise<boolean> lock.locked(dir)`](#promiseboolean-locklockeddir)
6568
- [Notes](#notes)
6669
- [Migrations](#migrations)
6770
- [Contribute](#contribute)
@@ -184,79 +187,84 @@ Arguments:
184187
const repo = new Repo('path/to/repo')
185188
```
186189

187-
#### `Promise repo.init ()`
190+
#### `Promise repo.init()`
188191

189-
Creates the necessary folder structure inside the repo.
192+
Creates the necessary folder structure inside the repo
190193

191-
#### `Promise repo.open ()`
194+
#### `Promise repo.open()`
192195

193-
[Locks](https://en.wikipedia.org/wiki/Record_locking) the repo to prevent conflicts arising from simultaneous access.
196+
[Locks](https://en.wikipedia.org/wiki/Record_locking) the repo to prevent conflicts arising from simultaneous access
194197

195-
#### `Promise repo.close ()`
198+
#### `Promise repo.close()`
196199

197200
Unlocks the repo.
198201

199-
#### `Promise<boolean> repo.exists ()`
202+
#### `Promise<boolean> repo.exists()`
200203

201-
Tells whether this repo exists or not. Returned promise resolves to a `boolean`.
204+
Tells whether this repo exists or not. Returned promise resolves to a `boolean`
205+
206+
#### `Promise<Boolean> repo.isInitialized()`
207+
208+
The returned promise resolves to `false` if the repo has not been initialized and `true` if it has
202209

203210
### Repos
204211

205212
Root repo:
206213

207-
#### `Promise repo.put (key, value:Buffer)`
214+
#### `Promise repo.put(key, value:Buffer)`
208215

209-
Put a value at the root of the repo.
216+
Put a value at the root of the repo
210217

211-
* `key` can be a buffer, a string or a [Key](https://github.com/ipfs/interface-datastore#keys).
218+
* `key` can be a buffer, a string or a [Key][]
212219

213-
#### `Promise<Buffer> repo.get (key)`
220+
#### `Promise<Buffer> repo.get(key)`
214221

215-
Get a value at the root of the repo.
222+
Get a value at the root of the repo
216223

217-
* `key` can be a buffer, a string or a [Key](https://github.com/ipfs/interface-datastore#keys).
224+
* `key` can be a buffer, a string or a [Key][]
218225

219-
[Blocks](https://github.com/ipfs/js-ipld-block#readme):
226+
### Blocks
220227

221-
#### `Promise<Boolean> repo.isInitialized ()`
228+
#### `Promise<Block> repo.blocks.put(block:Block)`
222229

223-
The returned promise resolves to `false` if the repo has not been initialized and `true` if it has.
230+
* `block` should be of type [Block][]
224231

225-
#### `Promise repo.blocks.put (block:Block)`
232+
#### `AsyncIterator<Block> repo.blocks.putMany(source)`
226233

227-
* `block` should be of type [Block](https://github.com/ipfs/js-ipld-block#readme).
234+
Put many blocks.
228235

229-
#### `Promise repo.blocks.putMany (blocks)`
236+
* `source` should be an AsyncIterable that yields entries of type [Block][]
230237

231-
Put many blocks.
238+
#### `Promise<Buffer> repo.blocks.get(cid)`
232239

233-
* `block` should be an Iterable or AsyncIterable that yields entries of type [Block](https://github.com/ipfs/js-ipld-block#readme).
240+
Get block.
241+
242+
* `cid` is the content id of type [CID][]
234243

235-
#### `Promise<Buffer> repo.blocks.get (cid)`
244+
#### `AsyncIterable<Buffer> repo.blocks.getMany(source)`
236245

237246
Get block.
238247

239-
* `cid` is the content id of [type CID](https://github.com/ipld/js-cid#readme).
248+
* `source` should be an AsyncIterable that yields entries of type [CID][]
240249

241-
#### `Promise repo.blocks.delete (cid:CID)`
250+
#### `Promise<CID> repo.blocks.delete(cid:CID)`
242251

243-
* `cid` should be of the [type CID](https://github.com/ipld/js-cid#readme).
252+
* `cid` should be of the type [CID][]
244253

245254
Delete a block
246255

247-
#### `Promise repo.blocks.deleteMany (cids)`
256+
#### `AsyncIterator<CID> repo.blocks.deleteMany(source)`
248257

249-
* `cids` should be an Iterable or AsyncIterable that yields entries of the [type CID](https://github.com/ipld/js-cid#readme).
258+
* `source` should be an Iterable or AsyncIterable that yields entries of the type [CID][]
250259

251260
Delete many blocks
252261

253-
Datastore:
262+
### Datastore
254263

255264
#### `repo.datastore`
256265

257266
This contains a full implementation of [the `interface-datastore` API](https://github.com/ipfs/interface-datastore#api).
258267

259-
260268
### Config
261269

262270
Instead of using `repo.set('config')` this exposes an API that allows you to set and get a decoded config object, as well as, in a safe manner, change any of the config values individually.
@@ -298,7 +306,7 @@ Whether the config sub-repo exists.
298306

299307
### Version
300308

301-
#### `Promise<Number> repo.version.get ()`
309+
#### `Promise<Number> repo.version.get()`
302310

303311
Gets the repo version (an integer).
304312

@@ -308,19 +316,19 @@ Sets the repo version
308316

309317
### API Addr
310318

311-
#### `Promise<String> repo.apiAddr.get ()`
319+
#### `Promise<String> repo.apiAddr.get()`
312320

313321
Gets the API address.
314322

315-
#### `Promise repo.apiAddr.set (value)`
323+
#### `Promise repo.apiAddr.set(value)`
316324

317325
Sets the API address.
318326

319-
* `value` should be a [Multiaddr](https://github.com/multiformats/js-multiaddr) or a String representing a valid one.
327+
* `value` should be a [Multiaddr][] or a String representing a valid one.
320328

321329
### Status
322330

323-
#### `Promise<Object> repo.stat ()`
331+
#### `Promise<Object> repo.stat()`
324332

325333
Gets the repo status.
326334

@@ -343,21 +351,21 @@ const memoryLock = require('ipfs-repo/src/lock-memory') // Default in browser
343351

344352
You can also provide your own custom Lock. It must be an object with the following interface:
345353

346-
#### `Promise lock.lock (dir)`
354+
#### `Promise lock.lock(dir)`
347355

348356
Sets the lock if one does not already exist. If a lock already exists, should throw an error.
349357

350358
`dir` is a string to the directory the lock should be created at. The repo typically creates the lock at its root.
351359

352360
Returns `closer`, where `closer` has a `close` method for removing the lock.
353361

354-
#### `Promise closer.close ()`
362+
#### `Promise closer.close()`
355363

356364
Closes the lock created by `lock.open`
357365

358366
If no error was thrown, the lock was successfully removed.
359367

360-
#### `Promise<boolean> lock.locked (dir)`
368+
#### `Promise<boolean> lock.locked(dir)`
361369

362370
Checks the existence of the lock.
363371

@@ -392,3 +400,8 @@ This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/c
392400
## License
393401

394402
[MIT](LICENSE)
403+
404+
[CID]: https://github.com/multiformats/js-cid
405+
[Key]: https://github.com/ipfs/interface-datastore#keys
406+
[Block]: https://github.com/ipld/js-ipld-block
407+
[Multiaddr]: https://github.com/multiformats/js-multiaddr

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,13 @@
4343
"npm": ">=3.0.0"
4444
},
4545
"devDependencies": {
46-
"aegir": "^21.10.1",
46+
"aegir": "^22.0.0",
4747
"chai": "^4.2.0",
4848
"chai-as-promised": "^7.1.1",
4949
"dirty-chai": "^2.0.1",
50+
"it-all": "^1.0.2",
51+
"it-drain": "^1.0.1",
52+
"it-first": "^1.0.2",
5053
"just-range": "^2.1.0",
5154
"memdown": "^5.1.0",
5255
"multihashes": "~0.4.15",
@@ -60,13 +63,13 @@
6063
"buffer": "^5.6.0",
6164
"bytes": "^3.1.0",
6265
"cids": "^0.8.0",
63-
"datastore-core": "^1.0.0",
64-
"datastore-fs": "^1.0.0",
65-
"datastore-idb": "^1.0.2",
66-
"datastore-level": "^1.0.0",
66+
"datastore-core": "^1.1.0",
67+
"datastore-fs": "^1.1.0",
68+
"datastore-idb": "^1.1.0",
69+
"datastore-level": "^1.1.0",
6770
"debug": "^4.1.0",
6871
"err-code": "^2.0.0",
69-
"interface-datastore": "^0.8.3",
72+
"interface-datastore": "^1.0.2",
7073
"ipfs-repo-migrations": "^0.2.1",
7174
"ipfs-utils": "^2.2.0",
7275
"ipld-block": "^0.9.1",

0 commit comments

Comments
 (0)