Commit b3f9bdef authored by Kelvin Fichter's avatar Kelvin Fichter

feat(cmn): gracefully handle BaseServiceV2 exits

Gracefully handles BaseServiceV2 exits by default. Adds a new function
for stopping the service and catches SIGTERM and SIGINT correctly.
parent b5dd1f9f
---
'@eth-optimism/common-ts': patch
---
Have BaseServiceV2 gracefully catch exit signals
...@@ -52,6 +52,16 @@ export abstract class BaseServiceV2< ...@@ -52,6 +52,16 @@ export abstract class BaseServiceV2<
*/ */
protected loopIntervalMs: number protected loopIntervalMs: number
/**
* Whether or not the service is currently running.
*/
protected running: boolean
/**
* Whether or not the service has run to completion.
*/
protected done: boolean
/** /**
* Logger class for this service. * Logger class for this service.
*/ */
...@@ -181,14 +191,24 @@ export abstract class BaseServiceV2< ...@@ -181,14 +191,24 @@ export abstract class BaseServiceV2<
}, {}) as TMetrics }, {}) as TMetrics
this.logger = new Logger({ name: params.name }) this.logger = new Logger({ name: params.name })
// Gracefully handle stop signals.
const stop = async (signal: string) => {
this.logger.info(`stopping service`, { signal })
await this.stop()
process.exit(0)
}
process.on('SIGTERM', stop)
process.on('SIGINT', stop)
} }
/** /**
* Runs the main function. If this service is set up to loop, will repeatedly loop around the * Runs the main function. If this service is set up to loop, will repeatedly loop around the
* main function. Will also catch unhandled errors. * main function. Will also catch unhandled errors.
*/ */
public run(): void { public async run(): Promise<void> {
const _run = async () => { this.done = false
if (this.init) { if (this.init) {
this.logger.info('initializing service') this.logger.info('initializing service')
await this.init() await this.init()
...@@ -197,7 +217,8 @@ export abstract class BaseServiceV2< ...@@ -197,7 +217,8 @@ export abstract class BaseServiceV2<
if (this.loop) { if (this.loop) {
this.logger.info('starting main loop') this.logger.info('starting main loop')
while (true) { this.running = true
while (this.running) {
try { try {
await this.main() await this.main()
} catch (err) { } catch (err) {
...@@ -208,16 +229,30 @@ export abstract class BaseServiceV2< ...@@ -208,16 +229,30 @@ export abstract class BaseServiceV2<
}) })
} }
// Always sleep between loops // Sleep between loops if we're still running (service not stopped).
if (this.running) {
await sleep(this.loopIntervalMs) await sleep(this.loopIntervalMs)
} }
}
} else { } else {
this.logger.info('running main function') this.logger.info('running main function')
await this.main() await this.main()
} }
this.done = true
} }
_run() /**
* Tries to gracefully stop the service. Service will continue running until the current loop
* iteration is finished and will then stop looping.
*/
public async stop(): Promise<void> {
this.running = false
// Wait until the main loop has finished.
while (!this.done) {
await sleep(1000)
}
} }
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment