/* eslint-disable handle-callback-err, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* * 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 */ let LockManager const logger = require('logger-sharelatex') const LockState = {} // locks for docker container operations, by container name 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 tryLock(key, callback) { let lockValue if (callback == null) { callback = function(err, gotLock) {} } 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) }, getLock(key, callback) { let attempt if (callback == null) { callback = function(error, lockValue) {} } const startTime = Date.now() return (attempt = () => LockManager.tryLock(key, function(error, gotLock, lockValue) { 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) } }))() }, releaseLock(key, lockValue, callback) { if (callback == null) { callback = function(error) {} } 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() } }, runWithLock(key, runner, callback) { if (callback == null) { callback = function(error) {} } return LockManager.getLock(key, function(error, lockValue) { if (error != null) { return callback(error) } return runner((error1, ...args) => LockManager.releaseLock(key, lockValue, function(error2) { error = error1 || error2 if (error != null) { return callback(error) } return callback(null, ...Array.from(args)) }) ) }) } }