UNPKG

@daiso-tech/core

Version:

The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.

201 lines 6.99 kB
/** * @module CircuitBreaker */ import { OpenCircuitBreakerError, IsolatedCircuitBreakerError, CIRCUIT_BREAKER_TRIGGER, CIRCUIT_BREAKER_STATE, CIRCUIT_BREAKER_EVENTS, } from "../../../../circuit-breaker/contracts/_module.js"; import {} from "../../../../event-bus/contracts/_module.js"; import {} from "../../../../namespace/_module.js"; import {} from "../../../../task/contracts/_module.js"; import { Task } from "../../../../task/implementations/_module.js"; import { TimeSpan } from "../../../../time-span/implementations/_module.js"; import { callErrorPolicyOnThrow, resolveAsyncLazyable, } from "../../../../utilities/_module.js"; /** * @internal */ export class CircuitBreaker { /** * @internal */ static _internal_serialize(deserializedValue) { return { version: "1", key: deserializedValue._key.get(), }; } _key; errorPolicy; trigger; slowCallTime; adapter; eventDispatcher; serdeTransformerName; namespace; enableAsyncTracking; constructor(settings) { const { enableAsyncTracking, eventDispatcher, key, errorPolicy, trigger, adapter, slowCallTime, serdeTransformerName, namespace, } = settings; this.enableAsyncTracking = enableAsyncTracking; this.eventDispatcher = eventDispatcher; this._key = key; this.errorPolicy = errorPolicy; this.trigger = trigger; this.adapter = adapter; this.slowCallTime = slowCallTime; this.serdeTransformerName = serdeTransformerName; this.namespace = namespace; } _internal_getNamespace() { return this.namespace; } _internal_getSerdeTransformerName() { return this.serdeTransformerName; } _internal_getAdapter() { return this.adapter; } get key() { return this._key.get(); } getState() { return new Task(async () => { return this.adapter.getState(this._key.toString()); }); } async trackFailure() { if (this.enableAsyncTracking) { new Task(async () => { await this.adapter.trackFailure(this._key.toString()); }).detach(); return; } await this.adapter.trackFailure(this._key.toString()); } async trackSuccess() { if (this.enableAsyncTracking) { new Task(async () => { await this.adapter.trackSuccess(this._key.toString()); }).detach(); return; } await this.adapter.trackSuccess(this._key.toString()); } async trackErrorWrapper(fn) { try { return await fn(); } catch (error) { const isErrorMatching = await callErrorPolicyOnThrow(this.errorPolicy, error); const shouldRecordError = this.trigger === CIRCUIT_BREAKER_TRIGGER.BOTH || this.trigger === CIRCUIT_BREAKER_TRIGGER.ONLY_ERROR; if (shouldRecordError && isErrorMatching) { if (this.enableAsyncTracking) { new Task(async () => { await this.trackFailure(); }).detach(); } else { await this.trackFailure(); } this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.TRACKED_FAILURE, { circuitBreaker: this, error, }) .detach(); } else if (shouldRecordError) { this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.UNTRACKED_FAILURE, { circuitBreaker: this, error, }) .detach(); } throw error; } } async trackSlowCallWrapper(fn) { const start = performance.now(); const value = await fn(); const end = performance.now(); const executionTime = TimeSpan.fromMilliseconds(end - start); const shouldRecordSlowCall = this.trigger === CIRCUIT_BREAKER_TRIGGER.BOTH || this.trigger === CIRCUIT_BREAKER_TRIGGER.ONLY_SLOW_CALL; const isCallSlow = executionTime.gte(this.slowCallTime); if (shouldRecordSlowCall && isCallSlow) { await this.trackFailure(); this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.TRACKED_SLOW_CALL, { circuitBreaker: this, }) .detach(); } if (shouldRecordSlowCall && !isCallSlow) { await this.trackSuccess(); this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.TRACKED_SUCCESS, { circuitBreaker: this, }) .detach(); } if (!shouldRecordSlowCall) { await this.trackSuccess(); this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.TRACKED_SUCCESS, { circuitBreaker: this, }) .detach(); } return value; } async guard() { const transition = await this.adapter.updateState(this._key.toString()); const hasStateChaned = transition.to !== transition.from; if (hasStateChaned) { this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.STATE_TRANSITIONED, { circuitBreaker: this, from: transition.from, to: transition.to, }) .detach(); } const isInOpenState = transition.to === CIRCUIT_BREAKER_STATE.OPEN; if (isInOpenState) { throw OpenCircuitBreakerError.create(this._key); } const isIsolatedState = transition.to === CIRCUIT_BREAKER_EVENTS.ISOLATED; if (isIsolatedState) { throw IsolatedCircuitBreakerError.create(this._key); } } runOrFail(asyncFn) { return new Task(async () => { await this.guard(); return await this.trackErrorWrapper(async () => { return await this.trackSlowCallWrapper(async () => { return await resolveAsyncLazyable(asyncFn); }); }); }); } reset() { return new Task(async () => { await this.adapter.reset(this._key.toString()); this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.RESETED, { circuitBreaker: this, }) .detach(); }); } isolate() { return new Task(async () => { await this.adapter.isolate(this.key); this.eventDispatcher .dispatch(CIRCUIT_BREAKER_EVENTS.ISOLATED, { circuitBreaker: this, }) .detach(); }); } } //# sourceMappingURL=circuit-breaker.js.map