UNPKG

acelga-bus

Version:

An extensible typescript message bus with support for middlewares

82 lines (67 loc) 2.55 kB
import {DecodedEvent, PullBasicClient} from '../corebus/interfaces'; import {BackoffExecutor, BackoffStopper} from '../corebus/backoff'; import {IEmptyTracker} from '../corebus/emptyTracker'; import {ErrorLogger} from '../index'; const NO_MESSAGES = Symbol('no messages'); // E: Event contract, SC: Subcription config (accepted as array for multiple subscriptions, DC: DecodedEvent subtype export class SynchronousClientProcessor<E, SC, DE>{ protected subscriptionsCancellers: BackoffStopper[] = []; protected messagesToGet = 100; constructor( protected client: PullBasicClient<SC>, protected onEvent: (events: DecodedEvent<DE>[]) => Promise<void>, protected logError: ErrorLogger, protected backoffStrategy: BackoffExecutor, protected eventstoreResponseDecoder: (response: any, subscriptionConfig: SC) => Array<DecodedEvent<DE>>, protected subscriptions: Array<SC>, protected tracker: IEmptyTracker, protected milisecondsToStop: number, ) { } public startConsumption() { this.tracker.remember('running'); this.subscriptions.forEach(config => this.declareConsumer(config)); } public stop(): Promise<void> { this.tracker.forget('running'); this.subscriptionsCancellers.forEach(canceller => canceller()); this.subscriptionsCancellers.length = 0; return this.tracker.waitUntilEmpty(this.milisecondsToStop) .catch(() => { this.logError(new TooLongToStop()); }); } private declareConsumer(config: SC): void { const backoffStopper = this.backoffStrategy(async (continuing, restarting, number, delay) => { const trackerId = number + Math.random(); this.tracker.remember(trackerId); try { const response = await this.client.getEvents(config); const events = this.eventstoreResponseDecoder(response, config); await this.processConsumedResponse(events); this.tracker.forget(trackerId); restarting(); } catch (error) { if (error === NO_MESSAGES){ this.tracker.forget(trackerId); return continuing(); } this.logError(error); this.tracker.forget(trackerId); restarting(); } }); this.subscriptionsCancellers.push(backoffStopper); } protected processConsumedResponse(events: Array<DecodedEvent<DE>>): Promise<void> { if (!Array.isArray(events) || events.length === 0) { return Promise.reject(NO_MESSAGES); } return this.onEvent(events); } } export class TooLongToStop extends Error { constructor() { super(); this.message = 'Stopping the server took too much time. Stopping anyway, events may still be processing'; } }