This repository has been archived by the owner on Feb 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Status handler infrastructure and API impl (#40)
* Status hanlder infrastructure Implements status handling infrastructure. This allows multiple polling status handlers to exist for different dependencies. The status and detail for these are then combined to form an overall service status which is served by the health endpoint * Add timeout for service status * Switch looping structure to a generator * Rename accumulator to make it clear it\'s a status value * Small refactor and handled graceful close * Add API healthcheck
- Loading branch information
1 parent
4ac9e77
commit 27b0feb
Showing
13 changed files
with
641 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const { startStatusHandler, serviceState } = require('../util/statusPoll') | ||
const { substrateApi } = require('../util/substrateApi') | ||
const { SUBSTRATE_STATUS_POLL_PERIOD_MS, SUBSTRATE_STATUS_TIMEOUT_MS } = require('../env') | ||
|
||
const getStatus = async () => { | ||
await substrateApi.isReady | ||
const [chain, runtime] = await Promise.all([substrateApi.runtimeChain, substrateApi.runtimeVersion]) | ||
return { | ||
status: serviceState.UP, | ||
detail: { | ||
chain, | ||
runtime: { | ||
name: runtime.specName, | ||
versions: { | ||
spec: runtime.specVersion.toNumber(), | ||
impl: runtime.implVersion.toNumber(), | ||
authoring: runtime.authoringVersion.toNumber(), | ||
transaction: runtime.transactionVersion.toNumber(), | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
const startApiStatus = () => | ||
startStatusHandler({ | ||
getStatus, | ||
pollingPeriodMs: SUBSTRATE_STATUS_POLL_PERIOD_MS, | ||
serviceTimeoutMs: SUBSTRATE_STATUS_TIMEOUT_MS, | ||
}) | ||
|
||
module.exports = startApiStatus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
const startApiStatus = require('./apiStatus') | ||
const { buildCombinedHandler } = require('../util/statusPoll') | ||
|
||
const startStatusHandlers = async () => { | ||
const handlers = new Map() | ||
handlers.set('api', await startApiStatus()) | ||
|
||
return buildCombinedHandler(handlers) | ||
} | ||
|
||
module.exports = { | ||
startStatusHandlers, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
const serviceState = { | ||
UP: Symbol('status-up'), | ||
DOWN: Symbol('status-down'), | ||
ERROR: Symbol('status-error'), | ||
} | ||
const stateSymbols = new Set(Object.values(serviceState)) | ||
|
||
const delay = (delayMs, result) => new Promise((resolve) => setTimeout(resolve, delayMs, result)) | ||
|
||
const mkStatusGenerator = async function* ({ getStatus, serviceTimeoutMs }) { | ||
while (true) { | ||
try { | ||
const newStatus = await Promise.race([ | ||
getStatus(), | ||
delay(serviceTimeoutMs, { status: serviceState.ERROR, detail: null }), | ||
]) | ||
|
||
if (stateSymbols.has(newStatus.status)) { | ||
yield { | ||
status: newStatus.status, | ||
detail: newStatus.detail === undefined ? null : newStatus.detail, | ||
} | ||
continue | ||
} | ||
throw new Error('Status is not a valid value') | ||
} catch (err) { | ||
yield { | ||
status: serviceState.ERROR, | ||
detail: null, | ||
} | ||
} | ||
} | ||
} | ||
|
||
const startStatusHandler = async ({ pollingPeriodMs, serviceTimeoutMs, getStatus }) => { | ||
let status = null | ||
const statusGenerator = mkStatusGenerator({ getStatus, serviceTimeoutMs }) | ||
status = (await statusGenerator.next()).value | ||
|
||
const statusLoop = async function () { | ||
await delay(pollingPeriodMs) | ||
for await (const newStatus of statusGenerator) { | ||
status = newStatus | ||
await delay(pollingPeriodMs) | ||
} | ||
} | ||
statusLoop() | ||
|
||
return { | ||
get status() { | ||
return status.status | ||
}, | ||
get detail() { | ||
return status.detail | ||
}, | ||
close: () => { | ||
statusGenerator.return() | ||
}, | ||
} | ||
} | ||
|
||
const buildCombinedHandler = async (handlerMap) => { | ||
const getStatus = () => | ||
[...handlerMap].reduce((accStatus, [, h]) => { | ||
const handlerStatus = h.status | ||
if (accStatus === serviceState.UP) { | ||
return handlerStatus | ||
} | ||
if (accStatus === serviceState.DOWN) { | ||
return accStatus | ||
} | ||
if (handlerStatus === serviceState.DOWN) { | ||
return handlerStatus | ||
} | ||
return accStatus | ||
}, serviceState.UP) | ||
|
||
return { | ||
get status() { | ||
return getStatus() | ||
}, | ||
get detail() { | ||
return Object.fromEntries([...handlerMap].map(([name, { detail }]) => [name, detail])) | ||
}, | ||
close: () => { | ||
for (const handler of handlerMap.values()) { | ||
handler.close() | ||
} | ||
}, | ||
} | ||
} | ||
|
||
module.exports = { | ||
serviceState, | ||
startStatusHandler, | ||
buildCombinedHandler, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.