Use rwlock together with lockfile

Select app to kill from DB within lock (otherwise, if some other part kills and restarts the app, the
containerId will have changed and the real container will not be removed).
This commit is contained in:
Pablo Carranza Vélez 2015-08-21 19:48:07 +00:00
parent d1b317399e
commit c7c4aed746
2 changed files with 38 additions and 22 deletions

View File

@ -67,20 +67,20 @@ module.exports = (secret) ->
utils.mixpanelTrack('Purge /data', appId) utils.mixpanelTrack('Purge /data', appId)
if !appId? if !appId?
return res.status(400).send('Missing app id') return res.status(400).send('Missing app id')
knex('app').select().where({ appId }) Promise.using application.lockUpdates(app), ->
.then ([ app ]) -> knex('app').select().where({ appId })
if !app? .then ([ app ]) ->
throw new Error('App not found') if !app?
Promise.using application.lockUpdates(app), -> throw new Error('App not found')
application.kill(app) application.kill(app)
.then -> .then ->
new Promise (resolve, reject) -> new Promise (resolve, reject) ->
request.post(config.gosuperAddress + '/v1/purge', { json: true, body: applicationId: appId.toString() }) request.post(config.gosuperAddress + '/v1/purge', { json: true, body: applicationId: appId.toString() })
.on 'error', reject .on 'error', reject
.on 'response', -> resolve() .on 'response', -> resolve()
.pipe(res) .pipe(res)
.finally -> .finally ->
application.start(app) application.start(app)
.catch (err) -> .catch (err) ->
res.status(503).send(err?.message or err or 'Unknown error') res.status(503).send(err?.message or err or 'Unknown error')

View File

@ -251,13 +251,19 @@ exports.unlockAndStart = unlockAndStart = (app) ->
.then -> .then ->
start(app) start(app)
exports.lockUpdates = lockUpdates = (app, force) -> exports.lockUpdates = lockUpdates = do ->
Promise.try -> _lock = new Lock()
lockFile.unlockAsync(lockPath(app)) if force == true _writeLock = Promise.promisify(_lock.async.writeLock)
.then -> return (app, force) ->
lockFile.lockAsync(lockPath(app)) _writeLock(lockPath(app))
.disposer -> .tap ->
lockFile.unlockAsync(lockPath(app)) lockFile.unlockAsync(lockPath(app)) if force == true
.tap ->
lockFile.lockAsync(lockPath(app))
.disposer (release) ->
lockFile.unlockAsync(lockPath(app))
.then ->
release()
joinErrorMessages = (failures) -> joinErrorMessages = (failures) ->
s = if failures.length > 1 then 's' else '' s = if failures.length > 1 then 's' else ''
@ -354,7 +360,13 @@ exports.update = update = (force) ->
# Then delete all the ones to remove in one go # Then delete all the ones to remove in one go
Promise.map toBeRemoved, (appId) -> Promise.map toBeRemoved, (appId) ->
Promise.using lockUpdates(apps[appId], force), -> Promise.using lockUpdates(apps[appId], force), ->
kill(apps[appId]) # We get the app from the DB again in case someone restarted it
# (which would have changed its containerId)
knex('app').select().where({ appId })
.then ([ app ]) ->
if !app?
throw new Error('App not found')
kill(app)
.then -> .then ->
knex('app').where('appId', appId).delete() knex('app').where('appId', appId).delete()
.catch (err) -> .catch (err) ->
@ -372,7 +384,11 @@ exports.update = update = (force) ->
app = remoteApps[appId] app = remoteApps[appId]
logSystemEvent(logTypes.updateApp, app) if localApp.imageId == app.imageId logSystemEvent(logTypes.updateApp, app) if localApp.imageId == app.imageId
Promise.using lockUpdates(localApp, force), -> Promise.using lockUpdates(localApp, force), ->
kill(localApp) knex('app').select().where({ appId })
.then ([ localApp ]) ->
if !localApp?
throw new Error('App not found')
kill(localApp)
.then -> .then ->
start(app) start(app)
.catch (err) -> .catch (err) ->