Back off fetching the target state exponentially, for faster retries when there's no connectivity

Change-Type: patch
Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
This commit is contained in:
Pablo Carranza Velez 2018-03-20 17:06:50 -03:00 committed by Cameron Diver
parent caca29f3a0
commit 72a5f03b0c
No known key found for this signature in database
GPG Key ID: E76D7ACBEE436E12

View File

@ -8,7 +8,7 @@ express = require 'express'
bodyParser = require 'body-parser' bodyParser = require 'body-parser'
Lock = require 'rwlock' Lock = require 'rwlock'
{ request, requestOpts } = require './lib/request' { request, requestOpts } = require './lib/request'
{ checkTruthy } = require './lib/validation' { checkTruthy, checkInt } = require './lib/validation'
DuplicateUuidError = (err) -> DuplicateUuidError = (err) ->
_.startsWith(err.message, '"uuid" must be unique') _.startsWith(err.message, '"uuid" must be unique')
@ -26,6 +26,7 @@ createAPIBinderRouter = (apiBinder) ->
apiBinder.eventTracker.track('Update notification') apiBinder.eventTracker.track('Update notification')
if apiBinder.readyForUpdates if apiBinder.readyForUpdates
apiBinder.getAndSetTargetState(req.body.force) apiBinder.getAndSetTargetState(req.body.force)
.catchReturn()
res.sendStatus(204) res.sendStatus(204)
return router return router
@ -40,6 +41,7 @@ module.exports = class APIBinder
@_targetStateInterval = null @_targetStateInterval = null
@reportPending = false @reportPending = false
@stateReportErrors = 0 @stateReportErrors = 0
@targetStateFetchErrors = 0
@router = createAPIBinderRouter(this) @router = createAPIBinderRouter(this)
_lock = new Lock() _lock = new Lock()
@_writeLock = Promise.promisify(_lock.async.writeLock) @_writeLock = Promise.promisify(_lock.async.writeLock)
@ -332,27 +334,31 @@ module.exports = class APIBinder
.then => .then =>
@lastTarget = _.cloneDeep(targetState) @lastTarget = _.cloneDeep(targetState)
@deviceState.triggerApplyTarget({ force }) @deviceState.triggerApplyTarget({ force })
.catch (err) -> .tapCatch (err) ->
console.error("Failed to get target state for device: #{err}") console.error("Failed to get target state for device: #{err}")
.finally => .finally =>
@lastTargetStateFetch = process.hrtime() @lastTargetStateFetch = process.hrtime()
_pollTargetState: => _pollTargetState: =>
@getAndSetTargetState()
.then =>
@targetStateFetchErrors = 0
@config.get('appUpdatePollInterval')
.catch =>
@targetStateFetchErrors += 1
@config.get('appUpdatePollInterval') @config.get('appUpdatePollInterval')
.then (appUpdatePollInterval) => .then (appUpdatePollInterval) =>
if @_targetStateInterval? Math.min(appUpdatePollInterval, 15000 * 2 ** (@targetStateFetchErrors - 1))
clearInterval(@_targetStateInterval) .then(checkInt)
@_targetStateInterval = setInterval(@getAndSetTargetState, appUpdatePollInterval) .then(Promise.delay)
@getAndSetTargetState() .then(@_pollTargetState)
return null
startTargetStatePoll: -> startTargetStatePoll: =>
Promise.try =>
if !@resinApi? if !@resinApi?
throw new Error('Trying to start poll without initializing API client') throw new Error('Trying to start poll without initializing API client')
@_pollTargetState() @_pollTargetState()
@config.on 'change', (changedConfig) => return null
if changedConfig.appUpdatePollInterval?
@_pollTargetState()
_getStateDiff: => _getStateDiff: =>
diff = { diff = {