Skip to content

Commit

Permalink
Merge pull request #228 from FlowFuse/227-health-check-interval
Browse files Browse the repository at this point in the history
User defined health check interval
  • Loading branch information
knolleary committed May 1, 2024
2 parents 279538d + 871d7a8 commit 64d2704
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 8 deletions.
4 changes: 4 additions & 0 deletions lib/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const bodyParser = require('body-parser')
const { States } = require('./launcher')

class AdminInterface {
/**
* @param {Object} options Options
* @param {import ('./launcher').Launcher} launcher The launcher instance
*/
constructor (options, launcher) {
this.options = options
this.launcher = launcher
Expand Down
20 changes: 12 additions & 8 deletions lib/launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,8 @@ const MIN_RUNTIME_DEVIATION = 2000 // 2 seconds either side of the mean
/** How long wait for Node-RED to cleanly stop before killing */
const NODE_RED_STOP_TIMEOUT = 10000

/** Interval between status polls of Node-RED used to detect a hung runtime */
/** Interval between status polls of Node-RED used to detect a hung runtime */
const HEALTH_POLL_INTERVAL = 7499 // A prime number (to minimise syncing with other processes)

/** Timeout to apply to health polling requests */
const HEALTH_POLL_TIMEOUT = HEALTH_POLL_INTERVAL - 500 // Allow 500ms to avoid overlapping requests
/** Default interval between status polls of Node-RED used to detect a hung runtime */
const HEALTH_POLL_INTERVAL_DEFAULT = 7499 // A prime number (to minimise syncing with other processes)

/** The number of consecutive timeouts during startup phase before considering a NR hang */
const HEALTH_POLL_MAX_STARTUP_ERROR_COUNT = 10
Expand Down Expand Up @@ -92,6 +88,13 @@ class Launcher {
this.memoryAuditLogged = 0
}

/** @type {Number} */
get healthCheckInterval () {
const parsed = parseInt(this.settings?.healthCheckInterval)
const value = isNaN(parsed) ? 0 : parsed
return value > 1000 ? value : HEALTH_POLL_INTERVAL_DEFAULT
}

async loadSettings () {
this.state = States.LOADING
this.logBuffer.add({ level: 'system', msg: 'Loading project settings' })
Expand Down Expand Up @@ -416,7 +419,7 @@ class Launcher {
pragma: 'no-cache',
'Cache-Control': 'max-age=0, must-revalidate, no-cache'
},
timeout: { request: HEALTH_POLL_TIMEOUT },
timeout: { request: this.healthCheckInterval - 500 },
retry: { limit: 0 }
}
// Use a HEAD request to minimise data transfer
Expand Down Expand Up @@ -488,6 +491,7 @@ class Launcher {
clearInterval(this.healthPoll)
}
let errorCount = 0
this.logBuffer.add({ level: 'system', msg: `Starting health check monitor (${(this.healthCheckInterval / 1000).toFixed(1)}s)` })
this.healthPoll = setInterval(() => {
if (this.state === States.STARTING || this.state === States.RUNNING) {
statusPoll().then(() => {
Expand All @@ -513,7 +517,7 @@ class Launcher {
}
})
}
}, HEALTH_POLL_INTERVAL)
}, this.healthCheckInterval)

if (this.resourcePoll) {
clearInterval(this.resourcePoll)
Expand Down
112 changes: 112 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"mocha": "^10.2.0",
"sass": "1.66.1",
"should": "^13.2.3",
"sinon" : "^17.0.1",
"sqlite3": "^5.1.6",
"yaml": "^2.1.3"
}
Expand Down
37 changes: 37 additions & 0 deletions test/unit/lib/launcher_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const should = require('should') // eslint-disable-line
const sinon = require('sinon')
const launcher = require('../../../lib/launcher.js')

describe.only('Launcher', function () {
it('should create a new launcher', async function () {
const l = new launcher.Launcher({})
should.exist(l)
})
describe('health check', function () {
it('has a default value', async function () {
const l = new launcher.Launcher({})
l.should.have.property('healthCheckInterval', 7499)
})
it('can be set by user', async function () {
const l = new launcher.Launcher({})
sinon.stub(l, 'loadSettings').callsFake(() => {
l.settings = {
healthCheckInterval: 1234
}
})
await l.loadSettings()
l.should.have.property('healthCheckInterval', 1234)
})
it('cannot be less than 1 second', async function () {
const l = new launcher.Launcher({})
sinon.stub(l, 'loadSettings').callsFake(() => {
l.settings = {
healthCheckInterval: 999
}
})
await l.loadSettings()
// returns the default value when the user sets the value out of range
l.should.have.property('healthCheckInterval', 7499)
})
})
})

0 comments on commit 64d2704

Please sign in to comment.