mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2026-10-06 07:47:04 +00:00
Merge pull request #31801 from mastermatt/opossum/add-stats-and-fix-symbols
[Opossum] Add Stats and fix symbols.
This commit is contained in:
Vendored
+164
-36
@@ -3,6 +3,7 @@
|
||||
// Definitions by: Quinn Langille <https://github.com/quinnlangille>
|
||||
// Willy Zhang <https://github.com/merufm>
|
||||
// Lance Ball <https://github.com/lance>
|
||||
// Matt R. Wilson <https://github.com/mastermatt>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
@@ -13,63 +14,190 @@ import { EventEmitter } from "events";
|
||||
export type Action = (...args: any[]) => any;
|
||||
|
||||
export class CircuitBreaker extends EventEmitter {
|
||||
constructor(action: Action, options: CircuitBreakerOptions);
|
||||
|
||||
readonly name: string;
|
||||
readonly group: string;
|
||||
readonly enabled: boolean;
|
||||
readonly pendingClose: boolean;
|
||||
readonly closed: boolean;
|
||||
readonly opened: boolean;
|
||||
readonly halfOpen: boolean;
|
||||
readonly status: Status;
|
||||
readonly stats: Stats;
|
||||
readonly hystrixStats: HystrixStats;
|
||||
readonly warmUp: boolean;
|
||||
readonly volumeThreshold: number;
|
||||
|
||||
clearCache(): void;
|
||||
open(): void;
|
||||
close(): void;
|
||||
disable(): void;
|
||||
enable(): void;
|
||||
fallback(func?: (...args: any[]) => any): this;
|
||||
fallback(func: Action | CircuitBreaker): this;
|
||||
fire(...args: any[]): Promise<any>;
|
||||
healthCheck(func: (...args: any[]) => Promise<any>, interval?: number): Promise<any>;
|
||||
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
open(): void;
|
||||
promisify(action: Action): Promise<Action>;
|
||||
stats(): stream.Transform;
|
||||
|
||||
static readonly name: symbol;
|
||||
static readonly group: symbol;
|
||||
static readonly pendingClose: symbol;
|
||||
static readonly closed: symbol;
|
||||
static readonly opened: symbol;
|
||||
static readonly halfOpen: symbol;
|
||||
static readonly status: symbol;
|
||||
static readonly hystrixStats: symbol;
|
||||
static readonly enabled: symbol;
|
||||
static readonly warmUp: symbol;
|
||||
static readonly volumeThreshold: symbol;
|
||||
healthCheck(
|
||||
func: (...args: any[]) => Promise<any>,
|
||||
interval?: number
|
||||
): void;
|
||||
}
|
||||
|
||||
export enum Event {
|
||||
cacheHit = 'cacheHit',
|
||||
cacheMiss = 'cacheMiss',
|
||||
close = 'close',
|
||||
failure = 'failure',
|
||||
fallback = 'fallback',
|
||||
fire = 'fire',
|
||||
halfOpen = 'halfOpen',
|
||||
healthCheckFailed = 'health-check-failed',
|
||||
open = 'open',
|
||||
reject = 'reject',
|
||||
semaphoreLocked = 'semaphore-locked',
|
||||
success = 'success',
|
||||
timeout = 'timeout'
|
||||
cacheHit = "cacheHit",
|
||||
cacheMiss = "cacheMiss",
|
||||
close = "close",
|
||||
failure = "failure",
|
||||
fallback = "fallback",
|
||||
fire = "fire",
|
||||
halfOpen = "halfOpen",
|
||||
healthCheckFailed = "health-check-failed",
|
||||
open = "open",
|
||||
reject = "reject",
|
||||
semaphoreLocked = "semaphore-locked",
|
||||
success = "success",
|
||||
timeout = "timeout"
|
||||
}
|
||||
|
||||
export interface CircuitBreakerOptions {
|
||||
timeout?: number;
|
||||
/**
|
||||
* The time in milliseconds that action should be allowed to execute before timing out.
|
||||
* Timeout can be disabled by setting this to `false`.
|
||||
*/
|
||||
timeout?: number | false;
|
||||
|
||||
/**
|
||||
* The number of times the circuit can fail before opening.
|
||||
* @deprecated see options.errorThresholdPercentage
|
||||
*/
|
||||
maxFailures?: number;
|
||||
|
||||
/**
|
||||
* The time in milliseconds to wait before setting the breaker to `halfOpen` state, and trying the action again.
|
||||
*/
|
||||
resetTimeout?: number;
|
||||
|
||||
/**
|
||||
* Sets the duration of the statistical rolling window, in milliseconds.
|
||||
* This is how long Opossum keeps metrics for the circuit breaker to use and for publishing.
|
||||
* @default 10000
|
||||
*/
|
||||
rollingCountTimeout?: number;
|
||||
|
||||
/**
|
||||
* Sets the number of buckets the rolling statistical window is divided into.
|
||||
* So, if options.rollingCountTimeout is 10,000, and options.rollingCountBuckets is 10, then the
|
||||
* statistical window will be 1,000 1 second snapshots in the statistical window.
|
||||
* @default 10
|
||||
*/
|
||||
rollingCountBuckets?: number;
|
||||
|
||||
/**
|
||||
* The circuit name to use when reporting stats.
|
||||
* Defaults to the name of the action function then falls back to a UUID
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* A grouping key for reporting.
|
||||
* Defaults to the computed value of options.name
|
||||
*/
|
||||
group?: string;
|
||||
|
||||
/**
|
||||
* This property indicates whether execution latencies should be tracked and calculated as percentiles.
|
||||
* If they are disabled, all summary statistics (mean, percentiles) are returned as -1.
|
||||
* @default true
|
||||
*/
|
||||
rollingPercentilesEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* The number of concurrent requests allowed.
|
||||
* If the number currently executing function calls is equal to options.capacity, further calls
|
||||
* to `fire()` are rejected until at least one of the current requests completes.
|
||||
* @default MAX_SAFE_INTEGER
|
||||
*/
|
||||
capacity?: number;
|
||||
|
||||
/**
|
||||
* The error percentage at which to open the circuit and start short-circuiting requests to fallback.
|
||||
*/
|
||||
errorThresholdPercentage?: number;
|
||||
|
||||
/**
|
||||
* Whether this circuit is enabled upon construction.
|
||||
* @default true
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Determines whether to allow failures without opening the circuit during a brief warmup period
|
||||
* This can help in situations where no matter what your `errorThresholdPercentage` is, if the
|
||||
* first execution times out or fails, the circuit immediately opens.
|
||||
* @default false
|
||||
*/
|
||||
allowWarmUp?: boolean;
|
||||
|
||||
/**
|
||||
* The minimum number of requests within the rolling statistical window that must exist before
|
||||
* the circuit breaker can open. This is similar to `allowWarmUp` in that no matter how many
|
||||
* failures there are, if the number of requests within the statistical window does not exceed
|
||||
* this threshold, the circuit will remain closed.
|
||||
* @default 0
|
||||
*/
|
||||
volumeThreshold?: number;
|
||||
|
||||
/**
|
||||
* If set to true, the value from the first call to `fire` will be cached an subsequent calls
|
||||
* will not execute the `action` function, but return the cached value instead.
|
||||
* @default false
|
||||
*/
|
||||
cache?: boolean;
|
||||
}
|
||||
|
||||
export default function circuitBreaker(
|
||||
action: Action,
|
||||
options?: CircuitBreakerOptions
|
||||
): CircuitBreaker;
|
||||
export interface Status extends EventEmitter {
|
||||
stats: Stats;
|
||||
window: Window;
|
||||
|
||||
increment(property: string, latencyRunTime?: number): void;
|
||||
open(): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
export interface Bucket {
|
||||
failures: number;
|
||||
fallbacks: number;
|
||||
successes: number;
|
||||
rejects: number;
|
||||
fires: number;
|
||||
timeouts: number;
|
||||
cacheHits: number;
|
||||
cacheMisses: number;
|
||||
semaphoreRejections: number;
|
||||
percentiles: { [percentile: number]: number };
|
||||
latencyTimes: number[];
|
||||
}
|
||||
|
||||
export type Window = Bucket[];
|
||||
|
||||
export interface Stats extends Bucket {
|
||||
latencyMean: number;
|
||||
}
|
||||
|
||||
export class HystrixStats {
|
||||
constructor(circuit: CircuitBreaker);
|
||||
|
||||
getHystrixStream(): stream.Transform;
|
||||
}
|
||||
|
||||
export function promisify(action: Action): (...args: any[]) => Promise<any>;
|
||||
export const stats: stream.Transform;
|
||||
|
||||
interface index {
|
||||
(action: Action, options: CircuitBreakerOptions): CircuitBreaker;
|
||||
|
||||
promisify: (action: Action) => (...args: any[]) => Promise<any>;
|
||||
stats: stream.Transform;
|
||||
}
|
||||
|
||||
export const circuitBreaker: index;
|
||||
export default circuitBreaker;
|
||||
|
||||
+104
-35
@@ -1,48 +1,117 @@
|
||||
import { Transform } from "stream";
|
||||
import circuitBreaker, { CircuitBreaker, CircuitBreakerOptions } from "opossum";
|
||||
import * as fs from "fs";
|
||||
import circuitBreaker, {
|
||||
CircuitBreaker,
|
||||
CircuitBreakerOptions,
|
||||
promisify,
|
||||
stats,
|
||||
Stats,
|
||||
Window
|
||||
} from "opossum";
|
||||
|
||||
const _blank = () => {};
|
||||
const async_blank = () => new Promise(resolve => null);
|
||||
let readFile = promisify(fs.readFile);
|
||||
stats.removeAllListeners();
|
||||
|
||||
const testNoOptions: CircuitBreaker = circuitBreaker(_blank);
|
||||
let breaker: CircuitBreaker;
|
||||
const callbackNoArgs = () => console.log("foo");
|
||||
|
||||
const options: CircuitBreakerOptions = {
|
||||
timeout: 1,
|
||||
maxFailures: 1,
|
||||
resetTimeout: 1,
|
||||
rollingCountTimeout: 1,
|
||||
rollingCountBuckets: 1,
|
||||
name: "testing",
|
||||
breaker = circuitBreaker(() => true, {
|
||||
timeout: false,
|
||||
maxFailures: 50,
|
||||
resetTimeout: 10,
|
||||
rollingCountTimeout: 500,
|
||||
rollingCountBuckets: 20,
|
||||
name: "test",
|
||||
group: "group",
|
||||
rollingPercentilesEnabled: true,
|
||||
capacity: 1,
|
||||
errorThresholdPercentage: 1,
|
||||
enabled: true,
|
||||
allowWarmUp: true,
|
||||
volumeThreshold: 1
|
||||
volumeThreshold: 1,
|
||||
cache: true
|
||||
});
|
||||
|
||||
breaker.name; // $ExpectType string
|
||||
breaker.group; // $ExpectType string
|
||||
breaker.enabled; // $ExpectType boolean
|
||||
breaker.pendingClose; // $ExpectType boolean
|
||||
breaker.closed; // $ExpectType boolean
|
||||
breaker.opened; // $ExpectType boolean
|
||||
breaker.halfOpen; // $ExpectType boolean
|
||||
breaker.warmUp; // $ExpectType boolean
|
||||
breaker.volumeThreshold; // $ExpectType number
|
||||
breaker.status.stats.latencyMean; // $ExpectType number
|
||||
breaker.stats.latencyTimes; // $ExpectType number[]
|
||||
breaker.hystrixStats.getHystrixStream().removeAllListeners();
|
||||
|
||||
breaker.clearCache(); // $ExpectType void
|
||||
breaker.open(); // $ExpectType void
|
||||
breaker.close(); // $ExpectType void
|
||||
breaker.disable(); // $ExpectType void
|
||||
breaker.enable(); // $ExpectType void
|
||||
|
||||
// The following are examples are from the libs README and official documentation
|
||||
// https://nodeshift.github.io/opossum/index.html.
|
||||
|
||||
function asyncFunctionThatCouldFail(x: any, y: any) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Do something, maybe on the network or a disk
|
||||
resolve([x, y]);
|
||||
});
|
||||
}
|
||||
|
||||
const options: CircuitBreakerOptions = {
|
||||
timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
|
||||
errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
|
||||
resetTimeout: 30000 // After 30 seconds, try again.
|
||||
};
|
||||
breaker = circuitBreaker(asyncFunctionThatCouldFail, options);
|
||||
|
||||
const testWithOptions: CircuitBreaker = circuitBreaker(_blank, options);
|
||||
const shouldBeAPromise: Promise<any> = testWithOptions.promisify(_blank);
|
||||
const shouldBeATransformStream: Transform = testWithOptions.stats();
|
||||
breaker
|
||||
.fire("foo")
|
||||
.then(console.log)
|
||||
.catch(console.error);
|
||||
|
||||
const healthCheck: Promise<any> = testWithOptions.healthCheck(async_blank, 8);
|
||||
const shouldBeACircuitBreaker: CircuitBreaker = testWithOptions.fallback();
|
||||
const shouldBeAPromiseGeneric: Promise<any> = testWithOptions.fire();
|
||||
breaker = circuitBreaker(asyncFunctionThatCouldFail, options);
|
||||
// if asyncFunctionThatCouldFail starts to fail, firing the breaker
|
||||
// will trigger our fallback function
|
||||
breaker.fallback(() => "Sorry, out of service right now");
|
||||
breaker.on("fallback", result => console.log(result));
|
||||
|
||||
const shouldBeVoid = (): void => testWithOptions.enable();
|
||||
const shouldBeVoid2 = (): void => testWithOptions.disable();
|
||||
const shouldBeVoid3 = (): void => testWithOptions.close();
|
||||
const shouldBeVoid4 = (): void => testWithOptions.open();
|
||||
const shouldBeVoid5 = (): void => testWithOptions.clearCache();
|
||||
breaker = circuitBreaker(callbackNoArgs, options);
|
||||
|
||||
const shouldBeSymbol: symbol = CircuitBreaker.name;
|
||||
const shouldBeSymbol2: symbol = CircuitBreaker.group;
|
||||
const shouldBeSymbol3: symbol = CircuitBreaker.pendingClose;
|
||||
const shouldBeSymbol4: symbol = CircuitBreaker.closed;
|
||||
const shouldBeSymbol5: symbol = CircuitBreaker.opened;
|
||||
const shouldBeSymbol6: symbol = CircuitBreaker.halfOpen;
|
||||
const shouldBeSymbol7: symbol = CircuitBreaker.hystrixStats;
|
||||
const shouldBeSymbol8: symbol = CircuitBreaker.status;
|
||||
const shouldBeSymbol9: symbol = CircuitBreaker.enabled;
|
||||
const shouldBeSymbol10: symbol = CircuitBreaker.warmUp;
|
||||
const shouldBeSymbol11: symbol = CircuitBreaker.volumeThreshold;
|
||||
breaker.fallback(callbackNoArgs);
|
||||
|
||||
breaker.on("success", result => console.log(result));
|
||||
breaker.on("timeout", callbackNoArgs);
|
||||
breaker.on("reject", callbackNoArgs);
|
||||
breaker.on("open", callbackNoArgs);
|
||||
breaker.on("halfOpen", callbackNoArgs);
|
||||
breaker.on("close", callbackNoArgs);
|
||||
breaker.on("fallback", data => console.log(data));
|
||||
|
||||
readFile = circuitBreaker.promisify(fs.readFile);
|
||||
breaker = circuitBreaker(readFile, options);
|
||||
|
||||
breaker
|
||||
.fire("./package.json", "utf-8")
|
||||
.then(console.log)
|
||||
.catch(console.error);
|
||||
|
||||
breaker = circuitBreaker(fs.readFile, {});
|
||||
|
||||
breaker.hystrixStats.getHystrixStream().pipe(process.stdout);
|
||||
|
||||
// Creates a 1 second window consisting of ten time slices,
|
||||
// each 100ms long.
|
||||
const circuit = circuitBreaker(fs.readFile, {
|
||||
rollingCountBuckets: 10,
|
||||
rollingCountTimeout: 1000
|
||||
});
|
||||
|
||||
// get the cumulative statistics for the last second
|
||||
const theStats: Stats = breaker.status.stats;
|
||||
|
||||
// get the array of 10, 1 second time slices for the last second
|
||||
const window: Window = breaker.status.window;
|
||||
window[0].fires; // $ExpectType number
|
||||
|
||||
Reference in New Issue
Block a user