Skip to content

Commit

Permalink
feat: lifecycle events for bot, svcs (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssube committed Dec 23, 2018
1 parent 7955675 commit 2d4e650
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 15 deletions.
6 changes: 5 additions & 1 deletion src/BaseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as uuid from 'uuid/v4';

import { SchemaError } from 'src/error/SchemaError';
import { ServiceModule } from 'src/module/ServiceModule';
import { Service, ServiceDefinition } from 'src/Service';
import { Service, ServiceDefinition, ServiceLifecycle } from 'src/Service';
import { Clock } from 'src/utils/Clock';
import { JsonPath } from 'src/utils/JsonPath';
import { dictToMap } from 'src/utils/Map';
Expand Down Expand Up @@ -64,6 +64,10 @@ export abstract class BaseService<TData> implements Service {
}
}

public async notify(event: ServiceLifecycle): Promise<void> {
this.logger.debug({ event }, 'service notified of event');
}

public abstract start(): Promise<void>;
public abstract stop(): Promise<void>;

Expand Down
14 changes: 13 additions & 1 deletion src/Bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { checkFilter, Filter, FilterData, FilterValue } from 'src/filter/Filter'
import { ContextFetchOptions, Listener, ListenerData } from 'src/listener/Listener';
import { ServiceModule } from 'src/module/ServiceModule';
import { Parser, ParserData } from 'src/parser/Parser';
import { Service, ServiceDefinition } from 'src/Service';
import { Service, ServiceDefinition, ServiceLifecycle } from 'src/Service';
import { filterNil, mustFind } from 'src/utils';
import { incrementServiceCounter } from 'src/utils/metrics/Service';
import { StorageLogger, StorageLoggerOptions } from 'src/utils/StorageLogger';
Expand Down Expand Up @@ -88,6 +88,18 @@ export class Bot extends BaseService<BotData> implements Service {
return this.storage;
}

public async notify(event: ServiceLifecycle) {
await super.notify(event);
await this.services.notify(event);

switch (event) {
case ServiceLifecycle.Reset:
this.metrics.clear();
this.logger.info('metrics reset');
break;
}
}

/**
* Set up the async resources that cannot be created in the constructor: filters, controllers, parsers, etc
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ export interface ServiceDefinition<TData = any> {
data: TData;
}

export enum ServiceLifecycle {
Reload = 'reload',
Reset = 'reset',
Start = 'start',
Stop = 'stop',
}

export interface ServiceMetadata {
/**
* The service's unique id.
Expand All @@ -33,6 +40,7 @@ export interface ServiceMetadata {
export interface Service extends ServiceMetadata {
readonly id: string;

notify(event: ServiceLifecycle): Promise<void>;
start(): Promise<void>;
stop(): Promise<void>;
}
Expand Down
28 changes: 23 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Container, Module } from 'noicejs';
import { Container, Logger, Module } from 'noicejs';
import * as sourceMapSupport from 'source-map-support';
import * as yargs from 'yargs-parser';

Expand All @@ -15,7 +15,8 @@ import { ServiceModule } from 'src/module/ServiceModule';
import { TransformModule } from 'src/module/TransformModule';
import { BunyanLogger } from 'src/utils/BunyanLogger';
import { Schema } from 'src/utils/Schema';
import { signal, SIGNAL_STOP } from 'src/utils/Signal';
import { signal, SIGNAL_RELOAD, SIGNAL_RESET, SIGNAL_STOP } from 'src/utils/Signal';
import { ServiceLifecycle } from './Service';

// main arguments
const MAIN_ARGS: yargs.Options = {
Expand Down Expand Up @@ -81,9 +82,26 @@ function createModules(botModule: BotModule, migrate: boolean) {
return modules;
}

async function handleSignals(bot: Bot) {
async function handleSignals(bot: Bot, logger: Logger) {
await bot.start();
await signal(SIGNAL_STOP);
await bot.notify(ServiceLifecycle.Start);

const signals = [SIGNAL_RELOAD, SIGNAL_RESET, SIGNAL_STOP];
let s = await signal(...signals);
while (s !== SIGNAL_STOP) {
switch (s) {
case SIGNAL_RELOAD:
bot.notify(ServiceLifecycle.Reload);
break;
case SIGNAL_RESET:
bot.notify(ServiceLifecycle.Reset);
break;
}
s = await signal(...signals);
}

logger.info('stop signal');
await bot.notify(ServiceLifecycle.Stop);
await bot.stop();
}

Expand Down Expand Up @@ -116,7 +134,7 @@ async function main(argv: Array<string>): Promise<number> {
botModule.setBot(bot);

logger.info('starting bot');
await handleSignals(bot);
await handleSignals(bot, logger);

return STATUS_SUCCESS;
}
Expand Down
8 changes: 7 additions & 1 deletion src/module/ServiceModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Container } from 'noicejs/Container';

import { BotServiceOptions } from 'src/BotService';
import { NotFoundError } from 'src/error/NotFoundError';
import { Service, ServiceDefinition, ServiceMetadata } from 'src/Service';
import { Service, ServiceDefinition, ServiceMetadata, ServiceLifecycle } from 'src/Service';
import { mustGet } from 'src/utils/Map';

/**
Expand All @@ -29,6 +29,12 @@ export class ServiceModule extends Module implements Service {
return this.services.size;
}

public async notify(event: ServiceLifecycle) {
for (const svc of this.services.values()) {
await svc.notify(event);
}
}

public async start() {
for (const svc of this.services.values()) {
await svc.start();
Expand Down
15 changes: 8 additions & 7 deletions src/utils/Signal.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
export const SIGNAL_RELOAD: Array<NodeJS.Signals> = ['SIGHUP'];
export const SIGNAL_STOP: Array<NodeJS.Signals> = ['SIGINT', 'SIGTERM'];
export const SIGNAL_RELOAD: NodeJS.Signals = 'SIGHUP';
export const SIGNAL_RESET: NodeJS.Signals = 'SIGINT';
export const SIGNAL_STOP: NodeJS.Signals = 'SIGTERM';

export function signal(signals: Array<NodeJS.Signals>): Promise<void> {
export function signal(...signals: Array<NodeJS.Signals>): Promise<NodeJS.Signals> {
return new Promise((res, _) => {
function handler() {
for (const sig of signals) {
process.removeListener(sig, handler);
function handler(fired: NodeJS.Signals) {
for (const signal of signals) {
process.removeListener(signal, handler);
}
res();
res(fired);
}

for (const sig of signals) {
Expand Down

0 comments on commit 2d4e650

Please sign in to comment.