Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit 57de7b5

Browse files
committed
feat: gc uses multihashes instead CIDs
1 parent 6d6e8c6 commit 57de7b5

File tree

3 files changed

+62
-73
lines changed

3 files changed

+62
-73
lines changed

src/core/components/pin/gc.js

+22-40
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const CID = require('cids')
44
const base32 = require('base32.js')
55
const callbackify = require('callbackify')
6-
const { cidToString } = require('../../../utils/cid')
76
const log = require('debug')('ipfs:gc')
87
const { default: Queue } = require('p-queue')
98
// TODO: Use exported key from root when upgraded to ipfs-mfs@>=13
@@ -47,15 +46,16 @@ module.exports = function gc (self) {
4746
})
4847
}
4948

50-
// Get Set of CIDs of blocks to keep
49+
// Get Set of multihashes of blocks to keep
5150
async function createMarkedSet (ipfs) {
5251
const output = new Set()
5352

5453
const addPins = pins => {
5554
log(`Found ${pins.length} pinned blocks`)
5655

5756
pins.forEach(pin => {
58-
output.add(cidToString(new CID(pin), { base: 'base32' }))
57+
const cid = new CID(pin)
58+
output.add(base32.encode(cid.multihash))
5959
})
6060
}
6161

@@ -91,7 +91,6 @@ async function getDescendants (ipfs, cid) {
9191
const refs = await ipfs.refs(cid, { recursive: true })
9292
const cids = [cid, ...refs.map(r => new CID(r.ref))]
9393
log(`Found ${cids.length} MFS blocks`)
94-
// log(' ' + cids.join('\n '))
9594

9695
return cids
9796
}
@@ -100,54 +99,37 @@ async function getDescendants (ipfs, cid) {
10099
async function deleteUnmarkedBlocks (ipfs, markedSet, blockKeys) {
101100
// Iterate through all blocks and find those that are not in the marked set
102101
// The blockKeys variable has the form [ { key: Key() }, { key: Key() }, ... ]
103-
const unreferenced = []
104102
const result = []
103+
let blockCounter = 0
105104

106105
const queue = new Queue({
107106
concurrency: BLOCK_RM_CONCURRENCY
108107
})
109108

110109
for await (const { key: k } of blockKeys) {
111-
try {
112-
const cid = dsKeyToCid(k)
113-
const b32 = cid.toV1().toString('base32')
114-
if (!markedSet.has(b32)) {
115-
unreferenced.push(cid)
116-
117-
queue.add(async () => {
118-
const res = {
119-
cid
120-
}
121-
122-
try {
123-
await ipfs._repo.blocks.delete(cid)
124-
} catch (err) {
125-
res.err = new Error(`Could not delete block with CID ${cid}: ${err.message}`)
126-
}
127-
128-
result.push(res)
129-
})
130-
}
131-
} catch (err) {
132-
const msg = `Could not convert block with key '${k}' to CID`
133-
log(msg, err)
134-
result.push({ err: new Error(msg + `: ${err.message}`) })
110+
blockCounter++
111+
const multihashString = k.toString().substr(1)
112+
if (!markedSet.has(multihashString)) {
113+
queue.add(async () => {
114+
const res = {
115+
multihash: multihashString
116+
}
117+
118+
try {
119+
await ipfs._repo.blocks.delete(Buffer.from(multihashString))
120+
} catch (err) {
121+
res.err = new Error(`Could not delete block with multihash ${multihashString}: ${err.message}`)
122+
}
123+
124+
result.push(res)
125+
})
135126
}
136127
}
137128

138129
await queue.onIdle()
139130

140-
log(`Marked set has ${markedSet.size} unique blocks. Blockstore has ${blockKeys.length} blocks. ` +
141-
`Deleted ${unreferenced.length} blocks.`)
131+
log(`Marked set has ${markedSet.size} unique blocks. Blockstore has ${blockCounter} blocks. ` +
132+
`Deleted ${result.filter(res => res.err === undefined).length} blocks.`)
142133

143134
return result
144135
}
145-
146-
// TODO: Use exported utility when upgrade to ipfs-repo@>=0.27.1
147-
// https://github.com/ipfs/js-ipfs-repo/pull/206
148-
function dsKeyToCid (key) {
149-
// Block key is of the form /<base32 encoded string>
150-
const decoder = new base32.Decoder()
151-
const buff = decoder.write(key.toString().slice(1)).finalize()
152-
return new CID(Buffer.from(buff))
153-
}

src/http/api/resources/repo.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ exports.gc = {
1818
const response = filtered.map(r => {
1919
return {
2020
Err: r.err && r.err.message,
21-
Key: !r.err && { '/': r.cid.toString() }
21+
Key: !r.err && r.multihash
2222
}
2323
})
2424
return h.response(response)

test/core/gc.spec.js

+39-32
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const IPFSFactory = require('ipfsd-ctl')
77
const pEvent = require('p-event')
88
const env = require('ipfs-utils/src/env')
99
const IPFS = require('../../src/core')
10+
const CID = require('cids')
11+
const base32 = require('base32.js')
1012
const { Errors } = require('interface-datastore')
1113

1214
// We need to detect when a readLock or writeLock is requested for the tests
@@ -90,17 +92,17 @@ describe('gc', function () {
9092
name: 'add',
9193
add1: () => ipfs.add(fixtures[0], { pin: false }),
9294
add2: () => ipfs.add(fixtures[1], { pin: false }),
93-
resToCid: (res) => res[0].hash
95+
resToMultihash: (res) => base32.encode(new CID(res[0].hash).multihash)
9496
}, {
9597
name: 'object put',
9698
add1: () => ipfs.object.put({ Data: 'obj put 1', Links: [] }),
9799
add2: () => ipfs.object.put({ Data: 'obj put 2', Links: [] }),
98-
resToCid: (res) => res.toString()
100+
resToMultihash: (res) => base32.encode(res.multihash)
99101
}, {
100102
name: 'block put',
101103
add1: () => ipfs.block.put(Buffer.from('block put 1'), null),
102104
add2: () => ipfs.block.put(Buffer.from('block put 2'), null),
103-
resToCid: (res) => res.cid.toString()
105+
resToMultihash: (res) => base32.encode(res.cid.multihash)
104106
}]
105107

106108
describe('locks', function () {
@@ -122,9 +124,9 @@ describe('gc', function () {
122124
await gcStarted
123125
const add2 = test.add2()
124126

125-
const deleted = (await gc).map(i => i.cid.toString())
126-
const add1Res = test.resToCid(await add1)
127-
const add2Res = test.resToCid(await add2)
127+
const deleted = (await gc).map(i => i.multihash)
128+
const add1Res = test.resToMultihash(await add1)
129+
const add2Res = test.resToMultihash(await add2)
128130

129131
// Should have garbage collected blocks from first add, because GC should
130132
// have waited for first add to finish
@@ -152,9 +154,9 @@ describe('gc', function () {
152154
await gcStarted
153155
const add2 = ipfs.add(fixtures[3], { pin: true })
154156

155-
const deleted = (await gc).map(i => i.cid.toString())
156-
const add1Res = (await add1)[0].hash
157-
const add2Res = (await add2)[0].hash
157+
const deleted = (await gc).map(i => i.multihash)
158+
const add1Res = base32.encode(new CID((await add1)[0].hash).multihash)
159+
const add2Res = base32.encode(new CID((await add2)[0].hash).multihash)
158160

159161
// Should not have garbage collected blocks from first add, because GC should
160162
// have waited for first add + pin to finish (protected by pin)
@@ -168,7 +170,9 @@ describe('gc', function () {
168170
it('garbage collection should wait for pending block rm to finish', async () => {
169171
// Add two blocks so that we can remove them
170172
const cid1 = (await ipfs.block.put(Buffer.from('block to rm 1'), null)).cid
173+
const cid1Multihash = base32.encode(cid1.multihash)
171174
const cid2 = (await ipfs.block.put(Buffer.from('block to rm 2'), null)).cid
175+
const cid2Multihash = base32.encode(cid2.multihash)
172176

173177
// Remove first block from IPFS
174178
// Note: block rm will take a write lock
@@ -185,33 +189,34 @@ describe('gc', function () {
185189
await gcStarted
186190
const rm2 = ipfs.block.rm(cid2)
187191

188-
const deleted = (await gc).map(i => i.cid.toString())
189-
await rm1
190-
191-
// Second rm should fail because GC has already removed that block
192-
try {
193-
await rm2
194-
} catch (err) {
195-
expect(err.code).eql(Errors.dbDeleteFailedError().code)
196-
}
192+
const deleted = (await gc).map(i => i.multihash)
193+
const rm1Out = await rm1
194+
expect(rm1Out[0]).to.not.have.property('error')
197195

198196
// Confirm second block has been removed
199-
const localRefs = (await ipfs.refs.local()).map(r => r.ref)
200-
expect(localRefs).not.includes(cid2.toString())
197+
const localMultihashes = (await ipfs.refs.local()).map(r => base32.encode(new CID(r.ref).multihash))
198+
expect(localMultihashes).not.includes(cid2Multihash)
199+
200+
// Second rm should fail because GC has already removed that block
201+
expect((await rm2)[0])
202+
.to.have.property('error')
203+
.that.has.property('code').that.equal(Errors.dbDeleteFailedError().code)
201204

202205
// Should not have garbage collected block from first block put, because
203206
// GC should have waited for first rm (removing first block put) to finish
204-
expect(deleted).not.includes(cid1.toString())
207+
expect(deleted).not.includes(cid1Multihash)
205208

206209
// Should have garbage collected block from second block put, because GC
207210
// should have completed before second rm (removing second block put)
208-
expect(deleted).includes(cid2.toString())
211+
expect(deleted).includes(cid2Multihash)
209212
})
210213

211214
it('garbage collection should wait for pending pin add to finish', async () => {
212215
// Add two blocks so that we can pin them
213-
const cid1 = (await ipfs.block.put(Buffer.from('block to pin add 1'), null)).cid
214-
const cid2 = (await ipfs.block.put(Buffer.from('block to pin add 2'), null)).cid
216+
const cid1 = (await ipfs.block.put(Buffer.from('block to test pin add 1'), null)).cid
217+
const cid2 = (await ipfs.block.put(Buffer.from('block to test pin add 2'), null)).cid
218+
const cid1Multihash = base32.encode(cid1.multihash)
219+
const cid2Multihash = base32.encode(cid2.multihash)
215220

216221
// Pin first block
217222
// Note: pin add will take a read lock
@@ -221,30 +226,32 @@ describe('gc', function () {
221226
// Once pin lock has been requested, start GC
222227
await pinLockRequested
223228
const gc = ipfs.repo.gc()
224-
const deleted = (await gc).map(i => i.cid.toString())
229+
const deleted = (await gc).map(i => i.multihash)
225230
await pin1
226231

227232
// TODO: Adding pin for removed block never returns, which means the lock
228233
// never gets released
229234
// const pin2 = ipfs.pin.add(cid2)
230235

231236
// Confirm second second block has been removed
232-
const localRefs = (await ipfs.refs.local()).map(r => r.ref)
233-
expect(localRefs).not.includes(cid2.toString())
237+
const localMultihashes = (await ipfs.refs.local()).map(r => base32.encode(new CID(r.ref).multihash))
238+
expect(localMultihashes).not.includes(cid2Multihash)
234239

235240
// Should not have garbage collected block from first block put, because
236241
// GC should have waited for pin (protecting first block put) to finish
237-
expect(deleted).not.includes(cid1.toString())
242+
expect(deleted).not.includes(cid1Multihash)
238243

239244
// Should have garbage collected block from second block put, because GC
240245
// should have completed before second pin
241-
expect(deleted).includes(cid2.toString())
246+
expect(deleted).includes(cid2Multihash)
242247
})
243248

244249
it('garbage collection should wait for pending pin rm to finish', async () => {
245250
// Add two blocks so that we can pin them
246251
const cid1 = (await ipfs.block.put(Buffer.from('block to pin rm 1'), null)).cid
247252
const cid2 = (await ipfs.block.put(Buffer.from('block to pin rm 2'), null)).cid
253+
const cid1Multihash = base32.encode(cid1.multihash)
254+
const cid2Multihash = base32.encode(cid2.multihash)
248255

249256
// Pin blocks
250257
await ipfs.pin.add(cid1)
@@ -265,17 +272,17 @@ describe('gc', function () {
265272
await gcStarted
266273
const pinRm2 = ipfs.pin.rm(cid2)
267274

268-
const deleted = (await gc).map(i => i.cid.toString())
275+
const deleted = (await gc).map(i => i.multihash)
269276
await pinRm1
270277
await pinRm2
271278

272279
// Should have garbage collected block from first block put, because
273280
// GC should have waited for pin rm (unpinning first block put) to finish
274-
expect(deleted).includes(cid1.toString())
281+
expect(deleted).includes(cid1Multihash)
275282

276283
// Should not have garbage collected block from second block put, because
277284
// GC should have completed before second block was unpinned
278-
expect(deleted).not.includes(cid2.toString())
285+
expect(deleted).not.includes(cid2Multihash)
279286
})
280287
})
281288
})

0 commit comments

Comments
 (0)