UNPKG

@sha1n/fungus

Version:

A dependency based service graph controller library

96 lines (95 loc) 3.98 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServiceController = void 0; const assert = require("assert"); const EventEmitter = require("events"); const logger_1 = require("./logger"); const logger = (0, logger_1.createLogger)('srv-ctrl'); class ServiceController extends EventEmitter { constructor(service) { super(); this.service = service; this.startPendingDependencies = new Set(); this.meta = undefined; this.start = (ctx) => __awaiter(this, void 0, void 0, function* () { if (this.isStarted()) { return; } return this.startPromise || (this.startPromise = this.doStart(ctx)); }); this.stop = (ctx) => __awaiter(this, void 0, void 0, function* () { logger.debug('%s: going to shutdown...', this.id); if (!this.isStarted() && !this.startPromise) { return; } try { if (this.startPromise) { logger.debug('%s: waiting for startup to finish...', this.id); yield this.startPromise; } logger.debug('%s: stopping...', this.id); yield this.service.stop(ctx); this.emit('stopped', this.service.id, ctx); this.meta = undefined; } catch (e) { this.emit('error', e); throw e; } finally { this.meta = undefined; } }); } get id() { return this.service.id; } addDependency(dependency) { this.startPendingDependencies.add(dependency.id); dependency.once('started', (metadata, ctx) => { // An event emitter should trigger a promise rejection up the stack this.onDependencyStarted(metadata, ctx).catch(logger.error); }); } doStart(ctx) { return __awaiter(this, void 0, void 0, function* () { try { this.meta = yield this.service.start(ctx); ctx.register(this.meta); this.emit('started', this.meta, ctx); } catch (e) { const hasListeners = this.emit('error', e); assert(hasListeners, 'A service controller is expected to have a listener at this point'); throw e; } finally { this.startPromise = undefined; } }); } onDependencyStarted(metadata, ctx) { return __awaiter(this, void 0, void 0, function* () { logger.debug('%s: dependency started -> %s', this.id, metadata.id); this.startPendingDependencies.delete(metadata.id); assert(!this.isStarted() && !this.startPromise, `Unexpected internal state. starting=${this.startPromise !== undefined}, started=${this.isStarted()}`); if (this.startPendingDependencies.size === 0 && !ctx.shuttingDown) { logger.debug('%s: all dependencies are started', this.id); yield this.start(ctx); } }); } isStarted() { return this.meta !== undefined; } } exports.ServiceController = ServiceController;