adaptive-concurrency 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Limit.d.ts +29 -0
- package/dist/Limit.d.ts.map +1 -0
- package/dist/Limit.js +1 -0
- package/dist/LimitAllotment.d.ts +23 -0
- package/dist/LimitAllotment.d.ts.map +1 -0
- package/dist/LimitAllotment.js +1 -0
- package/dist/Limiter.d.ts +175 -0
- package/dist/Limiter.d.ts.map +1 -0
- package/dist/Limiter.js +240 -0
- package/dist/Listener.d.ts +23 -0
- package/dist/Listener.d.ts.map +1 -0
- package/dist/Listener.js +1 -0
- package/dist/ListenerSet.d.ts +12 -0
- package/dist/ListenerSet.d.ts.map +1 -0
- package/dist/ListenerSet.js +35 -0
- package/dist/MetricIds.d.ts +13 -0
- package/dist/MetricIds.d.ts.map +1 -0
- package/dist/MetricIds.js +12 -0
- package/dist/MetricRegistry.d.ts +66 -0
- package/dist/MetricRegistry.d.ts.map +1 -0
- package/dist/MetricRegistry.js +30 -0
- package/dist/RunResult.d.ts +33 -0
- package/dist/RunResult.d.ts.map +1 -0
- package/dist/RunResult.js +35 -0
- package/dist/StreamingLimit.d.ts +26 -0
- package/dist/StreamingLimit.d.ts.map +1 -0
- package/dist/StreamingLimit.js +1 -0
- package/dist/executors/AdaptiveExecutor.d.ts +50 -0
- package/dist/executors/AdaptiveExecutor.d.ts.map +1 -0
- package/dist/executors/AdaptiveExecutor.js +80 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/limit/AIMDLimit.d.ts +37 -0
- package/dist/limit/AIMDLimit.d.ts.map +1 -0
- package/dist/limit/AIMDLimit.js +49 -0
- package/dist/limit/FixedLimit.d.ts +15 -0
- package/dist/limit/FixedLimit.d.ts.map +1 -0
- package/dist/limit/FixedLimit.js +23 -0
- package/dist/limit/Gradient2Limit.d.ts +122 -0
- package/dist/limit/Gradient2Limit.d.ts.map +1 -0
- package/dist/limit/Gradient2Limit.js +107 -0
- package/dist/limit/GradientLimit.d.ts +122 -0
- package/dist/limit/GradientLimit.d.ts.map +1 -0
- package/dist/limit/GradientLimit.js +108 -0
- package/dist/limit/SettableLimit.d.ts +18 -0
- package/dist/limit/SettableLimit.d.ts.map +1 -0
- package/dist/limit/SettableLimit.js +30 -0
- package/dist/limit/StreamingLimit.d.ts +26 -0
- package/dist/limit/StreamingLimit.d.ts.map +1 -0
- package/dist/limit/StreamingLimit.js +1 -0
- package/dist/limit/TracingLimitDecorator.d.ts +16 -0
- package/dist/limit/TracingLimitDecorator.d.ts.map +1 -0
- package/dist/limit/TracingLimitDecorator.js +23 -0
- package/dist/limit/VegasLimit.d.ts +85 -0
- package/dist/limit/VegasLimit.d.ts.map +1 -0
- package/dist/limit/VegasLimit.js +127 -0
- package/dist/limit/WindowedLimit.d.ts +48 -0
- package/dist/limit/WindowedLimit.d.ts.map +1 -0
- package/dist/limit/WindowedLimit.js +67 -0
- package/dist/limit/statistics/ExpMovingAverage.d.ts +21 -0
- package/dist/limit/statistics/ExpMovingAverage.d.ts.map +1 -0
- package/dist/limit/statistics/ExpMovingAverage.js +43 -0
- package/dist/limit/statistics/Minimum.d.ts +12 -0
- package/dist/limit/statistics/Minimum.d.ts.map +1 -0
- package/dist/limit/statistics/Minimum.js +22 -0
- package/dist/limit/statistics/MinimumValue.d.ts +12 -0
- package/dist/limit/statistics/MinimumValue.d.ts.map +1 -0
- package/dist/limit/statistics/MinimumValue.js +22 -0
- package/dist/limit/statistics/SingleMeasurement.d.ts +12 -0
- package/dist/limit/statistics/SingleMeasurement.d.ts.map +1 -0
- package/dist/limit/statistics/SingleMeasurement.js +21 -0
- package/dist/limit/statistics/StreamingStatistic.d.ts +29 -0
- package/dist/limit/statistics/StreamingStatistic.d.ts.map +1 -0
- package/dist/limit/statistics/StreamingStatistic.js +1 -0
- package/dist/limit/utils/index.d.ts +10 -0
- package/dist/limit/utils/index.d.ts.map +1 -0
- package/dist/limit/utils/index.js +19 -0
- package/dist/limit/window/AverageSampleWindow.d.ts +4 -0
- package/dist/limit/window/AverageSampleWindow.d.ts.map +1 -0
- package/dist/limit/window/AverageSampleWindow.js +46 -0
- package/dist/limit/window/PercentileSampleWindow.d.ts +38 -0
- package/dist/limit/window/PercentileSampleWindow.d.ts.map +1 -0
- package/dist/limit/window/PercentileSampleWindow.js +81 -0
- package/dist/limit/window/SampleWindow.d.ts +30 -0
- package/dist/limit/window/SampleWindow.d.ts.map +1 -0
- package/dist/limit/window/SampleWindow.js +1 -0
- package/dist/limiter/AbstractLimiter.d.ts +48 -0
- package/dist/limiter/AbstractLimiter.d.ts.map +1 -0
- package/dist/limiter/AbstractLimiter.js +78 -0
- package/dist/limiter/AbstractPartitionedLimiter.d.ts +66 -0
- package/dist/limiter/AbstractPartitionedLimiter.d.ts.map +1 -0
- package/dist/limiter/AbstractPartitionedLimiter.js +209 -0
- package/dist/limiter/BlockingLimiter.d.ts +55 -0
- package/dist/limiter/BlockingLimiter.d.ts.map +1 -0
- package/dist/limiter/BlockingLimiter.js +111 -0
- package/dist/limiter/DelayedRejectStrategy.d.ts +32 -0
- package/dist/limiter/DelayedRejectStrategy.d.ts.map +1 -0
- package/dist/limiter/DelayedRejectStrategy.js +60 -0
- package/dist/limiter/DelayedThenBlockingRejection.d.ts +19 -0
- package/dist/limiter/DelayedThenBlockingRejection.d.ts.map +1 -0
- package/dist/limiter/DelayedThenBlockingRejection.js +26 -0
- package/dist/limiter/FifoBlockingRejection.d.ts +26 -0
- package/dist/limiter/FifoBlockingRejection.d.ts.map +1 -0
- package/dist/limiter/FifoBlockingRejection.js +77 -0
- package/dist/limiter/LifoBlockingLimiter.d.ts +53 -0
- package/dist/limiter/LifoBlockingLimiter.d.ts.map +1 -0
- package/dist/limiter/LifoBlockingLimiter.js +108 -0
- package/dist/limiter/LifoBlockingRejection.d.ts +31 -0
- package/dist/limiter/LifoBlockingRejection.d.ts.map +1 -0
- package/dist/limiter/LifoBlockingRejection.js +63 -0
- package/dist/limiter/PartitionedStrategy.d.ts +90 -0
- package/dist/limiter/PartitionedStrategy.d.ts.map +1 -0
- package/dist/limiter/PartitionedStrategy.js +183 -0
- package/dist/limiter/SimpleLimiter.d.ts +31 -0
- package/dist/limiter/SimpleLimiter.d.ts.map +1 -0
- package/dist/limiter/SimpleLimiter.js +119 -0
- package/dist/limiter/factories/index.d.ts +7 -0
- package/dist/limiter/factories/index.d.ts.map +1 -0
- package/dist/limiter/factories/index.js +6 -0
- package/dist/limiter/factories/makeBlockingLimiter.d.ts +6 -0
- package/dist/limiter/factories/makeBlockingLimiter.d.ts.map +1 -0
- package/dist/limiter/factories/makeBlockingLimiter.js +8 -0
- package/dist/limiter/factories/makeLifoBlockingLimiter.d.ts +8 -0
- package/dist/limiter/factories/makeLifoBlockingLimiter.d.ts.map +1 -0
- package/dist/limiter/factories/makeLifoBlockingLimiter.js +15 -0
- package/dist/limiter/factories/makePartitionedBlockingLimiter.d.ts +12 -0
- package/dist/limiter/factories/makePartitionedBlockingLimiter.d.ts.map +1 -0
- package/dist/limiter/factories/makePartitionedBlockingLimiter.js +35 -0
- package/dist/limiter/factories/makePartitionedLifoBlockingLimiter.d.ts +14 -0
- package/dist/limiter/factories/makePartitionedLifoBlockingLimiter.d.ts.map +1 -0
- package/dist/limiter/factories/makePartitionedLifoBlockingLimiter.js +38 -0
- package/dist/limiter/factories/makePartitionedLimiter.d.ts +11 -0
- package/dist/limiter/factories/makePartitionedLimiter.d.ts.map +1 -0
- package/dist/limiter/factories/makePartitionedLimiter.js +30 -0
- package/dist/limiter/factories/makeSimpleLimiter.d.ts +3 -0
- package/dist/limiter/factories/makeSimpleLimiter.d.ts.map +1 -0
- package/dist/limiter/factories/makeSimpleLimiter.js +9 -0
- package/dist/limiter/factories.d.ts +31 -0
- package/dist/limiter/factories.d.ts.map +1 -0
- package/dist/limiter/factories.js +74 -0
- package/dist/statistics/ExpMovingAverage.d.ts +21 -0
- package/dist/statistics/ExpMovingAverage.d.ts.map +1 -0
- package/dist/statistics/ExpMovingAverage.js +43 -0
- package/dist/statistics/MinimumValue.d.ts +12 -0
- package/dist/statistics/MinimumValue.d.ts.map +1 -0
- package/dist/statistics/MinimumValue.js +22 -0
- package/dist/statistics/MostRecentValue.d.ts +12 -0
- package/dist/statistics/MostRecentValue.d.ts.map +1 -0
- package/dist/statistics/MostRecentValue.js +21 -0
- package/dist/statistics/StreamingStatistic.d.ts +29 -0
- package/dist/statistics/StreamingStatistic.d.ts.map +1 -0
- package/dist/statistics/StreamingStatistic.js +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +19 -0
- package/package.json +31 -0
package/dist/Limit.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract for an algorithm that calculates a concurrency limit based on
|
|
3
|
+
* rtt measurements.
|
|
4
|
+
*/
|
|
5
|
+
export interface Limit {
|
|
6
|
+
/**
|
|
7
|
+
* @returns Current estimated limit
|
|
8
|
+
*/
|
|
9
|
+
getLimit(): number;
|
|
10
|
+
/**
|
|
11
|
+
* Register a callback to receive notification whenever the limit is updated
|
|
12
|
+
* to a new value.
|
|
13
|
+
*
|
|
14
|
+
* Returns a function to unsubscribe. Optional AbortSignal support is
|
|
15
|
+
* provided for ergonomic cancellation.
|
|
16
|
+
*/
|
|
17
|
+
notifyOnChange(consumer: (newLimit: number) => void, options?: {
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}): () => void;
|
|
20
|
+
/**
|
|
21
|
+
* Adjust the estimated limit using a completed request sample.
|
|
22
|
+
* @param startTime Start time in fractional milliseconds (from performance.now())
|
|
23
|
+
* @param rtt Round trip time in fractional milliseconds
|
|
24
|
+
* @param inflight Number of inflight requests at the time the request started
|
|
25
|
+
* @param didDrop Whether the request was dropped (timeout or rejection)
|
|
26
|
+
*/
|
|
27
|
+
adjustForSample(startTime: number, rtt: number, inflight: number, didDrop: boolean): void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=Limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Limit.d.ts","sourceRoot":"","sources":["../src/Limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,QAAQ,IAAI,MAAM,CAAC;IAEnB;;;;;;OAMG;IACH,cAAc,CACZ,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACpC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,MAAM,IAAI,CAAC;IAEd;;;;;;OAMG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3F"}
|
package/dist/Limit.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle returned when a concurrency slot is acquired. The caller must invoke
|
|
3
|
+
* exactly one of the report methods when the operation completes.
|
|
4
|
+
*/
|
|
5
|
+
export interface LimitAllotment {
|
|
6
|
+
/**
|
|
7
|
+
* The operation succeeded and internally measured latency should be used as
|
|
8
|
+
* an RTT sample.
|
|
9
|
+
*/
|
|
10
|
+
reportSuccess(): void;
|
|
11
|
+
/**
|
|
12
|
+
* The operation failed before any meaningful RTT measurement could be made
|
|
13
|
+
* and should be ignored so it does not introduce an artificially low RTT.
|
|
14
|
+
*/
|
|
15
|
+
reportIgnore(): void;
|
|
16
|
+
/**
|
|
17
|
+
* The request failed and was dropped due to being rejected by an external
|
|
18
|
+
* limit or hitting a timeout. Loss based StreamingLimit implementations will
|
|
19
|
+
* likely do an aggressive reduction in limit when this happens.
|
|
20
|
+
*/
|
|
21
|
+
reportDropped(): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=LimitAllotment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LimitAllotment.d.ts","sourceRoot":"","sources":["../src/LimitAllotment.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,aAAa,IAAI,IAAI,CAAC;IAEtB;;;OAGG;IACH,YAAY,IAAI,IAAI,CAAC;IAErB;;;;OAIG;IACH,aAAa,IAAI,IAAI,CAAC;CACvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { AdaptiveLimit } from "./limit/StreamingLimit.js";
|
|
2
|
+
import type { LimitAllotment } from "./LimitAllotment.js";
|
|
3
|
+
import type { MetricRegistry } from "./MetricRegistry.js";
|
|
4
|
+
import { QuotaNotAvailable, type RunResult } from "./RunResult.js";
|
|
5
|
+
export type SyncAcquireResult = LimitAllotment | undefined;
|
|
6
|
+
export type AsyncAcquireResult = Promise<LimitAllotment | undefined>;
|
|
7
|
+
/**
|
|
8
|
+
* Union of all possible acquire return types. Useful for code that must handle
|
|
9
|
+
* both sync and async limiters generically.
|
|
10
|
+
*/
|
|
11
|
+
export type AcquireResult = SyncAcquireResult | AsyncAcquireResult;
|
|
12
|
+
export interface AcquireOptions<ContextT = void> {
|
|
13
|
+
context?: ContextT;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Read-only view of the limiter's current state, provided to strategies so
|
|
18
|
+
* they can make gating decisions without coupling to the Limiter class.
|
|
19
|
+
*/
|
|
20
|
+
export interface LimiterState {
|
|
21
|
+
readonly limit: number;
|
|
22
|
+
readonly inflight: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Decides whether a non-bypassed caller may take a concurrency allotment and
|
|
26
|
+
* updates strategy bookkeeping when they may (e.g. permits counter or
|
|
27
|
+
* per-partition tracking).
|
|
28
|
+
*/
|
|
29
|
+
export interface AcquireStrategy<ContextT> {
|
|
30
|
+
/**
|
|
31
|
+
* Tries to acquire an allotment for this request under the strategy's rules.
|
|
32
|
+
* When `true`, the strategy has reserved capacity for one inflight unit; when
|
|
33
|
+
* `false`, an allotment was not available.
|
|
34
|
+
*/
|
|
35
|
+
tryAcquireAllotment(context: ContextT, state: LimiterState): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Called when an acquired allotment completes (success, ignore, or drop).
|
|
38
|
+
* Perform any cleanup (e.g. increment permits, release a partition slot).
|
|
39
|
+
*/
|
|
40
|
+
onAllotmentReleased(context: ContextT): void;
|
|
41
|
+
/**
|
|
42
|
+
* Called when the adaptive limit changes. The strategy can react
|
|
43
|
+
* (e.g. adjust available permits by the delta, update partition sub-limits).
|
|
44
|
+
*/
|
|
45
|
+
onLimitChanged?(oldLimit: number, newLimit: number): void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Determines what happens when a request is rejected by the
|
|
49
|
+
* {@link AcquireStrategy}. The type parameter `ResultT` flows through to
|
|
50
|
+
* {@link Limiter.acquire}'s return type, enabling the type system to
|
|
51
|
+
* distinguish sync limiters (no promise) from async/blocking ones.
|
|
52
|
+
*/
|
|
53
|
+
export interface AllotmentUnavailableStrategy<ContextT, ResultT extends SyncAcquireResult | AsyncAcquireResult> {
|
|
54
|
+
/**
|
|
55
|
+
* Called when the acquire strategy cannot allocate an allotment for a
|
|
56
|
+
* request.
|
|
57
|
+
*
|
|
58
|
+
* @param context The request context.
|
|
59
|
+
* @param retry Callback to re-attempt acquisition. The rejection strategy can
|
|
60
|
+
* call this after a waiter is woken to try again. The retry runs
|
|
61
|
+
* {@link AcquireStrategy.tryAcquireAllotment} and, if successful, returns a
|
|
62
|
+
* ready-to-use allotment with all required wrapping.
|
|
63
|
+
* @param signal Optional abort signal from the caller.
|
|
64
|
+
*/
|
|
65
|
+
onAllotmentUnavailable(context: ContextT, retry: (context: ContextT) => SyncAcquireResult, signal?: AbortSignal): ResultT;
|
|
66
|
+
/**
|
|
67
|
+
* Called whenever any allotment is released (success, ignore, or drop).
|
|
68
|
+
* Blocking strategies use this to wake queued waiters.
|
|
69
|
+
*/
|
|
70
|
+
onAllotmentReleased(): void;
|
|
71
|
+
}
|
|
72
|
+
export interface LimiterOptions<ContextT, RejResultT extends SyncAcquireResult | AsyncAcquireResult = SyncAcquireResult> {
|
|
73
|
+
limit?: AdaptiveLimit;
|
|
74
|
+
/**
|
|
75
|
+
* Clock function returning the current time in fractional milliseconds
|
|
76
|
+
* (like performance.now()). Default: performance.now
|
|
77
|
+
*/
|
|
78
|
+
clock?: () => number;
|
|
79
|
+
name?: string;
|
|
80
|
+
metricRegistry?: MetricRegistry;
|
|
81
|
+
/**
|
|
82
|
+
* Predicate that, when returning true for a context, causes the request to
|
|
83
|
+
* bypass the limiter entirely. The request won't affect inflight count or
|
|
84
|
+
* the limit algorithm.
|
|
85
|
+
*/
|
|
86
|
+
bypassResolver?: (context: ContextT) => boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Strategy that decides whether a request gets a concurrency slot.
|
|
89
|
+
* Default: SemaphoreStrategy.
|
|
90
|
+
*/
|
|
91
|
+
acquireStrategy?: AcquireStrategy<ContextT>;
|
|
92
|
+
/**
|
|
93
|
+
* Strategy that decides what happens when the acquire strategy rejects a
|
|
94
|
+
* request. When omitted, rejected requests immediately receive `undefined`.
|
|
95
|
+
*/
|
|
96
|
+
allotmentUnavailableStrategy?: AllotmentUnavailableStrategy<ContextT, RejResultT>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Concurrency limiter with pluggable strategies for gating decisions and
|
|
100
|
+
* rejection handling.
|
|
101
|
+
*
|
|
102
|
+
* When no rejection strategy is provided, `acquire()` returns synchronously
|
|
103
|
+
* (`LimitAllotment | undefined`). When a blocking rejection strategy is
|
|
104
|
+
* configured, `acquire()` may also return a `Promise`.
|
|
105
|
+
*
|
|
106
|
+
* @typeParam ContextT Request context type (e.g. partition key).
|
|
107
|
+
* @typeParam RejResultT The result type produced by the rejection strategy.
|
|
108
|
+
*/
|
|
109
|
+
export declare class Limiter<Context = void, AcquireResult extends SyncAcquireResult | AsyncAcquireResult = SyncAcquireResult> {
|
|
110
|
+
private _inflight;
|
|
111
|
+
private _limit;
|
|
112
|
+
private readonly clock;
|
|
113
|
+
private readonly limitAlgorithm;
|
|
114
|
+
private readonly acquireStrategy;
|
|
115
|
+
private readonly rejectionStrategy;
|
|
116
|
+
private readonly bypassResolver;
|
|
117
|
+
private readonly successCounter;
|
|
118
|
+
private readonly droppedCounter;
|
|
119
|
+
private readonly ignoredCounter;
|
|
120
|
+
private readonly rejectedCounter;
|
|
121
|
+
private readonly bypassCounter;
|
|
122
|
+
static makeDefaultLimit(): AdaptiveLimit;
|
|
123
|
+
constructor(options?: LimiterOptions<Context, AcquireResult>);
|
|
124
|
+
acquire(options?: AcquireOptions<Context>): SyncAcquireResult | AcquireResult;
|
|
125
|
+
private tryAcquireCore;
|
|
126
|
+
private createAllotment;
|
|
127
|
+
getLimit(): number;
|
|
128
|
+
getInflight(): number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Simple semaphore-based acquire strategy. Tracks a permits counter that is
|
|
132
|
+
* decremented when an allotment is taken and incremented on release. When
|
|
133
|
+
* permits reach zero, {@link tryAcquireAllotment} returns false.
|
|
134
|
+
*/
|
|
135
|
+
export declare class SemaphoreStrategy {
|
|
136
|
+
private permits;
|
|
137
|
+
constructor(initialLimit: number);
|
|
138
|
+
tryAcquireAllotment(): boolean;
|
|
139
|
+
onAllotmentReleased(): void;
|
|
140
|
+
onLimitChanged(oldLimit: number, newLimit: number): void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Run a callback when an acquire result is ready. If the result is a Promise,
|
|
144
|
+
* waits asynchronously; otherwise invokes the callback synchronously.
|
|
145
|
+
*/
|
|
146
|
+
export declare function whenAcquireSettled(result: AcquireResult, callback: (allotment: LimitAllotment | undefined) => void): void;
|
|
147
|
+
export type RunCallbackArgs<ContextT> = {
|
|
148
|
+
context: ContextT | undefined;
|
|
149
|
+
signal: AbortSignal | undefined;
|
|
150
|
+
};
|
|
151
|
+
export interface LimitedFunction<ContextT> {
|
|
152
|
+
<T, E extends Error = Error>(fn: (args: RunCallbackArgs<ContextT>) => RunResult<T, E> | Promise<RunResult<T, E>>): Promise<T | typeof QuotaNotAvailable>;
|
|
153
|
+
<T, E extends Error = Error>(options: AcquireOptions<ContextT>, fn: (args: RunCallbackArgs<ContextT>) => RunResult<T, E> | Promise<RunResult<T, E>>): Promise<T | typeof QuotaNotAvailable>;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Creates a helper that runs callbacks under acquired limiter allotments.
|
|
157
|
+
*
|
|
158
|
+
* - If {@link Limiter.acquire} yields no allotment, returns
|
|
159
|
+
* {@link QuotaNotAvailable} without invoking `fn`.
|
|
160
|
+
* - On {@link RunResult} `success` / `ignore`, returns the carried value after
|
|
161
|
+
* reporting to the allotment.
|
|
162
|
+
* - On `dropped`, reports drop and throws the carried error.
|
|
163
|
+
* - On uncaught exceptions from `fn`, reports ignore and rethrows, except for
|
|
164
|
+
* {@link AdaptiveTimeoutError}, which reports drop and rethrows.
|
|
165
|
+
* - Callback receives `{ context, signal }` from acquire options.
|
|
166
|
+
*/
|
|
167
|
+
export declare function withLimiter<ContextT, RejResultT extends SyncAcquireResult | AsyncAcquireResult>(limiter: Limiter<ContextT, RejResultT>): LimitedFunction<ContextT>;
|
|
168
|
+
/**
|
|
169
|
+
* Synchronous try-acquire interface. A `Limiter<Ctx>` (with default sync
|
|
170
|
+
* rejection) is structurally compatible with this interface.
|
|
171
|
+
*/
|
|
172
|
+
export interface SyncLimiter<ContextT = void> {
|
|
173
|
+
acquire(options?: AcquireOptions<ContextT>): LimitAllotment | undefined;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=Limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Limiter.d.ts","sourceRoot":"","sources":["../src/Limiter.ts"],"names":[],"mappings":"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"}
|
package/dist/Limiter.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { GradientLimit } from "./limit/GradientLimit.js";
|
|
2
|
+
import { MetricIds, NoopMetricRegistry } from "./MetricRegistry.js";
|
|
3
|
+
import { isAdaptiveTimeoutError, QuotaNotAvailable, } from "./RunResult.js";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Limiter options
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const NOOP_ALLOTMENT = {
|
|
8
|
+
reportSuccess() { },
|
|
9
|
+
reportIgnore() { },
|
|
10
|
+
reportDropped() { },
|
|
11
|
+
};
|
|
12
|
+
let idCounter = 0;
|
|
13
|
+
/**
|
|
14
|
+
* Concurrency limiter with pluggable strategies for gating decisions and
|
|
15
|
+
* rejection handling.
|
|
16
|
+
*
|
|
17
|
+
* When no rejection strategy is provided, `acquire()` returns synchronously
|
|
18
|
+
* (`LimitAllotment | undefined`). When a blocking rejection strategy is
|
|
19
|
+
* configured, `acquire()` may also return a `Promise`.
|
|
20
|
+
*
|
|
21
|
+
* @typeParam ContextT Request context type (e.g. partition key).
|
|
22
|
+
* @typeParam RejResultT The result type produced by the rejection strategy.
|
|
23
|
+
*/
|
|
24
|
+
export class Limiter {
|
|
25
|
+
_inflight = 0;
|
|
26
|
+
_limit;
|
|
27
|
+
clock;
|
|
28
|
+
limitAlgorithm;
|
|
29
|
+
acquireStrategy;
|
|
30
|
+
rejectionStrategy;
|
|
31
|
+
bypassResolver;
|
|
32
|
+
successCounter;
|
|
33
|
+
droppedCounter;
|
|
34
|
+
ignoredCounter;
|
|
35
|
+
rejectedCounter;
|
|
36
|
+
bypassCounter;
|
|
37
|
+
static makeDefaultLimit() {
|
|
38
|
+
return new GradientLimit();
|
|
39
|
+
}
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.clock = options.clock ?? (() => performance.now());
|
|
42
|
+
this.limitAlgorithm = options.limit ?? Limiter.makeDefaultLimit();
|
|
43
|
+
this._limit = this.limitAlgorithm.currentLimit;
|
|
44
|
+
this.bypassResolver = options.bypassResolver;
|
|
45
|
+
this.acquireStrategy =
|
|
46
|
+
options.acquireStrategy ?? new SemaphoreStrategy(this._limit);
|
|
47
|
+
this.rejectionStrategy = options.allotmentUnavailableStrategy;
|
|
48
|
+
this.limitAlgorithm.subscribe((newLimit) => {
|
|
49
|
+
const oldLimit = this._limit;
|
|
50
|
+
this._limit = newLimit;
|
|
51
|
+
this.acquireStrategy.onLimitChanged?.(oldLimit, newLimit);
|
|
52
|
+
});
|
|
53
|
+
const registry = options.metricRegistry ?? NoopMetricRegistry;
|
|
54
|
+
const limiterName = options.name ?? `unnamed-${++idCounter}`;
|
|
55
|
+
registry.gauge(MetricIds.LIMIT_NAME, () => this._limit);
|
|
56
|
+
this.successCounter = registry.counter(MetricIds.CALL_NAME, {
|
|
57
|
+
id: limiterName,
|
|
58
|
+
status: "success",
|
|
59
|
+
});
|
|
60
|
+
this.droppedCounter = registry.counter(MetricIds.CALL_NAME, {
|
|
61
|
+
id: limiterName,
|
|
62
|
+
status: "dropped",
|
|
63
|
+
});
|
|
64
|
+
this.ignoredCounter = registry.counter(MetricIds.CALL_NAME, {
|
|
65
|
+
id: limiterName,
|
|
66
|
+
status: "ignored",
|
|
67
|
+
});
|
|
68
|
+
this.rejectedCounter = registry.counter(MetricIds.CALL_NAME, {
|
|
69
|
+
id: limiterName,
|
|
70
|
+
status: "rejected",
|
|
71
|
+
});
|
|
72
|
+
this.bypassCounter = registry.counter(MetricIds.CALL_NAME, {
|
|
73
|
+
id: limiterName,
|
|
74
|
+
status: "bypassed",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
acquire(options) {
|
|
78
|
+
if (options?.signal?.aborted)
|
|
79
|
+
return undefined;
|
|
80
|
+
const ctx = (options?.context ?? undefined);
|
|
81
|
+
if (this.bypassResolver?.(ctx)) {
|
|
82
|
+
this.bypassCounter.increment();
|
|
83
|
+
return NOOP_ALLOTMENT;
|
|
84
|
+
}
|
|
85
|
+
const state = {
|
|
86
|
+
limit: this._limit,
|
|
87
|
+
inflight: this._inflight,
|
|
88
|
+
};
|
|
89
|
+
if (!this.acquireStrategy.tryAcquireAllotment(ctx, state)) {
|
|
90
|
+
this.rejectedCounter.increment();
|
|
91
|
+
if (this.rejectionStrategy) {
|
|
92
|
+
return this.rejectionStrategy.onAllotmentUnavailable(ctx, (retryCtx) => this.tryAcquireCore(retryCtx), options?.signal);
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
return this.createAllotment(ctx);
|
|
97
|
+
}
|
|
98
|
+
tryAcquireCore(ctx) {
|
|
99
|
+
const state = {
|
|
100
|
+
limit: this._limit,
|
|
101
|
+
inflight: this._inflight,
|
|
102
|
+
};
|
|
103
|
+
if (!this.acquireStrategy.tryAcquireAllotment(ctx, state)) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
return this.createAllotment(ctx);
|
|
107
|
+
}
|
|
108
|
+
createAllotment(ctx) {
|
|
109
|
+
const startTime = this.clock();
|
|
110
|
+
const currentInflight = ++this._inflight;
|
|
111
|
+
return {
|
|
112
|
+
reportSuccess: () => {
|
|
113
|
+
this._inflight--;
|
|
114
|
+
this.acquireStrategy.onAllotmentReleased(ctx);
|
|
115
|
+
this.successCounter.increment();
|
|
116
|
+
this.limitAlgorithm.addSample(startTime, this.clock() - startTime, currentInflight, false);
|
|
117
|
+
this.rejectionStrategy?.onAllotmentReleased();
|
|
118
|
+
},
|
|
119
|
+
reportIgnore: () => {
|
|
120
|
+
this._inflight--;
|
|
121
|
+
this.acquireStrategy.onAllotmentReleased(ctx);
|
|
122
|
+
this.ignoredCounter.increment();
|
|
123
|
+
this.rejectionStrategy?.onAllotmentReleased();
|
|
124
|
+
},
|
|
125
|
+
reportDropped: () => {
|
|
126
|
+
this._inflight--;
|
|
127
|
+
this.acquireStrategy.onAllotmentReleased(ctx);
|
|
128
|
+
this.droppedCounter.increment();
|
|
129
|
+
this.limitAlgorithm.addSample(startTime, this.clock() - startTime, currentInflight, true);
|
|
130
|
+
this.rejectionStrategy?.onAllotmentReleased();
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
getLimit() {
|
|
135
|
+
return this._limit;
|
|
136
|
+
}
|
|
137
|
+
getInflight() {
|
|
138
|
+
return this._inflight;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Built-in acquire strategy: Semaphore
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
/**
|
|
145
|
+
* Simple semaphore-based acquire strategy. Tracks a permits counter that is
|
|
146
|
+
* decremented when an allotment is taken and incremented on release. When
|
|
147
|
+
* permits reach zero, {@link tryAcquireAllotment} returns false.
|
|
148
|
+
*/
|
|
149
|
+
export class SemaphoreStrategy {
|
|
150
|
+
permits;
|
|
151
|
+
constructor(initialLimit) {
|
|
152
|
+
this.permits = initialLimit;
|
|
153
|
+
}
|
|
154
|
+
tryAcquireAllotment() {
|
|
155
|
+
if (this.permits <= 0)
|
|
156
|
+
return false;
|
|
157
|
+
this.permits--;
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
onAllotmentReleased() {
|
|
161
|
+
this.permits++;
|
|
162
|
+
}
|
|
163
|
+
onLimitChanged(oldLimit, newLimit) {
|
|
164
|
+
this.permits += newLimit - oldLimit;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Helper: whenAcquireSettled
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
/**
|
|
171
|
+
* Run a callback when an acquire result is ready. If the result is a Promise,
|
|
172
|
+
* waits asynchronously; otherwise invokes the callback synchronously.
|
|
173
|
+
*/
|
|
174
|
+
export function whenAcquireSettled(result, callback) {
|
|
175
|
+
if (result !== null &&
|
|
176
|
+
typeof result === "object" &&
|
|
177
|
+
"then" in result &&
|
|
178
|
+
typeof result.then ===
|
|
179
|
+
"function") {
|
|
180
|
+
void result.then(callback);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
callback(result);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Creates a helper that runs callbacks under acquired limiter allotments.
|
|
188
|
+
*
|
|
189
|
+
* - If {@link Limiter.acquire} yields no allotment, returns
|
|
190
|
+
* {@link QuotaNotAvailable} without invoking `fn`.
|
|
191
|
+
* - On {@link RunResult} `success` / `ignore`, returns the carried value after
|
|
192
|
+
* reporting to the allotment.
|
|
193
|
+
* - On `dropped`, reports drop and throws the carried error.
|
|
194
|
+
* - On uncaught exceptions from `fn`, reports ignore and rethrows, except for
|
|
195
|
+
* {@link AdaptiveTimeoutError}, which reports drop and rethrows.
|
|
196
|
+
* - Callback receives `{ context, signal }` from acquire options.
|
|
197
|
+
*/
|
|
198
|
+
export function withLimiter(limiter) {
|
|
199
|
+
async function limited(optionsOrFn, maybeFn) {
|
|
200
|
+
const hasOptions = maybeFn !== undefined;
|
|
201
|
+
const options = hasOptions
|
|
202
|
+
? optionsOrFn
|
|
203
|
+
: undefined;
|
|
204
|
+
const fn = hasOptions
|
|
205
|
+
? maybeFn
|
|
206
|
+
: optionsOrFn;
|
|
207
|
+
const allotment = await Promise.resolve(limiter.acquire(options));
|
|
208
|
+
if (!allotment) {
|
|
209
|
+
return QuotaNotAvailable;
|
|
210
|
+
}
|
|
211
|
+
const [result] = await Promise.allSettled([
|
|
212
|
+
fn({
|
|
213
|
+
context: options?.context,
|
|
214
|
+
signal: options?.signal,
|
|
215
|
+
}),
|
|
216
|
+
]);
|
|
217
|
+
if (result.status === "rejected") {
|
|
218
|
+
if (isAdaptiveTimeoutError(result.reason)) {
|
|
219
|
+
allotment.reportDropped();
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
allotment.reportIgnore();
|
|
223
|
+
}
|
|
224
|
+
throw result.reason;
|
|
225
|
+
}
|
|
226
|
+
const outcome = result.value;
|
|
227
|
+
switch (outcome.kind) {
|
|
228
|
+
case "success":
|
|
229
|
+
allotment.reportSuccess();
|
|
230
|
+
return outcome.value;
|
|
231
|
+
case "ignore":
|
|
232
|
+
allotment.reportIgnore();
|
|
233
|
+
return outcome.value;
|
|
234
|
+
case "dropped":
|
|
235
|
+
allotment.reportDropped();
|
|
236
|
+
throw outcome.error;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return limited;
|
|
240
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback interface for the result of a request. The caller must invoke
|
|
3
|
+
* exactly one of these methods when the operation completes.
|
|
4
|
+
*/
|
|
5
|
+
export interface Listener {
|
|
6
|
+
/**
|
|
7
|
+
* Notification that the operation succeeded and internally measured latency
|
|
8
|
+
* should be used as an RTT sample.
|
|
9
|
+
*/
|
|
10
|
+
onSuccess(): void;
|
|
11
|
+
/**
|
|
12
|
+
* The operation failed before any meaningful RTT measurement could be made
|
|
13
|
+
* and should be ignored to not introduce an artificially low RTT.
|
|
14
|
+
*/
|
|
15
|
+
onIgnore(): void;
|
|
16
|
+
/**
|
|
17
|
+
* The request failed and was dropped due to being rejected by an external
|
|
18
|
+
* limit or hitting a timeout. Loss based StreamingLimit implementations will
|
|
19
|
+
* likely do an aggressive reduction in limit when this happens.
|
|
20
|
+
*/
|
|
21
|
+
onDropped(): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=Listener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Listener.d.ts","sourceRoot":"","sources":["../src/Listener.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,SAAS,IAAI,IAAI,CAAC;IAElB;;;OAGG;IACH,QAAQ,IAAI,IAAI,CAAC;IAEjB;;;;OAIG;IACH,SAAS,IAAI,IAAI,CAAC;CACnB"}
|
package/dist/Listener.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fan-out for numeric change notifications with the same subscribe contract as
|
|
3
|
+
* {@link StreamingLimit.subscribe}.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ListenerSet<T extends (this: void, ...args: any[]) => any> {
|
|
6
|
+
private readonly listeners;
|
|
7
|
+
subscribe(consumer: T, options?: {
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}): () => void;
|
|
10
|
+
notify(...args: Parameters<T>): void;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=ListenerSet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListenerSet.d.ts","sourceRoot":"","sources":["../src/ListenerSet.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,WAAW,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG;IACpE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAE1C,SAAS,CACP,QAAQ,EAAE,CAAC,EACX,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACrC,MAAM,IAAI;IA6Bb,MAAM,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;CAKrC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fan-out for numeric change notifications with the same subscribe contract as
|
|
3
|
+
* {@link StreamingLimit.subscribe}.
|
|
4
|
+
*/
|
|
5
|
+
export class ListenerSet {
|
|
6
|
+
listeners = [];
|
|
7
|
+
subscribe(consumer, options = {}) {
|
|
8
|
+
if (options.signal?.aborted) {
|
|
9
|
+
return () => { };
|
|
10
|
+
}
|
|
11
|
+
this.listeners.push(consumer);
|
|
12
|
+
let unsubscribed = false;
|
|
13
|
+
const onAbort = () => {
|
|
14
|
+
unsubscribe();
|
|
15
|
+
};
|
|
16
|
+
const unsubscribe = () => {
|
|
17
|
+
if (unsubscribed) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
unsubscribed = true;
|
|
21
|
+
const idx = this.listeners.indexOf(consumer);
|
|
22
|
+
if (idx !== -1) {
|
|
23
|
+
this.listeners.splice(idx, 1);
|
|
24
|
+
}
|
|
25
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
26
|
+
};
|
|
27
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
28
|
+
return unsubscribe;
|
|
29
|
+
}
|
|
30
|
+
notify(...args) {
|
|
31
|
+
for (const listener of this.listeners) {
|
|
32
|
+
listener(...args);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common metric ids used by the limiters and limit algorithms.
|
|
3
|
+
*/
|
|
4
|
+
export declare const MetricIds: {
|
|
5
|
+
readonly LIMIT_NAME: "limit";
|
|
6
|
+
readonly CALL_NAME: "call";
|
|
7
|
+
readonly INFLIGHT_NAME: "inflight";
|
|
8
|
+
readonly PARTITION_LIMIT_NAME: "limit.partition";
|
|
9
|
+
readonly MIN_RTT_NAME: "min_rtt";
|
|
10
|
+
readonly WINDOW_MIN_RTT_NAME: "min_window_rtt";
|
|
11
|
+
readonly WINDOW_QUEUE_SIZE_NAME: "queue_size";
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=MetricIds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricIds.d.ts","sourceRoot":"","sources":["../src/MetricIds.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;;CAQZ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common metric ids used by the limiters and limit algorithms.
|
|
3
|
+
*/
|
|
4
|
+
export const MetricIds = {
|
|
5
|
+
LIMIT_NAME: "limit",
|
|
6
|
+
CALL_NAME: "call",
|
|
7
|
+
INFLIGHT_NAME: "inflight",
|
|
8
|
+
PARTITION_LIMIT_NAME: "limit.partition",
|
|
9
|
+
MIN_RTT_NAME: "min_rtt",
|
|
10
|
+
WINDOW_MIN_RTT_NAME: "min_window_rtt",
|
|
11
|
+
WINDOW_QUEUE_SIZE_NAME: "queue_size",
|
|
12
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common metric ids used by the limiters and limit algorithms.
|
|
3
|
+
*/
|
|
4
|
+
export declare const MetricIds: {
|
|
5
|
+
readonly LIMIT_NAME: "limit";
|
|
6
|
+
readonly CALL_NAME: "call";
|
|
7
|
+
readonly INFLIGHT_NAME: "inflight";
|
|
8
|
+
readonly PARTITION_LIMIT_NAME: "limit.partition";
|
|
9
|
+
readonly MIN_RTT_NAME: "min_rtt";
|
|
10
|
+
readonly WINDOW_MIN_RTT_NAME: "min_window_rtt";
|
|
11
|
+
readonly WINDOW_QUEUE_SIZE_NAME: "queue_size";
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Listener to receive samples for a distribution.
|
|
15
|
+
*/
|
|
16
|
+
export interface DistributionMetric {
|
|
17
|
+
addSample(value: number): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A counter that can be incremented when an event occurs. Counters normally
|
|
21
|
+
* translate into an actions-per-second metric.
|
|
22
|
+
*/
|
|
23
|
+
export interface Counter {
|
|
24
|
+
increment(): void;
|
|
25
|
+
}
|
|
26
|
+
/** Opaque handle for a registered gauge (supplier is polled by the registry on flush). */
|
|
27
|
+
export interface GaugeMetric {
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Simple abstraction for tracking metrics in the limiters.
|
|
31
|
+
*/
|
|
32
|
+
export interface MetricRegistry {
|
|
33
|
+
/**
|
|
34
|
+
* Register a sample distribution. Samples are added to the distribution via
|
|
35
|
+
* the returned {@link DistributionMetric}. Will reuse an existing
|
|
36
|
+
* {@link DistributionMetric} if the distribution already exists.
|
|
37
|
+
*
|
|
38
|
+
* @param id Metric identifier
|
|
39
|
+
* @param tagNameValuePairs Pairs of tag name and tag value
|
|
40
|
+
* @returns SampleListener for the caller to add samples
|
|
41
|
+
*/
|
|
42
|
+
distribution(id: string, ...tagNameValuePairs: string[]): DistributionMetric;
|
|
43
|
+
/**
|
|
44
|
+
* Register a gauge using the provided supplier. The supplier will be polled
|
|
45
|
+
* whenever the gauge value is flushed by the registry.
|
|
46
|
+
*
|
|
47
|
+
* @param id Metric identifier
|
|
48
|
+
* @param supplier Function that returns the current gauge value
|
|
49
|
+
* @param tagNameValuePairs Pairs of tag name and tag value
|
|
50
|
+
* @returns Registration handle for the gauge
|
|
51
|
+
*/
|
|
52
|
+
gauge(id: string, supplier: () => number, ...tagNameValuePairs: string[]): GaugeMetric;
|
|
53
|
+
/**
|
|
54
|
+
* Create a counter that will be incremented when an event occurs.
|
|
55
|
+
*
|
|
56
|
+
* @param id Metric identifier
|
|
57
|
+
* @param attributes Counter attributes/tags
|
|
58
|
+
*/
|
|
59
|
+
counter(id: string, attributes: Record<string, string>): Counter;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* No-op MetricRegistry that discards all metrics. Used as the default when
|
|
63
|
+
* no registry is configured.
|
|
64
|
+
*/
|
|
65
|
+
export declare const NoopMetricRegistry: MetricRegistry;
|
|
66
|
+
//# sourceMappingURL=MetricRegistry.d.ts.map
|