Skip to content
This repository was archived by the owner on Oct 1, 2021. It is now read-only.

Commit 5e1a52c

Browse files
AuHauachingbrain
andauthored
Migration 8 (#4)
Migrates blockstore keys to store blocks by multihash instead of CID Co-authored-by: achingbrain <[email protected]>
1 parent 084b636 commit 5e1a52c

File tree

6 files changed

+189
-12
lines changed

6 files changed

+189
-12
lines changed

migrations/index.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ const emptyMigration = {
1111
}
1212

1313
module.exports = [
14-
Object.assign({}, emptyMigration, { version: 7, revert: undefined }),
15-
Object.assign({}, emptyMigration, { version: 6, revert: undefined }),
16-
Object.assign({}, emptyMigration, { version: 5, revert: undefined }),
17-
Object.assign({}, emptyMigration, { version: 4, revert: undefined }),
18-
Object.assign({}, emptyMigration, { version: 3, revert: undefined }),
19-
Object.assign({}, emptyMigration, { version: 2, revert: undefined }),
20-
Object.assign({}, emptyMigration, { version: 1, revert: undefined })
14+
Object.assign({version: 1}, emptyMigration),
15+
Object.assign({version: 2}, emptyMigration),
16+
Object.assign({version: 3}, emptyMigration),
17+
Object.assign({version: 4}, emptyMigration),
18+
Object.assign({version: 5}, emptyMigration),
19+
Object.assign({version: 6}, emptyMigration),
20+
Object.assign({version: 7}, emptyMigration),
21+
require('./migration-8')
2122
]

migrations/migration-8/index.js

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use strict'
2+
3+
const path = require('path')
4+
const CID = require('cids')
5+
const Key = require('interface-datastore').Key
6+
const core = require('datastore-core')
7+
const ShardingStore = core.ShardingDatastore
8+
const mb = require('multibase')
9+
const utils = require('../../src/utils')
10+
const log = require('debug')('ipfs-repo-migrations:migration-8')
11+
12+
// This function in js-ipfs-repo defaults to not using sharding
13+
// but the default value of the options.sharding is true hence this
14+
// function defaults to use sharding.
15+
async function maybeWithSharding (filestore, options) {
16+
if (options.sharding === false) {
17+
return filestore
18+
}
19+
20+
const shard = new core.shard.NextToLast(2)
21+
22+
return ShardingStore.createOrOpen(filestore, shard)
23+
}
24+
25+
function keyToMultihash (key) {
26+
const buf = mb.decode(`b${key.toString().slice(1)}`)
27+
28+
// Extract multihash from CID
29+
let multihash = new CID(buf).multihash
30+
31+
// Encode and slice off multibase codec
32+
multihash = mb.encode('base32', multihash).slice(1)
33+
34+
// Should be uppercase for interop with go
35+
multihash = multihash.toString().toUpperCase()
36+
37+
return new Key(`/${multihash}`, false)
38+
}
39+
40+
function keyToCid (key) {
41+
const buf = mb.decode(`b${key.toString().slice(1)}`)
42+
43+
// CID to Key
44+
const multihash = mb.encode('base32', new CID(1, 'raw', buf).buffer).slice(1)
45+
46+
return new Key(`/${multihash}`.toUpperCase(), false)
47+
}
48+
49+
async function process (repoPath, options, keyFunction){
50+
const { StorageBackend, storageOptions } = utils.getDatastoreAndOptions(options, 'blocks')
51+
52+
const baseStore = new StorageBackend(path.join(repoPath, 'blocks'), storageOptions)
53+
await baseStore.open()
54+
const store = await maybeWithSharding(baseStore, storageOptions)
55+
await store.open()
56+
57+
try {
58+
let counter = 0
59+
60+
for await (const block of store.query({})) {
61+
const newKey = keyFunction(block.key)
62+
63+
// If the Key is base32 CIDv0 then there's nothing to do
64+
if(newKey.toString() !== block.key.toString()) {
65+
counter += 1
66+
67+
log(`Migrating Block from ${block.key.toString()} to ${newKey.toString()}`)
68+
await store.delete(block.key)
69+
await store.put(newKey, block.value)
70+
}
71+
}
72+
73+
log(`Changed ${ counter } blocks`)
74+
} finally {
75+
await store.close()
76+
}
77+
}
78+
79+
module.exports = {
80+
version: 8,
81+
description: 'Transforms key names into base32 encoding and converts Block store to use bare multihashes encoded as base32',
82+
migrate: (repoPath, options = {}) => {
83+
return process(repoPath, options, keyToMultihash)
84+
},
85+
revert: (repoPath, options = {}) => {
86+
return process(repoPath, options, keyToCid)
87+
}
88+
}

package.json

+7-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"main": "src/index.js",
2121
"browser": {
2222
"./src/repo/lock.js": "./src/repo/lock-memory.js",
23-
"datastore-fs": "datastore-idb"
23+
"datastore-fs": "datastore-level"
2424
},
2525
"bin": {
2626
"jsipfs-migrations": "./src/cli.js"
@@ -46,16 +46,19 @@
4646
"dependencies": {
4747
"buffer": "^5.6.0",
4848
"chalk": "^4.0.0",
49+
"cids": "^0.8.3",
50+
"datastore-core": "^1.1.0",
4951
"datastore-fs": "^1.0.0",
50-
"datastore-idb": "^1.0.2",
52+
"datastore-level": "^1.1.0",
5153
"debug": "^4.1.0",
52-
"interface-datastore": "^1.0.4",
54+
"interface-datastore": "^1.0.2",
55+
"multibase": "^1.0.1",
5356
"proper-lockfile": "^4.1.1",
5457
"yargs": "^15.3.1",
5558
"yargs-promise": "^1.1.0"
5659
},
5760
"devDependencies": {
58-
"aegir": "^25.0.0",
61+
"aegir": "^23.0.0",
5962
"chai": "^4.2.0",
6063
"chai-as-promised": "^7.1.1",
6164
"dirty-chai": "^2.0.1",

test/browser.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
const { Buffer } = require('buffer')
55
const loadFixture = require('aegir/fixtures')
6-
const Datastore = require('datastore-idb')
6+
const Datastore = require('datastore-level')
77

88
const Key = require('interface-datastore').Key
99
const CONFIG_KEY = new Key('config')
@@ -44,6 +44,10 @@ describe('Browser specific tests', () => {
4444
require('./version-test')(createRepo, repoCleanup)
4545
})
4646

47+
describe('migrations tests', () => {
48+
require('./migrations/migration-8-test')(createRepo, repoCleanup)
49+
})
50+
4751
describe('init tests', () => {
4852
require('./init-test')(createRepo, repoCleanup)
4953
})

test/migrations/migration-8-test.js

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const chai = require('chai')
5+
chai.use(require('dirty-chai'))
6+
const chaiAsPromised = require('chai-as-promised')
7+
chai.use(chaiAsPromised)
8+
const expect = chai.expect
9+
10+
const path = require('path')
11+
const migration = require('../../migrations/migration-8')
12+
const Key = require('interface-datastore').Key
13+
const Datastore = require('datastore-fs')
14+
const core = require('datastore-core')
15+
const ShardingStore = core.ShardingDatastore
16+
17+
const blocksFixtures = [
18+
['AFKREIBFG77IKIKDMBDUFDCSPK7H5TE5LNPMCSXYLPML27WSTT5YA5IUNU',
19+
'CIQCKN76QUQUGYCHIKGFE6V6P3GJ2W26YFFPQW6YXV7NFHH3QB2RI3I']
20+
]
21+
22+
async function bootstrapBlocks (dir, encoded) {
23+
const baseStore = new Datastore(path.join(dir, 'blocks'), { extension: '.data', createIfMissing: true })
24+
const shard = new core.shard.NextToLast(2)
25+
26+
await baseStore.open()
27+
const store = await ShardingStore.createOrOpen(baseStore, shard)
28+
29+
let name
30+
for (const blocksNames of blocksFixtures) {
31+
name = encoded ? blocksNames[1] : blocksNames[0]
32+
await store.put(new Key(name), '')
33+
}
34+
35+
await store.close()
36+
}
37+
38+
async function validateBlocks (dir, shouldBeEncoded) {
39+
const baseStore = new Datastore(path.join(dir, 'blocks'), { extension: '.data', createIfMissing: false })
40+
const shard = new core.shard.NextToLast(2)
41+
42+
await baseStore.open()
43+
const store = await ShardingStore.createOrOpen(baseStore, shard)
44+
45+
let newName, oldName
46+
for (const blockNames of blocksFixtures) {
47+
newName = shouldBeEncoded ? blockNames[1] : blockNames[0]
48+
oldName = shouldBeEncoded ? blockNames[0] : blockNames[1]
49+
expect(await store.has(new Key(`/${oldName}`))).to.be.false(`${oldName} was not migrated to ${newName}`)
50+
expect(await store.has(new Key(`/${newName}`))).to.be.true(`${newName} was not removed`)
51+
}
52+
53+
await store.close()
54+
}
55+
56+
module.exports = (setup, cleanup) => {
57+
describe('migration 8', () => {
58+
let dir
59+
60+
beforeEach(async () => {
61+
dir = await setup()
62+
})
63+
afterEach(() => cleanup(dir))
64+
65+
it('should migrate blocks forward', async () => {
66+
await bootstrapBlocks(dir, false)
67+
await migration.migrate(dir)
68+
await validateBlocks(dir, true)
69+
})
70+
71+
it('should migrate blocks backward', async () => {
72+
await bootstrapBlocks(dir, true)
73+
await migration.revert(dir)
74+
await validateBlocks(dir, false)
75+
})
76+
})
77+
}

test/node.js

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ describe('Node specific tests', () => {
4343
require('./version-test')(createRepo, repoCleanup)
4444
})
4545

46+
describe('migrations tests', () => {
47+
require('./migrations/migration-8-test')(createRepo, repoCleanup)
48+
})
49+
4650
describe('init tests', () => {
4751
require('./init-test')(createRepo, repoCleanup)
4852
})

0 commit comments

Comments
 (0)