2020-02-19 06:14:28 -05:00
|
|
|
/* eslint-disable
|
|
|
|
handle-callback-err,
|
|
|
|
*/
|
|
|
|
// TODO: This file was created by bulk-decaffeinate.
|
|
|
|
// Fix any style issues and re-enable lint.
|
2020-02-19 06:14:14 -05:00
|
|
|
/*
|
|
|
|
* decaffeinate suggestions:
|
|
|
|
* DS101: Remove unnecessary use of Array.from
|
|
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
|
|
* DS207: Consider shorter variations of null checks
|
|
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
|
|
*/
|
2020-02-19 06:14:37 -05:00
|
|
|
let LockManager
|
|
|
|
const logger = require('logger-sharelatex')
|
2018-03-05 06:02:31 -05:00
|
|
|
|
2020-02-19 06:14:37 -05:00
|
|
|
const LockState = {} // locks for docker container operations, by container name
|
2018-03-05 06:02:31 -05:00
|
|
|
|
2020-02-19 06:14:37 -05:00
|
|
|
module.exports = LockManager = {
|
|
|
|
MAX_LOCK_HOLD_TIME: 15000, // how long we can keep a lock
|
|
|
|
MAX_LOCK_WAIT_TIME: 10000, // how long we wait for a lock
|
|
|
|
LOCK_TEST_INTERVAL: 1000, // retry time
|
2018-03-05 06:02:31 -05:00
|
|
|
|
2020-02-19 06:14:37 -05:00
|
|
|
tryLock(key, callback) {
|
|
|
|
let lockValue
|
|
|
|
if (callback == null) {
|
2020-08-10 12:01:11 -04:00
|
|
|
callback = function (err, gotLock) {}
|
2020-02-19 06:14:37 -05:00
|
|
|
}
|
|
|
|
const existingLock = LockState[key]
|
|
|
|
if (existingLock != null) {
|
|
|
|
// the lock is already taken, check how old it is
|
|
|
|
const lockAge = Date.now() - existingLock.created
|
|
|
|
if (lockAge < LockManager.MAX_LOCK_HOLD_TIME) {
|
|
|
|
return callback(null, false) // we didn't get the lock, bail out
|
|
|
|
} else {
|
|
|
|
logger.error(
|
|
|
|
{ key, lock: existingLock, age: lockAge },
|
|
|
|
'taking old lock by force'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// take the lock
|
|
|
|
LockState[key] = lockValue = { created: Date.now() }
|
|
|
|
return callback(null, true, lockValue)
|
|
|
|
},
|
2018-03-05 06:02:31 -05:00
|
|
|
|
2020-02-19 06:14:37 -05:00
|
|
|
getLock(key, callback) {
|
|
|
|
let attempt
|
|
|
|
if (callback == null) {
|
2020-08-10 12:01:11 -04:00
|
|
|
callback = function (error, lockValue) {}
|
2020-02-19 06:14:37 -05:00
|
|
|
}
|
|
|
|
const startTime = Date.now()
|
|
|
|
return (attempt = () =>
|
2020-08-10 12:01:11 -04:00
|
|
|
LockManager.tryLock(key, function (error, gotLock, lockValue) {
|
2020-02-19 06:14:37 -05:00
|
|
|
if (error != null) {
|
|
|
|
return callback(error)
|
|
|
|
}
|
|
|
|
if (gotLock) {
|
|
|
|
return callback(null, lockValue)
|
|
|
|
} else if (Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME) {
|
|
|
|
const e = new Error('Lock timeout')
|
|
|
|
e.key = key
|
|
|
|
return callback(e)
|
|
|
|
} else {
|
|
|
|
return setTimeout(attempt, LockManager.LOCK_TEST_INTERVAL)
|
|
|
|
}
|
|
|
|
}))()
|
|
|
|
},
|
2018-03-05 06:02:31 -05:00
|
|
|
|
2020-02-19 06:14:37 -05:00
|
|
|
releaseLock(key, lockValue, callback) {
|
|
|
|
if (callback == null) {
|
2020-08-10 12:01:11 -04:00
|
|
|
callback = function (error) {}
|
2020-02-19 06:14:37 -05:00
|
|
|
}
|
|
|
|
const existingLock = LockState[key]
|
|
|
|
if (existingLock === lockValue) {
|
|
|
|
// lockValue is an object, so we can test by reference
|
|
|
|
delete LockState[key] // our lock, so we can free it
|
|
|
|
return callback()
|
|
|
|
} else if (existingLock != null) {
|
|
|
|
// lock exists but doesn't match ours
|
|
|
|
logger.error(
|
|
|
|
{ key, lock: existingLock },
|
|
|
|
'tried to release lock taken by force'
|
|
|
|
)
|
|
|
|
return callback()
|
|
|
|
} else {
|
|
|
|
logger.error(
|
|
|
|
{ key, lock: existingLock },
|
|
|
|
'tried to release lock that has gone'
|
|
|
|
)
|
|
|
|
return callback()
|
|
|
|
}
|
|
|
|
},
|
2018-03-05 06:02:31 -05:00
|
|
|
|
2020-02-19 06:14:37 -05:00
|
|
|
runWithLock(key, runner, callback) {
|
|
|
|
if (callback == null) {
|
2020-08-10 12:01:11 -04:00
|
|
|
callback = function (error) {}
|
2020-02-19 06:14:37 -05:00
|
|
|
}
|
2020-08-10 12:01:11 -04:00
|
|
|
return LockManager.getLock(key, function (error, lockValue) {
|
2020-02-19 06:14:37 -05:00
|
|
|
if (error != null) {
|
|
|
|
return callback(error)
|
|
|
|
}
|
|
|
|
return runner((error1, ...args) =>
|
2020-08-10 12:01:11 -04:00
|
|
|
LockManager.releaseLock(key, lockValue, function (error2) {
|
2020-02-19 06:14:37 -05:00
|
|
|
error = error1 || error2
|
|
|
|
if (error != null) {
|
|
|
|
return callback(error)
|
|
|
|
}
|
|
|
|
return callback(null, ...Array.from(args))
|
|
|
|
})
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|