-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
36538b1
commit e4820d5
Showing
9 changed files
with
419 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { RetryPolicyType } from "./../../retry/models/retry-policy-type"; | ||
import PolicyExecutorFactory from "../../@common/policy-executor-factory"; | ||
import IPolicyExecutor from "../../@common/policy-executor-interface"; | ||
import Result from "../../@common/result"; | ||
import { CircuitBreakerPolicyType } from "../models/circuit-breaker-policy-type"; | ||
import ICircuitBreakerStateManager from "../state/circuit-breaker-state-manager.interface"; | ||
import CircuitBreakerStateManager from "../state/ciruit-breaker-state-manager"; | ||
import { CircuitState } from "../models/circuit-state"; | ||
|
||
export default class CircuitBreakerPolicyExecutor implements IPolicyExecutor { | ||
private retryPolicyExecutor: IPolicyExecutor; | ||
|
||
private constructor( | ||
private circuitBreakerPolicy: CircuitBreakerPolicyType, | ||
private circuitBreakerStateManager: ICircuitBreakerStateManager | ||
) { | ||
const retryPolicyType: RetryPolicyType = { | ||
...this.circuitBreakerPolicy, | ||
maxNumberOfRetries: | ||
this.circuitBreakerPolicy.maxNumberOfRetriesBeforeCircuitIsOpen, | ||
}; | ||
this.retryPolicyExecutor = | ||
PolicyExecutorFactory.createRetryHttpExecutor(retryPolicyType); | ||
} | ||
|
||
async ExecutePolicyAsync<T>(httpRequest: Promise<any>): Promise<Result<T>> { | ||
if (this.circuitBreakerStateManager.isCurrentStateOpen()) { | ||
return Result.createCircuitOpenedErrorResult( | ||
this.circuitBreakerPolicy.circuitOpenDurationInSeconds | ||
); | ||
} | ||
|
||
const httpResult = await this.retryPolicyExecutor.ExecutePolicyAsync( | ||
httpRequest | ||
); | ||
|
||
if (httpResult.data) { | ||
if (this.circuitBreakerStateManager.isCurrentStateHalfOpen()) { | ||
this.circuitBreakerStateManager.moveStateToClosed(); | ||
} | ||
|
||
return Result.createSuccessHttpResult<T>(httpResult.data); | ||
} else { | ||
this.circuitBreakerStateManager.moveStateToOpen(); | ||
|
||
return Result.createCircuitOpenedErrorResult( | ||
this.circuitBreakerPolicy.circuitOpenDurationInSeconds | ||
); | ||
} | ||
} | ||
|
||
getCurrentCircuitState(): CircuitState { | ||
return this.circuitBreakerStateManager.getCurrentState(); | ||
} | ||
|
||
static createCircuitBreakerExecutor( | ||
circuitBreakerPolicy: CircuitBreakerPolicyType | ||
) { | ||
return new CircuitBreakerPolicyExecutor( | ||
circuitBreakerPolicy, | ||
new CircuitBreakerStateManager(circuitBreakerPolicy) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import BaseError from "../../@common/base-error"; | ||
|
||
export default class CircuitBreakerError extends BaseError { | ||
private constructor(message: string, reason = "circuitBreaker") { | ||
super(reason, message); | ||
} | ||
|
||
static createCircuitOpenError(circuitOpenDurationInSeconds: number) { | ||
return new CircuitBreakerError( | ||
`Your request could not be processed. The circuit has been opened for ${circuitOpenDurationInSeconds} seconds.` | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { RetryIntervalStrategy } from "../../retry/models/retry-interval-options"; | ||
|
||
export type CircuitBreakerPolicyType = { | ||
maxNumberOfRetriesBeforeCircuitIsOpen: number; | ||
retryIntervalStrategy?: RetryIntervalStrategy; | ||
baseRetryDelayInSeconds?: number; | ||
timeoutPerRetryInSeconds?: number; | ||
excludeRetriesOnStatusCodes?: number[]; | ||
circuitOpenDurationInSeconds: number; | ||
onOpen?: () => void; | ||
onHalfOpen?: () => void; | ||
onClose?: () => void; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export enum CircuitState { | ||
Opened, | ||
Half_Opened, | ||
Closed, | ||
} |
11 changes: 11 additions & 0 deletions
11
src/circuit-breaker/state/circuit-breaker-state-manager.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { CircuitState } from "../models/circuit-state"; | ||
|
||
export default interface ICircuitBreakerStateManager { | ||
getCurrentState(): CircuitState; | ||
isCurrentStateOpen(): boolean; | ||
isCurrentStateHalfOpen(): boolean; | ||
isCurrentStateClosed(): boolean; | ||
moveStateToClosed(): void; | ||
moveStateToHalfOpen(): void; | ||
moveStateToOpen(): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { CircuitBreakerPolicyType } from "../models/circuit-breaker-policy-type"; | ||
import { CircuitState } from "../models/circuit-state"; | ||
import ICircuitBreakerStateManager from "./circuit-breaker-state-manager.interface"; | ||
|
||
export default class CircuitBreakerStateManager | ||
implements ICircuitBreakerStateManager | ||
{ | ||
private currentState = CircuitState.Closed; | ||
private onOpen: () => void; | ||
private onClose: () => void; | ||
private onHalfOpen: () => void; | ||
|
||
constructor(private circuitBreakerPolicy: CircuitBreakerPolicyType) { | ||
this.onOpen = () => { | ||
var fn = | ||
this.circuitBreakerPolicy.onOpen ?? | ||
(() => console.log("Circuit is now open.")); | ||
fn(); | ||
setTimeout(() => { | ||
this.onHalfOpen(); | ||
}, this.circuitBreakerPolicy.circuitOpenDurationInSeconds * 1000); | ||
this.currentState = CircuitState.Opened; | ||
}; | ||
|
||
this.onClose = () => { | ||
var fn = | ||
this.circuitBreakerPolicy.onClose ?? | ||
(() => console.log("Circuit is now closed.")); | ||
fn(); | ||
this.currentState = CircuitState.Closed; | ||
}; | ||
|
||
this.onHalfOpen = () => { | ||
var fn = | ||
this.circuitBreakerPolicy.onHalfOpen ?? | ||
(() => console.log("Circuit is now half-open.")); | ||
fn(); | ||
this.currentState = CircuitState.Half_Opened; | ||
}; | ||
} | ||
|
||
getCurrentState(): CircuitState { | ||
return this.currentState; | ||
} | ||
|
||
isCurrentStateOpen(): boolean { | ||
return this.currentState === CircuitState.Opened; | ||
} | ||
|
||
isCurrentStateHalfOpen(): boolean { | ||
return this.currentState === CircuitState.Half_Opened; | ||
} | ||
|
||
isCurrentStateClosed(): boolean { | ||
return this.currentState === CircuitState.Closed; | ||
} | ||
|
||
moveStateToClosed(): void { | ||
this.onClose(); | ||
} | ||
|
||
moveStateToHalfOpen(): void { | ||
this.onHalfOpen(); | ||
} | ||
|
||
moveStateToOpen(): void { | ||
this.onOpen(); | ||
} | ||
} |
Oops, something went wrong.