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

Commit 5348a50

Browse files
committed
feat: add locking for concurrent pin operations
1 parent 280f987 commit 5348a50

File tree

6 files changed

+216
-136
lines changed

6 files changed

+216
-136
lines changed

src/core/components/pin.js

+18-23
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function toB58String (hash) {
2020

2121
module.exports = (self) => {
2222
const dag = self.dag
23-
const pinManager = new PinManager(self._repo, dag, self.log)
23+
const pinManager = new PinManager(self._repo, dag, self._options.repoOwner, self.log)
2424

2525
const pin = {
2626
add: promisify((paths, options, callback) => {
@@ -43,7 +43,7 @@ module.exports = (self) => {
4343
if (recursive) {
4444
if (pinManager.recursivePins.has(key)) {
4545
// it's already pinned recursively
46-
return cb(null, key)
46+
return cb(null, null)
4747
}
4848

4949
// entire graph of nested links should be pinned,
@@ -60,7 +60,7 @@ module.exports = (self) => {
6060
}
6161
if (pinManager.directPins.has(key)) {
6262
// already directly pinned
63-
return cb(null, key)
63+
return cb(null, null)
6464
}
6565

6666
// make sure we have the object
@@ -73,15 +73,20 @@ module.exports = (self) => {
7373
}, (err, results) => {
7474
if (err) { return pinComplete(err) }
7575

76-
// update the pin sets in memory
77-
const pinset = recursive ? pinManager.recursivePins : pinManager.directPins
78-
results.forEach(key => pinset.add(key))
79-
80-
// persist updated pin sets to datastore
81-
pinManager.flushPins((err, root) => {
76+
const flushComplete = (err) => {
8277
if (err) { return pinComplete(err) }
83-
pinComplete(null, results.map(hash => ({ hash })))
84-
})
78+
pinComplete(null, mhs.map(mh => ({ hash: toB58String(mh) })))
79+
}
80+
81+
// each result is either a key or null if there is already a pin
82+
results = results.filter(Boolean)
83+
if (!results.length) { return flushComplete() }
84+
85+
if (recursive) {
86+
pinManager.addRecursivePins(results, flushComplete)
87+
} else {
88+
pinManager.addDirectPins(results, flushComplete)
89+
}
8590
})
8691
}
8792

@@ -143,20 +148,10 @@ module.exports = (self) => {
143148
}, (err, results) => {
144149
if (err) { return lockCb(err) }
145150

146-
// update the pin sets in memory
147-
results.forEach(key => {
148-
if (recursive && pinManager.recursivePins.has(key)) {
149-
pinManager.recursivePins.delete(key)
150-
} else {
151-
pinManager.directPins.delete(key)
152-
}
153-
})
154-
155-
// persist updated pin sets to datastore
156-
pinManager.flushPins((err, root) => {
151+
pinManager.rmPins(results, recursive, (err) => {
157152
if (err) { return lockCb(err) }
158153
self.log(`Removed pins: ${results}`)
159-
lockCb(null, results.map(hash => ({ hash })))
154+
lockCb(null, mhs.map(mh => ({ hash: toB58String(mh) })))
160155
})
161156
})
162157
}, callback)

src/core/components/pin/gc-lock.js renamed to src/core/components/pin/lock.js

+15-15
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@
33
const mortice = require('mortice')
44
const pull = require('pull-stream')
55
const EventEmitter = require('events')
6-
const log = require('debug')('ipfs:gc:lock')
6+
const debug = require('debug')
77

8-
class GCLock extends EventEmitter {
9-
constructor (repoOwner) {
8+
class Lock extends EventEmitter {
9+
constructor (repoOwner, debugName) {
1010
super()
1111

12-
// Ensure that we get a different mutex for each instance of GCLock
13-
// (There should only be one GCLock instance per IPFS instance, but
14-
// there may be multiple IPFS instances, eg in unit tests)
12+
// Ensure that we get a different mutex for each instance of Lock
1513
const randId = (~~(Math.random() * 1e9)).toString(36) + Date.now()
1614
this.mutex = mortice(randId, {
1715
singleProcess: repoOwner
1816
})
1917

2018
this.lockId = 0
19+
this.log = debug(debugName || 'lock')
2120
}
2221

2322
readLock (lockedFn, cb) {
@@ -37,14 +36,14 @@ class GCLock extends EventEmitter {
3736
}
3837

3938
const lockId = this.lockId++
40-
log(`[${lockId}] ${type} requested`)
39+
this.log(`[${lockId}] ${type} requested`)
4140
this.emit(`${type} request`, lockId)
4241
const locked = () => new Promise((resolve, reject) => {
4342
this.emit(`${type} start`, lockId)
44-
log(`[${lockId}] ${type} started`)
43+
this.log(`[${lockId}] ${type} started`)
4544
lockedFn((err, res) => {
4645
this.emit(`${type} release`, lockId)
47-
log(`[${lockId}] ${type} released`)
46+
this.log(`[${lockId}] ${type} released`)
4847
err ? reject(err) : resolve(res)
4948
})
5049
})
@@ -62,7 +61,7 @@ class GCLock extends EventEmitter {
6261
}
6362

6463
pullLock (type, lockedPullFn) {
65-
const pullLocker = new PullLocker(this, this.mutex, type, this.lockId++)
64+
const pullLocker = new PullLocker(this, this.mutex, type, this.lockId++, this.log)
6665

6766
return pull(
6867
pullLocker.take(),
@@ -73,11 +72,12 @@ class GCLock extends EventEmitter {
7372
}
7473

7574
class PullLocker {
76-
constructor (emitter, mutex, type, lockId) {
75+
constructor (emitter, mutex, type, lockId, log) {
7776
this.emitter = emitter
7877
this.mutex = mutex
7978
this.type = type
8079
this.lockId = lockId
80+
this.log = log
8181

8282
// This Promise resolves when the mutex gives us permission to start
8383
// running the locked piece of code
@@ -91,7 +91,7 @@ class PullLocker {
9191
return new Promise((resolve, reject) => {
9292
this.releaseLock = (err) => err ? reject(err) : resolve()
9393

94-
log(`[${this.lockId}] ${this.type} (pull) started`)
94+
this.log(`[${this.lockId}] ${this.type} (pull) started`)
9595
this.emitter.emit(`${this.type} start`, this.lockId)
9696

9797
// The locked piece of code is ready to start, so resolve the
@@ -106,7 +106,7 @@ class PullLocker {
106106
return pull(
107107
pull.asyncMap((i, cb) => {
108108
if (!this.lock) {
109-
log(`[${this.lockId}] ${this.type} (pull) requested`)
109+
this.log(`[${this.lockId}] ${this.type} (pull) requested`)
110110
this.emitter.emit(`${this.type} request`, this.lockId)
111111
// Request the lock
112112
this.lock = this.mutex[this.type](() => this.locked())
@@ -125,11 +125,11 @@ class PullLocker {
125125
// Releases the lock
126126
release () {
127127
return pull.through(null, (err) => {
128-
log(`[${this.lockId}] ${this.type} (pull) released`)
128+
this.log(`[${this.lockId}] ${this.type} (pull) released`)
129129
this.emitter.emit(`${this.type} release`, this.lockId)
130130
this.releaseLock(err)
131131
})
132132
}
133133
}
134134

135-
module.exports = GCLock
135+
module.exports = Lock

0 commit comments

Comments
 (0)