Skip to content

Commit 71503ac

Browse files
AuHauachingbrain
andcommitted
feat: integration of js-ipfs-repo-migrations
Integration of js-ipfs-repo-migrations brings automatic repo migrations to ipfs-repo (both in-browser and fs). It is possible to control the automatic migration using either config's setting 'repoDisableAutoMigration' or IPFSRepo's option 'disableAutoMigration'. BREAKING CHANGE: repo.blocks.query() now returns multihashes as a key instead of CID. If you want to have CID returned call it as query({}, true), which will constructs CIDv1 using IPLD's RAW codec. This means that this constructed CID might not equal to the one that the block was originally saved. Related to ipfs/js-ipfs#2415 Co-authored-by: achingbrain <[email protected]>
1 parent 4692a47 commit 71503ac

10 files changed

+86
-97
lines changed

README.md

+39-16
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,20 @@ This is the implementation of the [IPFS repo spec](https://github.com/ipfs/specs
4040
- [`Promise<Buffer> repo.get(key)`](#promisebuffer-repogetkey)
4141
- [Blocks](#blocks)
4242
- [`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)
43+
- [`AsyncIterator<Block> repo.blocks.putMany(source:AsyncIterable<Block>)`](#asynciteratorblock-repoblocksputmanysourceasynciterableblock)
44+
- [`Promise<Block> repo.blocks.get(cid:CID)`](#promiseblock-repoblocksgetcidcid)
45+
- [`AsyncIterable<Block> repo.blocks.getMany(source:AsyncIterable<CID>)`](#asynciterableblock-repoblocksgetmanysourceasynciterablecid)
46+
- [`Promise<boolean> repo.blocks.has (cid:CID)`](#promiseboolean-repoblockshas-cidcid)
47+
- [`Promise<boolean> repo.blocks.delete (cid:CID)`](#promiseboolean-repoblocksdelete-cidcid)
48+
- [`Promise<Array<Object>> repo.blocks.query (query)`](#promisearrayobject-repoblocksquery-query)
4649
- [`Promise<CID> repo.blocks.delete(cid:CID)`](#promisecid-repoblocksdeletecidcid)
47-
- [`AsyncIterator<CID> repo.blocks.deleteMany(source)`](#asynciteratorcid-repoblocksdeletemanysource)
50+
- [`AsyncIterator<CID> repo.blocks.deleteMany(source:AsyncIterable<CID>)`](#asynciteratorcid-repoblocksdeletemanysourceasynciterablecid)
4851
- [Datastore](#datastore)
4952
- [`repo.datastore`](#repodatastore)
5053
- [Config](#config)
51-
- [`Promise repo.config.set(key:string, value)`](#promise-repoconfigsetkeystring-value)
52-
- [`Promise repo.config.replace(value)`](#promise-repoconfigreplacevalue)
53-
- [`Promise<?> repo.config.get(key:string)`](#promise-repoconfiggetkeystring)
54+
- [`Promise repo.config.set(key:String, value:Object)`](#promise-repoconfigsetkeystring-valueobject)
55+
- [`Promise repo.config.replace(value:Object)`](#promise-repoconfigreplacevalueobject)
56+
- [`Promise<?> repo.config.get(key:String)`](#promise-repoconfiggetkeystring)
5457
- [`Promise<Object> repo.config.getAll()`](#promiseobject-repoconfiggetall)
5558
- [`Promise<boolean> repo.config.exists()`](#promiseboolean-repoconfigexists)
5659
- [Version](#version)
@@ -229,31 +232,51 @@ Get a value at the root of the repo
229232

230233
* `block` should be of type [Block][]
231234

232-
#### `AsyncIterator<Block> repo.blocks.putMany(source)`
235+
#### `AsyncIterator<Block> repo.blocks.putMany(source:AsyncIterable<Block>)`
233236

234237
Put many blocks.
235238

236239
* `source` should be an AsyncIterable that yields entries of type [Block][]
237240

238-
#### `Promise<Buffer> repo.blocks.get(cid)`
241+
#### `Promise<Block> repo.blocks.get(cid:CID)`
239242

240243
Get block.
241244

242245
* `cid` is the content id of type [CID][]
243246

244-
#### `AsyncIterable<Buffer> repo.blocks.getMany(source)`
247+
#### `AsyncIterable<Block> repo.blocks.getMany(source:AsyncIterable<CID>)`
245248

246-
Get block.
249+
Get many blocks
247250

248251
* `source` should be an AsyncIterable that yields entries of type [CID][]
249252

253+
#### `Promise<boolean> repo.blocks.has (cid:CID)`
254+
255+
Indicate if a block is present for the passed CID
256+
257+
* `cid` should be of the type [CID][]
258+
259+
#### `Promise<boolean> repo.blocks.delete (cid:CID)`
260+
261+
Deletes a block
262+
263+
* `cid` should be of the type [CID][]
264+
265+
#### `Promise<Array<Object>> repo.blocks.query (query)`
266+
267+
Query what blocks are available in blockstore.
268+
269+
* `query` is a object as specified in [interface-datastore](https://github.com/ipfs/interface-datastore#query).
270+
271+
Datastore:
272+
250273
#### `Promise<CID> repo.blocks.delete(cid:CID)`
251274

252275
* `cid` should be of the type [CID][]
253276

254277
Delete a block
255278

256-
#### `AsyncIterator<CID> repo.blocks.deleteMany(source)`
279+
#### `AsyncIterator<CID> repo.blocks.deleteMany(source:AsyncIterable<CID>)`
257280

258281
* `source` should be an Iterable or AsyncIterable that yields entries of the type [CID][]
259282

@@ -269,7 +292,7 @@ This contains a full implementation of [the `interface-datastore` API](https://g
269292

270293
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.
271294

272-
#### `Promise repo.config.set(key:string, value)`
295+
#### `Promise repo.config.set(key:String, value:Object)`
273296

274297
Set a config value. `value` can be any object that is serializable to JSON.
275298

@@ -281,11 +304,11 @@ const config = await repo.config.get()
281304
assert.equal(config.a.b.c, 'c value')
282305
```
283306

284-
#### `Promise repo.config.replace(value)`
307+
#### `Promise repo.config.replace(value:Object)`
285308

286309
Set the whole config value. `value` can be any object that is serializable to JSON.
287310

288-
#### `Promise<?> repo.config.get(key:string)`
311+
#### `Promise<?> repo.config.get(key:String)`
289312

290313
Get a config value. Returned promise resolves to the same type that was set before.
291314

@@ -379,7 +402,7 @@ Returned promise resolves to a `boolean` indicating the existence of the lock.
379402

380403
### Migrations
381404

382-
When there is a new repo migration and the version of repo is increased, don't
405+
When there is a new repo migration and the version of the repo is increased, don't
383406
forget to propagate the changes into the test repo (`test/test-repo`).
384407

385408
**For tools that run mainly in the browser environment, be aware that disabling automatic

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,10 @@
6969
"debug": "^4.1.0",
7070
"err-code": "^2.0.0",
7171
"interface-datastore": "^1.0.2",
72-
"ipfs-repo-migrations": "^0.2.1",
72+
"ipfs-repo-migrations": "github:ipfs/js-ipfs-repo-migrations#migration/8-multihash_and_keys",
7373
"ipfs-utils": "^2.2.0",
7474
"ipld-block": "^0.9.1",
7575
"it-map": "^1.0.2",
76-
"it-pipe": "^1.1.0",
7776
"just-safe-get": "^2.0.0",
7877
"just-safe-set": "^2.1.0",
7978
"multibase": "^0.7.0",

src/blockstore-utils.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ exports.cidToKey = cid => {
1616
throw errcode(new Error('Not a valid cid'), 'ERR_INVALID_CID')
1717
}
1818

19-
return new Key('/' + multibase.encode('base32', cid.buffer).toString().slice(1).toUpperCase(), false)
19+
return new Key('/' + multibase.encode('base32', cid.multihash).toString().slice(1).toUpperCase(), false)
2020
}
2121

2222
/**
2323
* Transform a datastore Key instance to a CID
24+
* As Key is a multihash of the CID, it is reconstructed using IPLD's RAW codec.
25+
* Hence it is highly probable that stored CID will differ from a CID retrieved from blockstore.
2426
*
2527
* @param {Key} key
2628
* @returns {CID}
2729
*/
2830
exports.keyToCid = key => {
29-
return new CID(multibase.decode('b' + key.toString().slice(1).toLowerCase()))
31+
// Block key is of the form /<base32 encoded string>
32+
return new CID(1, 'raw', multibase.decode('b' + key.toString().slice(1).toLowerCase()))
3033
}

src/blockstore.js

+28-65
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
const core = require('datastore-core')
44
const ShardingStore = core.ShardingDatastore
55
const Block = require('ipld-block')
6-
const { cidToKey, keyToCid } = require('./blockstore-utils')
6+
const { cidToKey } = require('./blockstore-utils')
77
const map = require('it-map')
8-
const pipe = require('it-pipe')
98

109
module.exports = async (filestore, options) => {
1110
const store = await maybeWithSharding(filestore, options)
@@ -32,6 +31,7 @@ function createBaseStore (store) {
3231
async * query (query, options) { // eslint-disable-line require-await
3332
yield * store.query(query, options)
3433
},
34+
3535
/**
3636
* Get a single block by CID.
3737
*
@@ -41,27 +41,11 @@ function createBaseStore (store) {
4141
*/
4242
async get (cid, options) {
4343
const key = cidToKey(cid)
44-
let blockData
45-
try {
46-
blockData = await store.get(key, options)
47-
return new Block(blockData, cid)
48-
} catch (err) {
49-
if (err.code === 'ERR_NOT_FOUND') {
50-
const otherCid = cidToOtherVersion(cid)
51-
52-
if (!otherCid) {
53-
throw err
54-
}
55-
56-
const otherKey = cidToKey(otherCid)
57-
const blockData = await store.get(otherKey, options)
58-
await store.put(key, blockData)
59-
return new Block(blockData, cid)
60-
}
44+
const blockData = await store.get(key, options)
6145

62-
throw err
63-
}
46+
return new Block(blockData, cid)
6447
},
48+
6549
/**
6650
* Like get, but for more.
6751
*
@@ -74,6 +58,7 @@ function createBaseStore (store) {
7458
yield this.get(cid, options)
7559
}
7660
},
61+
7762
/**
7863
* Write a single block to the store.
7964
*
@@ -86,14 +71,13 @@ function createBaseStore (store) {
8671
throw new Error('invalid block')
8772
}
8873

89-
const exists = await this.has(block.cid)
74+
const key = cidToKey(block.cid)
75+
const exists = await store.has(key, options)
9076

91-
if (exists) {
92-
return this.get(block.cid, options)
77+
if (!exists) {
78+
await store.put(key, block.data, options)
9379
}
9480

95-
await store.put(cidToKey(block.cid), block.data, options)
96-
9781
return block
9882
},
9983

@@ -104,41 +88,30 @@ function createBaseStore (store) {
10488
* @param {Object} options
10589
* @returns {AsyncIterable<Block>}
10690
*/
107-
async * putMany (blocks, options) { // eslint-disable-line require-await
108-
yield * pipe(
109-
blocks,
110-
(source) => {
111-
// turn them into a key/value pair
112-
return map(source, (block) => {
113-
return { key: cidToKey(block.cid), value: block.data }
114-
})
115-
},
116-
(source) => {
117-
// put them into the datastore
118-
return store.putMany(source, options)
119-
},
120-
(source) => {
121-
// map the returned key/value back into a block
122-
return map(source, ({ key, value }) => {
123-
return new Block(value, keyToCid(key))
124-
})
91+
async * putMany (blocks, options) {
92+
for await (const block of blocks) {
93+
const key = cidToKey(block.cid)
94+
const exists = await store.has(key, options)
95+
96+
if (!exists) {
97+
await store.put(key, block.data, options)
12598
}
126-
)
99+
100+
yield block
101+
}
127102
},
103+
128104
/**
129-
* Does the store contain block with this cid?
105+
* Does the store contain block with this CID?
130106
*
131107
* @param {CID} cid
132108
* @param {Object} options
133109
* @returns {Promise<bool>}
134110
*/
135-
async has (cid, options) {
136-
const exists = await store.has(cidToKey(cid), options)
137-
if (exists) return exists
138-
const otherCid = cidToOtherVersion(cid)
139-
if (!otherCid) return false
140-
return store.has(cidToKey(otherCid), options)
111+
async has (cid, options) { // eslint-disable-line require-await
112+
return store.has(cidToKey(cid), options)
141113
},
114+
142115
/**
143116
* Delete a block from the store
144117
*
@@ -149,6 +122,7 @@ function createBaseStore (store) {
149122
async delete (cid, options) { // eslint-disable-line require-await
150123
return store.delete(cidToKey(cid), options)
151124
},
125+
152126
/**
153127
* Delete a block from the store
154128
*
@@ -157,12 +131,9 @@ function createBaseStore (store) {
157131
* @returns {Promise<void>}
158132
*/
159133
async * deleteMany (cids, options) { // eslint-disable-line require-await
160-
yield * store.deleteMany((async function * () {
161-
for await (const cid of cids) {
162-
yield cidToKey(cid)
163-
}
164-
}()), options)
134+
yield * store.deleteMany(map(cids, cid => cidToKey(cid)), options)
165135
},
136+
166137
/**
167138
* Close the store
168139
*
@@ -173,11 +144,3 @@ function createBaseStore (store) {
173144
}
174145
}
175146
}
176-
177-
function cidToOtherVersion (cid) {
178-
try {
179-
return cid.version === 0 ? cid.toV1() : cid.toV0()
180-
} catch (err) {
181-
return null
182-
}
183-
}

src/constants.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict'
22

33
module.exports = {
4-
repoVersion: 7
4+
repoVersion: 8
55
}

src/index.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ const lockers = {
3535
*/
3636
class IpfsRepo {
3737
/**
38-
* @param {string} repoPath - path where the repo is stored
39-
* @param {object} options - Configuration
38+
* @param {String} repoPath - path where the repo is stored
39+
* @param {Object} options - Configuration
4040
*/
4141
constructor (repoPath, options) {
4242
if (typeof repoPath !== 'string') {
@@ -185,7 +185,7 @@ class IpfsRepo {
185185
* Creates a lock on the repo if a locker is specified. The lockfile object will
186186
* be returned in the callback if one has been created.
187187
*
188-
* @param {string} path
188+
* @param {String} path
189189
* @returns {Promise<lockfile>}
190190
*/
191191
async _openLock (path) {
@@ -354,7 +354,7 @@ class IpfsRepo {
354354
count = count.plus(1)
355355
size = size
356356
.plus(block.value.byteLength)
357-
.plus(block.key._buf.byteLength)
357+
.plus(block.key.toBuffer().byteLength)
358358
}
359359

360360
return { count, size }
@@ -365,7 +365,7 @@ async function getSize (queryFn) {
365365
const sum = new Big(0)
366366
for await (const block of queryFn.query({})) {
367367
sum.plus(block.value.byteLength)
368-
.plus(block.key._buf.byteLength)
368+
.plus(block.key.toBuffer().byteLength)
369369
}
370370
return sum
371371
}

src/lock-memory.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const LOCKS = {}
1212
/**
1313
* Lock the repo in the given dir.
1414
*
15-
* @param {string} dir
15+
* @param {String} dir
1616
* @returns {Promise<Object>}
1717
*/
1818
exports.lock = async (dir) => { // eslint-disable-line require-await
@@ -37,7 +37,7 @@ exports.lock = async (dir) => { // eslint-disable-line require-await
3737
/**
3838
* Check if the repo in the given directory is locked.
3939
*
40-
* @param {string} dir
40+
* @param {String} dir
4141
* @returns {bool}
4242
*/
4343
exports.locked = async (dir) => { // eslint-disable-line require-await

src/lock.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const STALE_TIME = 20000
2222
/**
2323
* Lock the repo in the given dir.
2424
*
25-
* @param {string} dir
25+
* @param {String} dir
2626
* @returns {Promise<Object>}
2727
*/
2828
exports.lock = async (dir) => {

0 commit comments

Comments
 (0)