Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User defined health check interval #228

Merged
merged 5 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
})
})
})
Loading