adaptive-concurrency 0.3.2 → 0.3.4
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/LimitAllotment.d.ts +7 -1
- package/dist/LimitAllotment.d.ts.map +1 -1
- package/dist/Limiter.d.ts +15 -21
- package/dist/Limiter.d.ts.map +1 -1
- package/dist/Limiter.js +99 -56
- package/dist/RunResult.d.ts +12 -6
- package/dist/RunResult.d.ts.map +1 -1
- package/dist/RunResult.js +10 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/limiter/acquire-strategies/PartitionedStrategy.d.ts +34 -5
- package/dist/limiter/acquire-strategies/PartitionedStrategy.d.ts.map +1 -1
- package/dist/limiter/acquire-strategies/PartitionedStrategy.js +34 -9
- package/dist/limiter/acquire-strategies/SemaphoreStrategy.d.ts +13 -0
- package/dist/limiter/acquire-strategies/SemaphoreStrategy.d.ts.map +1 -0
- package/dist/limiter/acquire-strategies/SemaphoreStrategy.js +23 -0
- package/dist/limiter/allocation-unavailable-strategies/BlockingBacklogRejection.d.ts +36 -0
- package/dist/limiter/allocation-unavailable-strategies/BlockingBacklogRejection.d.ts.map +1 -0
- package/dist/limiter/allocation-unavailable-strategies/BlockingBacklogRejection.js +117 -0
- package/dist/limiter/allocation-unavailable-strategies/DelayedThenBlockingRejection.d.ts +2 -1
- package/dist/limiter/allocation-unavailable-strategies/DelayedThenBlockingRejection.d.ts.map +1 -1
- package/dist/limiter/allocation-unavailable-strategies/DelayedThenBlockingRejection.js +10 -2
- package/dist/limiter/allocation-unavailable-strategies/FifoBlockingRejection.d.ts +19 -15
- package/dist/limiter/allocation-unavailable-strategies/FifoBlockingRejection.d.ts.map +1 -1
- package/dist/limiter/allocation-unavailable-strategies/FifoBlockingRejection.js +13 -61
- package/dist/limiter/allocation-unavailable-strategies/LifoBlockingRejection.d.ts +4 -8
- package/dist/limiter/allocation-unavailable-strategies/LifoBlockingRejection.d.ts.map +1 -1
- package/dist/limiter/allocation-unavailable-strategies/LifoBlockingRejection.js +13 -49
- package/dist/limiter/factories/makeBlockingLimiter.d.ts +3 -1
- package/dist/limiter/factories/makeBlockingLimiter.d.ts.map +1 -1
- package/dist/limiter/factories/makeBlockingLimiter.js +3 -2
- package/dist/limiter/factories/makeLifoBlockingLimiter.d.ts +1 -1
- package/dist/limiter/factories/makeLifoBlockingLimiter.d.ts.map +1 -1
- package/dist/limiter/factories/makeLifoBlockingLimiter.js +1 -1
- package/dist/limiter/factories/makePartitionedBlockingLimiter.d.ts +3 -1
- package/dist/limiter/factories/makePartitionedBlockingLimiter.d.ts.map +1 -1
- package/dist/limiter/factories/makePartitionedBlockingLimiter.js +5 -4
- package/dist/limiter/factories/makeSimpleLimiter.d.ts.map +1 -1
- package/dist/limiter/factories/makeSimpleLimiter.js +2 -1
- package/dist/utils/LinkedWaiterQueue.d.ts +21 -0
- package/dist/utils/LinkedWaiterQueue.d.ts.map +1 -0
- package/dist/utils/LinkedWaiterQueue.js +81 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/package.json +4 -3
package/dist/LimitAllotment.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Handle returned when a concurrency slot is acquired. The caller must invoke
|
|
3
|
-
* exactly one of the release-and-
|
|
3
|
+
* exactly one of the release-and-xxx methods when the operation completes.
|
|
4
|
+
*
|
|
5
|
+
* **Important for implementers:** the release methods below must never throw
|
|
6
|
+
* or produce a rejected promise. If an error occurs internally (e.g. notifying
|
|
7
|
+
* a strategy), the implementation is responsible for catching and handling it.
|
|
8
|
+
* Callers may invoke these methods in a fire-and-forget manner (without
|
|
9
|
+
* awaiting), so any rejection would become an unhandled promise rejection.
|
|
4
10
|
*/
|
|
5
11
|
export interface LimitAllotment {
|
|
6
12
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LimitAllotment.d.ts","sourceRoot":"","sources":["../src/LimitAllotment.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"LimitAllotment.d.ts","sourceRoot":"","sources":["../src/LimitAllotment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC;;;OAGG;IACH,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAElC;;;;OAIG;IACH,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C"}
|
package/dist/Limiter.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type MaybePromise<T> = T | Promise<T>;
|
|
|
9
9
|
export type AcquireResult = Promise<LimitAllotment | undefined>;
|
|
10
10
|
export interface AcquireOptions<ContextT = void> {
|
|
11
11
|
context?: ContextT;
|
|
12
|
-
signal?: AbortSignal;
|
|
12
|
+
signal?: AbortSignal | undefined;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Read-only view of the limiter's current state, provided to strategies so
|
|
@@ -40,13 +40,13 @@ export interface AcquireStrategy<ContextT> {
|
|
|
40
40
|
* Called when the adaptive limit changes. The strategy can react
|
|
41
41
|
* (e.g. adjust available permits by the delta, update partition sub-limits).
|
|
42
42
|
*/
|
|
43
|
-
onLimitChanged?(oldLimit: number, newLimit: number):
|
|
43
|
+
onLimitChanged?(oldLimit: number, newLimit: number): void;
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* Determines what happens when a request is rejected by the
|
|
47
|
-
* {@link AcquireStrategy}.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* {@link AcquireStrategy} (e.g. queue the caller, reject immediately, or block
|
|
48
|
+
* until a slot is available). Implementations must return an
|
|
49
|
+
* {@link AcquireResult}, which is always a `Promise`.
|
|
50
50
|
*/
|
|
51
51
|
export interface AllotmentUnavailableStrategy<ContextT> {
|
|
52
52
|
/**
|
|
@@ -66,6 +66,12 @@ export interface AllotmentUnavailableStrategy<ContextT> {
|
|
|
66
66
|
* Blocking strategies use this to wake queued waiters.
|
|
67
67
|
*/
|
|
68
68
|
onAllotmentReleased(): MaybePromise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Called when the adaptive limit changes. Blocking/rejection strategies can
|
|
71
|
+
* use this to proactively react to newly available capacity (e.g. drain
|
|
72
|
+
* backlog).
|
|
73
|
+
*/
|
|
74
|
+
onLimitChanged?(oldLimit: number, newLimit: number): MaybePromise<void>;
|
|
69
75
|
}
|
|
70
76
|
export interface LimiterOptions<ContextT> {
|
|
71
77
|
limit?: AdaptiveLimit;
|
|
@@ -97,8 +103,6 @@ export interface LimiterOptions<ContextT> {
|
|
|
97
103
|
* Concurrency limiter with pluggable strategies for gating decisions and
|
|
98
104
|
* rejection handling.
|
|
99
105
|
*
|
|
100
|
-
* `acquire()` always returns a `Promise<LimitAllotment | undefined>`.
|
|
101
|
-
*
|
|
102
106
|
* @typeParam ContextT Request context type (e.g. partition key).
|
|
103
107
|
*/
|
|
104
108
|
export declare class Limiter<Context = void> {
|
|
@@ -122,25 +126,13 @@ export declare class Limiter<Context = void> {
|
|
|
122
126
|
getLimit(): number;
|
|
123
127
|
getInflight(): number;
|
|
124
128
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Simple semaphore-based acquire strategy. Tracks a permits counter that is
|
|
127
|
-
* decremented when an allotment is taken and incremented on release. When
|
|
128
|
-
* permits reach zero, {@link tryAcquireAllotment} returns false.
|
|
129
|
-
*/
|
|
130
|
-
export declare class SemaphoreStrategy {
|
|
131
|
-
private permits;
|
|
132
|
-
constructor(initialLimit: number);
|
|
133
|
-
tryAcquireAllotment(): boolean;
|
|
134
|
-
onAllotmentReleased(): void;
|
|
135
|
-
onLimitChanged(oldLimit: number, newLimit: number): void;
|
|
136
|
-
}
|
|
137
129
|
export type RunCallbackArgs<ContextT> = {
|
|
138
130
|
context: ContextT | undefined;
|
|
139
131
|
signal: AbortSignal | undefined;
|
|
140
132
|
};
|
|
141
133
|
export interface LimitedFunction<ContextT> {
|
|
142
|
-
<T, E extends Error = Error>(fn: (args: RunCallbackArgs<ContextT>) => RunResult<T, E> | Promise<RunResult<T, E>>): Promise<T | typeof QuotaNotAvailable>;
|
|
143
|
-
<T, E extends Error = Error>(options: AcquireOptions<ContextT>, fn: (args: RunCallbackArgs<ContextT>) => RunResult<T, E> | Promise<RunResult<T, E>>): Promise<T | typeof QuotaNotAvailable>;
|
|
134
|
+
<T, E extends Error = Error>(fn: (args: RunCallbackArgs<ContextT>) => T | RunResult<T, E> | Promise<T | RunResult<T, E>>): Promise<T | typeof QuotaNotAvailable>;
|
|
135
|
+
<T, E extends Error = Error>(options: AcquireOptions<ContextT>, fn: (args: RunCallbackArgs<ContextT>) => T | RunResult<T, E> | Promise<T | RunResult<T, E>>): Promise<T | typeof QuotaNotAvailable>;
|
|
144
136
|
}
|
|
145
137
|
/**
|
|
146
138
|
* Creates a helper that runs callbacks under acquired limiter allotments.
|
|
@@ -150,6 +142,8 @@ export interface LimitedFunction<ContextT> {
|
|
|
150
142
|
* - On {@link RunResult} `success` / `ignore`, returns the carried value after
|
|
151
143
|
* recording success/ignore to the allotment.
|
|
152
144
|
* - On `dropped`, records drop and throws the carried error.
|
|
145
|
+
* - If callback returns a non-{@link RunResult} value, it is treated as
|
|
146
|
+
* implicit success and returned as-is.
|
|
153
147
|
* - On uncaught exceptions from `fn`, records ignore and rethrows, except for
|
|
154
148
|
* {@link AdaptiveTimeoutError}, which records drop and rethrows.
|
|
155
149
|
* - Callback receives `{ context, signal }` from acquire options.
|
package/dist/Limiter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Limiter.d.ts","sourceRoot":"","sources":["../src/Limiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Limiter.d.ts","sourceRoot":"","sources":["../src/Limiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,EAGL,iBAAiB,EACjB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;AAEhE,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,IAAI;IAC7C,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAClC;AAMD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,QAAQ;IACvC;;;;OAIG;IACH,mBAAmB,CACjB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,YAAY,GAClB,YAAY,CAAC,OAAO,CAAC,CAAC;IAEzB;;;OAGG;IACH,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAE3D;;;OAGG;IACH,cAAc,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D;AAED;;;;;GAKG;AACH,MAAM,WAAW,4BAA4B,CAAC,QAAQ;IACpD;;;;;;;;;;OAUG;IACH,sBAAsB,CACpB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,aAAa,EAC3C,MAAM,CAAC,EAAE,WAAW,GACnB,aAAa,CAAC;IAEjB;;;OAGG;IACH,mBAAmB,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;IAE1C;;;;OAIG;IACH,cAAc,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;CACzE;AAaD,MAAM,WAAW,cAAc,CAAC,QAAQ;IACtC,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAErB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;IAEhD;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IAE5C;;;OAGG;IACH,4BAA4B,CAAC,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC;CACvE;AAED;;;;;GAKG;AACH,qBAAa,OAAO,CAAC,OAAO,GAAG,IAAI;IACjC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA2B;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAEpB;IACd,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA8C;IAE7E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IAExC,MAAM,CAAC,gBAAgB,IAAI,aAAa;gBAI5B,OAAO,GAAE,cAAc,CAAC,OAAO,CAAM;IA2C3C,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,aAAa;YAgDjD,cAAc;IAY5B,OAAO,CAAC,eAAe;IA2EvB,QAAQ,IAAI,MAAM;IAIlB,WAAW,IAAI,MAAM;CAGtB;AAED,MAAM,MAAM,eAAe,CAAC,QAAQ,IAAI;IACtC,OAAO,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;CACjC,CAAC;AACF,MAAM,WAAW,eAAe,CAAC,QAAQ;IACvC,CAAC,CAAC,EAAE,CAAC,SAAS,KAAK,GAAG,KAAK,EACzB,EAAE,EAAE,CACF,IAAI,EAAE,eAAe,CAAC,QAAQ,CAAC,KAC5B,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC,GAAG,OAAO,iBAAiB,CAAC,CAAC;IAEzC,CAAC,CAAC,EAAE,CAAC,SAAS,KAAK,GAAG,KAAK,EACzB,OAAO,EAAE,cAAc,CAAC,QAAQ,CAAC,EACjC,EAAE,EAAE,CACF,IAAI,EAAE,eAAe,CAAC,QAAQ,CAAC,KAC5B,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC,GAAG,OAAO,iBAAiB,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAClC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,GACzB,eAAe,CAAC,QAAQ,CAAC,CAmE3B"}
|
package/dist/Limiter.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GradientLimit } from "./limit/GradientLimit.js";
|
|
2
|
+
import { SemaphoreStrategy } from "./limiter/acquire-strategies/SemaphoreStrategy.js";
|
|
2
3
|
import { MetricIds, NoopMetricRegistry } from "./MetricRegistry.js";
|
|
3
|
-
import { isAdaptiveTimeoutError, QuotaNotAvailable, } from "./RunResult.js";
|
|
4
|
+
import { isAdaptiveTimeoutError, isRunResult, QuotaNotAvailable, } from "./RunResult.js";
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
6
|
// Limiter options
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
@@ -14,8 +15,6 @@ let idCounter = 0;
|
|
|
14
15
|
* Concurrency limiter with pluggable strategies for gating decisions and
|
|
15
16
|
* rejection handling.
|
|
16
17
|
*
|
|
17
|
-
* `acquire()` always returns a `Promise<LimitAllotment | undefined>`.
|
|
18
|
-
*
|
|
19
18
|
* @typeParam ContextT Request context type (e.g. partition key).
|
|
20
19
|
*/
|
|
21
20
|
export class Limiter {
|
|
@@ -45,7 +44,8 @@ export class Limiter {
|
|
|
45
44
|
this.limitAlgorithm.subscribe((newLimit) => {
|
|
46
45
|
const oldLimit = this._limit;
|
|
47
46
|
this._limit = newLimit;
|
|
48
|
-
|
|
47
|
+
this.acquireStrategy.onLimitChanged?.(oldLimit, newLimit);
|
|
48
|
+
void this.rejectionStrategy?.onLimitChanged?.(oldLimit, newLimit);
|
|
49
49
|
});
|
|
50
50
|
const registry = options.metricRegistry ?? NoopMetricRegistry;
|
|
51
51
|
const limiterName = options.name ?? `unnamed-${++idCounter}`;
|
|
@@ -85,12 +85,27 @@ export class Limiter {
|
|
|
85
85
|
};
|
|
86
86
|
if (!(await this.acquireStrategy.tryAcquireAllotment(ctx, state))) {
|
|
87
87
|
this.rejectedCounter.increment();
|
|
88
|
-
if (this.rejectionStrategy) {
|
|
89
|
-
return
|
|
88
|
+
if (!this.rejectionStrategy) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
// if signal aborted here, nothing to cleanup, as we didn't acquire anything.
|
|
92
|
+
if (options?.signal?.aborted) {
|
|
93
|
+
return undefined;
|
|
90
94
|
}
|
|
95
|
+
return this.rejectionStrategy.onAllotmentUnavailable(ctx, async (retryCtx) => {
|
|
96
|
+
if (options?.signal?.aborted) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return this.tryAcquireCore(retryCtx);
|
|
100
|
+
}, options?.signal);
|
|
101
|
+
}
|
|
102
|
+
const allotment = this.createAllotment(ctx);
|
|
103
|
+
if (options?.signal?.aborted) {
|
|
104
|
+
// here, we did acquire, so we need to try to cleanup.
|
|
105
|
+
await allotment.releaseAndIgnore();
|
|
91
106
|
return undefined;
|
|
92
107
|
}
|
|
93
|
-
return
|
|
108
|
+
return allotment;
|
|
94
109
|
}
|
|
95
110
|
async tryAcquireCore(ctx) {
|
|
96
111
|
const state = {
|
|
@@ -105,26 +120,77 @@ export class Limiter {
|
|
|
105
120
|
createAllotment(ctx) {
|
|
106
121
|
const startTime = this.clock();
|
|
107
122
|
const currentInflight = ++this._inflight;
|
|
123
|
+
// Make sure an allotment can only be released once; future calls become a
|
|
124
|
+
// no-op. This simplifies a lot of cleanup handling etc that'd otherwise be
|
|
125
|
+
// much racier/more complicated. It could hide subtle correctness issue, but
|
|
126
|
+
// should be more valuable as defense-in-depth.
|
|
127
|
+
let releaseStarted = false;
|
|
108
128
|
return {
|
|
109
129
|
releaseAndRecordSuccess: async () => {
|
|
130
|
+
if (releaseStarted)
|
|
131
|
+
return;
|
|
132
|
+
releaseStarted = true;
|
|
133
|
+
const endTime = this.clock();
|
|
134
|
+
const rtt = endTime - startTime;
|
|
110
135
|
this._inflight--;
|
|
111
|
-
await this.acquireStrategy.onAllotmentReleased(ctx);
|
|
112
136
|
this.successCounter.increment();
|
|
113
|
-
|
|
114
|
-
|
|
137
|
+
// If one onAllotmentReleased call fails, hard to know what to do here.
|
|
138
|
+
// We're in some kind of inconsistent state, but we probably have to
|
|
139
|
+
// soldier on.
|
|
140
|
+
try {
|
|
141
|
+
await this.acquireStrategy.onAllotmentReleased(ctx);
|
|
142
|
+
}
|
|
143
|
+
catch { }
|
|
144
|
+
try {
|
|
145
|
+
this.limitAlgorithm.addSample(startTime, rtt, currentInflight, false);
|
|
146
|
+
}
|
|
147
|
+
catch { }
|
|
148
|
+
try {
|
|
149
|
+
await this.rejectionStrategy?.onAllotmentReleased();
|
|
150
|
+
}
|
|
151
|
+
catch { }
|
|
115
152
|
},
|
|
116
153
|
releaseAndIgnore: async () => {
|
|
154
|
+
if (releaseStarted)
|
|
155
|
+
return;
|
|
156
|
+
releaseStarted = true;
|
|
117
157
|
this._inflight--;
|
|
118
|
-
await this.acquireStrategy.onAllotmentReleased(ctx);
|
|
119
158
|
this.ignoredCounter.increment();
|
|
120
|
-
|
|
159
|
+
// If one onAllotmentReleased call fails, hard to know what to do here.
|
|
160
|
+
// We're in some kind of inconsistent state, but we probably have to
|
|
161
|
+
// soldier on.
|
|
162
|
+
try {
|
|
163
|
+
await this.acquireStrategy.onAllotmentReleased(ctx);
|
|
164
|
+
}
|
|
165
|
+
catch { }
|
|
166
|
+
try {
|
|
167
|
+
await this.rejectionStrategy?.onAllotmentReleased();
|
|
168
|
+
}
|
|
169
|
+
catch { }
|
|
121
170
|
},
|
|
122
171
|
releaseAndRecordDropped: async () => {
|
|
172
|
+
if (releaseStarted)
|
|
173
|
+
return;
|
|
174
|
+
releaseStarted = true;
|
|
175
|
+
const endTime = this.clock();
|
|
176
|
+
const rtt = endTime - startTime;
|
|
123
177
|
this._inflight--;
|
|
124
|
-
await this.acquireStrategy.onAllotmentReleased(ctx);
|
|
125
178
|
this.droppedCounter.increment();
|
|
126
|
-
|
|
127
|
-
|
|
179
|
+
// If one onAllotmentReleased call fails, hard to know what to do here.
|
|
180
|
+
// We're in some kind of inconsistent state, but we probably have to
|
|
181
|
+
// soldier on.
|
|
182
|
+
try {
|
|
183
|
+
await this.acquireStrategy.onAllotmentReleased(ctx);
|
|
184
|
+
}
|
|
185
|
+
catch { }
|
|
186
|
+
try {
|
|
187
|
+
this.limitAlgorithm.addSample(startTime, rtt, currentInflight, true);
|
|
188
|
+
}
|
|
189
|
+
catch { }
|
|
190
|
+
try {
|
|
191
|
+
await this.rejectionStrategy?.onAllotmentReleased();
|
|
192
|
+
}
|
|
193
|
+
catch { }
|
|
128
194
|
},
|
|
129
195
|
};
|
|
130
196
|
}
|
|
@@ -135,32 +201,6 @@ export class Limiter {
|
|
|
135
201
|
return this._inflight;
|
|
136
202
|
}
|
|
137
203
|
}
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
// Built-in acquire strategy: Semaphore
|
|
140
|
-
// ---------------------------------------------------------------------------
|
|
141
|
-
/**
|
|
142
|
-
* Simple semaphore-based acquire strategy. Tracks a permits counter that is
|
|
143
|
-
* decremented when an allotment is taken and incremented on release. When
|
|
144
|
-
* permits reach zero, {@link tryAcquireAllotment} returns false.
|
|
145
|
-
*/
|
|
146
|
-
export class SemaphoreStrategy {
|
|
147
|
-
permits;
|
|
148
|
-
constructor(initialLimit) {
|
|
149
|
-
this.permits = initialLimit;
|
|
150
|
-
}
|
|
151
|
-
tryAcquireAllotment() {
|
|
152
|
-
if (this.permits <= 0)
|
|
153
|
-
return false;
|
|
154
|
-
this.permits--;
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
onAllotmentReleased() {
|
|
158
|
-
this.permits++;
|
|
159
|
-
}
|
|
160
|
-
onLimitChanged(oldLimit, newLimit) {
|
|
161
|
-
this.permits += newLimit - oldLimit;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
204
|
/**
|
|
165
205
|
* Creates a helper that runs callbacks under acquired limiter allotments.
|
|
166
206
|
*
|
|
@@ -169,28 +209,26 @@ export class SemaphoreStrategy {
|
|
|
169
209
|
* - On {@link RunResult} `success` / `ignore`, returns the carried value after
|
|
170
210
|
* recording success/ignore to the allotment.
|
|
171
211
|
* - On `dropped`, records drop and throws the carried error.
|
|
212
|
+
* - If callback returns a non-{@link RunResult} value, it is treated as
|
|
213
|
+
* implicit success and returned as-is.
|
|
172
214
|
* - On uncaught exceptions from `fn`, records ignore and rethrows, except for
|
|
173
215
|
* {@link AdaptiveTimeoutError}, which records drop and rethrows.
|
|
174
216
|
* - Callback receives `{ context, signal }` from acquire options.
|
|
175
217
|
*/
|
|
176
218
|
export function withLimiter(limiter) {
|
|
177
219
|
async function limited(optionsOrFn, maybeFn) {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
: optionsOrFn;
|
|
220
|
+
const [options, fn] = typeof optionsOrFn === "function"
|
|
221
|
+
? [undefined, optionsOrFn]
|
|
222
|
+
: [optionsOrFn, maybeFn];
|
|
223
|
+
if (!fn) {
|
|
224
|
+
throw new Error("No function provided");
|
|
225
|
+
}
|
|
185
226
|
const allotment = await limiter.acquire(options);
|
|
186
227
|
if (!allotment) {
|
|
187
228
|
return QuotaNotAvailable;
|
|
188
229
|
}
|
|
189
230
|
const [result] = await Promise.allSettled([
|
|
190
|
-
fn({
|
|
191
|
-
context: options?.context,
|
|
192
|
-
signal: options?.signal,
|
|
193
|
-
}),
|
|
231
|
+
fn({ context: options?.context, signal: options?.signal }),
|
|
194
232
|
]);
|
|
195
233
|
if (result.status === "rejected") {
|
|
196
234
|
if (isAdaptiveTimeoutError(result.reason)) {
|
|
@@ -202,16 +240,21 @@ export function withLimiter(limiter) {
|
|
|
202
240
|
throw result.reason;
|
|
203
241
|
}
|
|
204
242
|
const outcome = result.value;
|
|
205
|
-
|
|
243
|
+
if (!isRunResult(outcome)) {
|
|
244
|
+
await allotment.releaseAndRecordSuccess();
|
|
245
|
+
return outcome;
|
|
246
|
+
}
|
|
247
|
+
const outcomeCast = outcome;
|
|
248
|
+
switch (outcomeCast.kind) {
|
|
206
249
|
case "success":
|
|
207
250
|
await allotment.releaseAndRecordSuccess();
|
|
208
|
-
return
|
|
251
|
+
return outcomeCast.value;
|
|
209
252
|
case "ignore":
|
|
210
253
|
await allotment.releaseAndIgnore();
|
|
211
|
-
return
|
|
254
|
+
return outcomeCast.value;
|
|
212
255
|
case "dropped":
|
|
213
256
|
await allotment.releaseAndRecordDropped();
|
|
214
|
-
throw
|
|
257
|
+
throw outcomeCast.error;
|
|
215
258
|
}
|
|
216
259
|
}
|
|
217
260
|
return limited;
|
package/dist/RunResult.d.ts
CHANGED
|
@@ -14,20 +14,26 @@ export declare class AdaptiveTimeoutError extends Error {
|
|
|
14
14
|
constructor(message?: string);
|
|
15
15
|
}
|
|
16
16
|
export declare function isAdaptiveTimeoutError(error: unknown): error is AdaptiveTimeoutError;
|
|
17
|
-
|
|
17
|
+
declare const isRunResultTag: unique symbol;
|
|
18
|
+
export type RunSuccess<T> = {
|
|
18
19
|
readonly kind: "success";
|
|
20
|
+
readonly [isRunResultTag]: true;
|
|
19
21
|
readonly value: T;
|
|
20
|
-
}
|
|
21
|
-
export
|
|
22
|
+
};
|
|
23
|
+
export type RunIgnore<T> = {
|
|
22
24
|
readonly kind: "ignore";
|
|
25
|
+
readonly [isRunResultTag]: true;
|
|
23
26
|
readonly value: T;
|
|
24
|
-
}
|
|
25
|
-
export
|
|
27
|
+
};
|
|
28
|
+
export type RunDropped<E extends Error = Error> = {
|
|
26
29
|
readonly kind: "dropped";
|
|
30
|
+
readonly [isRunResultTag]: true;
|
|
27
31
|
readonly error: E;
|
|
28
|
-
}
|
|
32
|
+
};
|
|
29
33
|
export type RunResult<T, E extends Error = Error> = RunSuccess<T> | RunIgnore<T> | RunDropped<E>;
|
|
34
|
+
export declare function isRunResult<T extends unknown>(value: T): value is T & RunResult<unknown, Error>;
|
|
30
35
|
export declare function success<T>(value: T): RunSuccess<T>;
|
|
31
36
|
export declare function ignore<T>(value: T): RunIgnore<T>;
|
|
32
37
|
export declare function dropped<E extends Error>(error: E): RunDropped<E>;
|
|
38
|
+
export {};
|
|
33
39
|
//# sourceMappingURL=RunResult.d.ts.map
|
package/dist/RunResult.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunResult.d.ts","sourceRoot":"","sources":["../src/RunResult.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAE7D;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,qBAA+B;gBAEhC,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,oBAAoB,CAQ/B;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"RunResult.d.ts","sourceRoot":"","sources":["../src/RunResult.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAE7D;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,qBAA+B;gBAEhC,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,oBAAoB,CAQ/B;AAED,QAAA,MAAM,cAAc,eAAwB,CAAC;AAE7C,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,IAAI;IAChD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,KAAK,GAAG,KAAK,IAC5C,UAAU,CAAC,CAAC,CAAC,GACb,SAAS,CAAC,CAAC,CAAC,GACZ,UAAU,CAAC,CAAC,CAAC,CAAC;AAElB,wBAAgB,WAAW,CAAC,CAAC,SAAS,OAAO,EAC3C,KAAK,EAAE,CAAC,GACP,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAOxC;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAElD;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAEhD;AAED,wBAAgB,OAAO,CAAC,CAAC,SAAS,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAEhE"}
|
package/dist/RunResult.js
CHANGED
|
@@ -24,12 +24,19 @@ export function isAdaptiveTimeoutError(error) {
|
|
|
24
24
|
"code" in error &&
|
|
25
25
|
error.code === ADAPTIVE_TIMEOUT_ERROR_CODE));
|
|
26
26
|
}
|
|
27
|
+
const isRunResultTag = Symbol("isRunResult");
|
|
28
|
+
export function isRunResult(value) {
|
|
29
|
+
return (typeof value === "object" &&
|
|
30
|
+
value !== null &&
|
|
31
|
+
isRunResultTag in value &&
|
|
32
|
+
value[isRunResultTag] === true);
|
|
33
|
+
}
|
|
27
34
|
export function success(value) {
|
|
28
|
-
return { kind: "success", value };
|
|
35
|
+
return { kind: "success", [isRunResultTag]: true, value };
|
|
29
36
|
}
|
|
30
37
|
export function ignore(value) {
|
|
31
|
-
return { kind: "ignore", value };
|
|
38
|
+
return { kind: "ignore", [isRunResultTag]: true, value };
|
|
32
39
|
}
|
|
33
40
|
export function dropped(error) {
|
|
34
|
-
return { kind: "dropped", error };
|
|
41
|
+
return { kind: "dropped", [isRunResultTag]: true, error };
|
|
35
42
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export type { AdaptiveLimit } from "./limit/StreamingLimit.js";
|
|
2
2
|
export type { LimitAllotment } from "./LimitAllotment.js";
|
|
3
|
-
export { Limiter,
|
|
3
|
+
export { Limiter, withLimiter, type AcquireOptions, type AcquireResult, type AcquireStrategy, type AllotmentUnavailableStrategy, type LimitedFunction, type LimiterOptions, type LimiterState, type MaybePromise, type RunCallbackArgs, } from "./Limiter.js";
|
|
4
4
|
export { ListenerSet } from "./ListenerSet.js";
|
|
5
5
|
export { MetricIds, NoopMetricRegistry, type Counter, type DistributionMetric, type GaugeMetric, type MetricRegistry, } from "./MetricRegistry.js";
|
|
6
|
-
export { AdaptiveTimeoutError, dropped, ignore, isAdaptiveTimeoutError, QuotaNotAvailable, success, type RunDropped, type RunIgnore, type RunResult, type RunSuccess, } from "./RunResult.js";
|
|
6
|
+
export { AdaptiveTimeoutError, dropped, ignore, isAdaptiveTimeoutError, isRunResult, QuotaNotAvailable, success, type RunDropped, type RunIgnore, type RunResult, type RunSuccess, } from "./RunResult.js";
|
|
7
7
|
export { AIMDLimit, type AIMDLimitOptions } from "./limit/AIMDLimit.js";
|
|
8
8
|
export { FixedLimit } from "./limit/FixedLimit.js";
|
|
9
9
|
export { GradientLimit, type Gradient2LimitOptions, } from "./limit/GradientLimit.js";
|
|
@@ -20,6 +20,7 @@ export type { SampleWindow } from "./limit/window/SampleWindow.js";
|
|
|
20
20
|
export { squareRoot, squareRootWithBaseline } from "./utils/index.js";
|
|
21
21
|
export * from "./limiter/factories/index.js";
|
|
22
22
|
export { PartitionedStrategy, type PartitionConfig, } from "./limiter/acquire-strategies/PartitionedStrategy.js";
|
|
23
|
+
export { SemaphoreStrategy } from "./limiter/acquire-strategies/SemaphoreStrategy.js";
|
|
23
24
|
export { DelayedRejectStrategy, type DelayedRejectStrategyOptions, } from "./limiter/allocation-unavailable-strategies/DelayedRejectStrategy.js";
|
|
24
25
|
export { DelayedThenBlockingRejection } from "./limiter/allocation-unavailable-strategies/DelayedThenBlockingRejection.js";
|
|
25
26
|
export { FifoBlockingRejection } from "./limiter/allocation-unavailable-strategies/FifoBlockingRejection.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EACL,OAAO,EACP,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EACL,OAAO,EACP,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,oBAAoB,EACpB,OAAO,EACP,MAAM,EACN,sBAAsB,EACtB,WAAW,EACX,iBAAiB,EACjB,OAAO,EACP,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACL,aAAa,EACb,KAAK,qBAAqB,GAC3B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,GAC1B,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,YAAY,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAG7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,4BAA4B,EAAE,MAAM,0CAA0C,CAAC;AACxF,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,cAAc,8BAA8B,CAAC;AAG7C,OAAO,EACL,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mDAAmD,CAAC;AAGtF,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,sEAAsE,CAAC;AAC9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,6EAA6E,CAAC;AAC3H,OAAO,EAAE,qBAAqB,EAAE,MAAM,sEAAsE,CAAC;AAC7G,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,sEAAsE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { Limiter,
|
|
1
|
+
export { Limiter, withLimiter, } from "./Limiter.js";
|
|
2
2
|
export { ListenerSet } from "./ListenerSet.js";
|
|
3
3
|
export { MetricIds, NoopMetricRegistry, } from "./MetricRegistry.js";
|
|
4
|
-
export { AdaptiveTimeoutError, dropped, ignore, isAdaptiveTimeoutError, QuotaNotAvailable, success, } from "./RunResult.js";
|
|
4
|
+
export { AdaptiveTimeoutError, dropped, ignore, isAdaptiveTimeoutError, isRunResult, QuotaNotAvailable, success, } from "./RunResult.js";
|
|
5
5
|
// Limit algorithms
|
|
6
6
|
export { AIMDLimit } from "./limit/AIMDLimit.js";
|
|
7
7
|
export { FixedLimit } from "./limit/FixedLimit.js";
|
|
@@ -18,9 +18,10 @@ export { makeAverageSampleWindow } from "./limit/window/AverageSampleWindow.js";
|
|
|
18
18
|
export { createPercentileSampleWindow } from "./limit/window/PercentileSampleWindow.js";
|
|
19
19
|
// Limit functions
|
|
20
20
|
export { squareRoot, squareRootWithBaseline } from "./utils/index.js";
|
|
21
|
-
// Acquire strategies
|
|
22
21
|
export * from "./limiter/factories/index.js";
|
|
22
|
+
// Acquire strategies
|
|
23
23
|
export { PartitionedStrategy, } from "./limiter/acquire-strategies/PartitionedStrategy.js";
|
|
24
|
+
export { SemaphoreStrategy } from "./limiter/acquire-strategies/SemaphoreStrategy.js";
|
|
24
25
|
// Rejection strategies
|
|
25
26
|
export { DelayedRejectStrategy, } from "./limiter/allocation-unavailable-strategies/DelayedRejectStrategy.js";
|
|
26
27
|
export { DelayedThenBlockingRejection } from "./limiter/allocation-unavailable-strategies/DelayedThenBlockingRejection.js";
|
|
@@ -6,6 +6,25 @@ export type PartitionConfig = {
|
|
|
6
6
|
* [0.0, 1.0].
|
|
7
7
|
*/
|
|
8
8
|
percent: number;
|
|
9
|
+
/**
|
|
10
|
+
* Controls whether this partition may exceed its limit-at-global-saturation
|
|
11
|
+
* while global inflight is still below the global limit.
|
|
12
|
+
*
|
|
13
|
+
* - `{ kind: "unbounded" }` (default): current behavior; any partition may
|
|
14
|
+
* consume all global slack.
|
|
15
|
+
* - `{ kind: "capped", maxBurstMultiplier }`: partition may burst, but only
|
|
16
|
+
* up to `ceil(limitAtGlobalSaturation * maxBurstMultiplier)`.
|
|
17
|
+
* - `{ kind: "none" }`: no bursting; partition cannot exceed its
|
|
18
|
+
* `limitAtGlobalSaturation`, even when global slack exists.
|
|
19
|
+
*/
|
|
20
|
+
burstMode?: {
|
|
21
|
+
kind: "unbounded";
|
|
22
|
+
} | {
|
|
23
|
+
kind: "capped";
|
|
24
|
+
maxBurstMultiplier: number;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "none";
|
|
27
|
+
} | undefined;
|
|
9
28
|
};
|
|
10
29
|
/**
|
|
11
30
|
* ## Partitioned acquire strategy (guaranteed share + bursting)
|
|
@@ -16,9 +35,12 @@ export type PartitionConfig = {
|
|
|
16
35
|
* max(1, ceil(globalLimit * percent))`.
|
|
17
36
|
*
|
|
18
37
|
* Admission policy:
|
|
19
|
-
* - If `globalInflight < globalLimit`,
|
|
20
|
-
*
|
|
21
|
-
* exists
|
|
38
|
+
* - If `globalInflight < globalLimit`, admission is controlled by partition
|
|
39
|
+
* burst policy:
|
|
40
|
+
* - `unbounded` (default): always admit while global slack exists.
|
|
41
|
+
* - `capped`: admit up to
|
|
42
|
+
* `ceil(limitAtGlobalSaturation * maxBurstMultiplier)`.
|
|
43
|
+
* - `none`: admit only up to `limitAtGlobalSaturation`.
|
|
22
44
|
*
|
|
23
45
|
* - If `globalInflight >= globalLimit`, admission is checked against the
|
|
24
46
|
* resolved partition's limitAtGlobalSaturation only (`tryAcquire` on that
|
|
@@ -30,8 +52,7 @@ export type PartitionConfig = {
|
|
|
30
52
|
*
|
|
31
53
|
* Examples (global limit = 10, A=50%, B=50% => limitsAtGlobalSaturation: A=5,
|
|
32
54
|
* B=5):
|
|
33
|
-
* - **
|
|
34
|
-
* admissions do not enforce per-partition limits at global saturation).
|
|
55
|
+
* - **Unbounded bursting:** A can reach inflight 10 while B is 0.
|
|
35
56
|
*
|
|
36
57
|
* - **Limit-at-global-saturation catch-up at saturation:** if A=10 and B=0,
|
|
37
58
|
* then B requests can still be admitted up to B=5 via `tryAcquire`, so global
|
|
@@ -81,6 +102,14 @@ export declare class PartitionedStrategy<ContextT, PartitionName extends string
|
|
|
81
102
|
getPartition(name: PartitionName): {
|
|
82
103
|
name: PartitionName;
|
|
83
104
|
percent: number;
|
|
105
|
+
burstMode: {
|
|
106
|
+
kind: "unbounded";
|
|
107
|
+
} | {
|
|
108
|
+
kind: "capped";
|
|
109
|
+
maxBurstMultiplier: number;
|
|
110
|
+
} | {
|
|
111
|
+
kind: "none";
|
|
112
|
+
};
|
|
84
113
|
limitAtGlobalSaturation: number;
|
|
85
114
|
inFlight: number;
|
|
86
115
|
isLimitAtGlobalSaturationExceeded: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PartitionedStrategy.d.ts","sourceRoot":"","sources":["../../../src/limiter/acquire-strategies/PartitionedStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,yBAAyB,CAAC;AAKjC,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"PartitionedStrategy.d.ts","sourceRoot":"","sources":["../../../src/limiter/acquire-strategies/PartitionedStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,yBAAyB,CAAC;AAKjC,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EACN;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,GACrB;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,GAC9C;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAChB,SAAS,CAAC;CACf,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,qBAAa,mBAAmB,CAC9B,QAAQ,EACR,aAAa,SAAS,MAAM,GAAG,MAAM,CACrC,YAAW,eAAe,CAAC,QAAQ,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAG9B;IACF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAuB;IACxD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAEH;gBAEnB,OAAO,EAAE;QACnB;;;WAGG;QACH,YAAY,EAAE,MAAM,CAAC;QAErB;;;;;WAKG;QACH,iBAAiB,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,aAAa,GAAG,SAAS,CAAC;QACpE,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;QAEnD,cAAc,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;KAC7C;IAmDD,mBAAmB,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO;IASpE,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI;IAI5C,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMzD;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,aAAa;;;;kBAuCpB,WAAW;;kBACX,QAAQ;gCAAsB,MAAM;;kBACpC,MAAM;;;;;;IAzBlB,OAAO,CAAC,gBAAgB;CAUzB"}
|