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.
Files changed (157) hide show
  1. package/dist/Limit.d.ts +29 -0
  2. package/dist/Limit.d.ts.map +1 -0
  3. package/dist/Limit.js +1 -0
  4. package/dist/LimitAllotment.d.ts +23 -0
  5. package/dist/LimitAllotment.d.ts.map +1 -0
  6. package/dist/LimitAllotment.js +1 -0
  7. package/dist/Limiter.d.ts +175 -0
  8. package/dist/Limiter.d.ts.map +1 -0
  9. package/dist/Limiter.js +240 -0
  10. package/dist/Listener.d.ts +23 -0
  11. package/dist/Listener.d.ts.map +1 -0
  12. package/dist/Listener.js +1 -0
  13. package/dist/ListenerSet.d.ts +12 -0
  14. package/dist/ListenerSet.d.ts.map +1 -0
  15. package/dist/ListenerSet.js +35 -0
  16. package/dist/MetricIds.d.ts +13 -0
  17. package/dist/MetricIds.d.ts.map +1 -0
  18. package/dist/MetricIds.js +12 -0
  19. package/dist/MetricRegistry.d.ts +66 -0
  20. package/dist/MetricRegistry.d.ts.map +1 -0
  21. package/dist/MetricRegistry.js +30 -0
  22. package/dist/RunResult.d.ts +33 -0
  23. package/dist/RunResult.d.ts.map +1 -0
  24. package/dist/RunResult.js +35 -0
  25. package/dist/StreamingLimit.d.ts +26 -0
  26. package/dist/StreamingLimit.d.ts.map +1 -0
  27. package/dist/StreamingLimit.js +1 -0
  28. package/dist/executors/AdaptiveExecutor.d.ts +50 -0
  29. package/dist/executors/AdaptiveExecutor.d.ts.map +1 -0
  30. package/dist/executors/AdaptiveExecutor.js +80 -0
  31. package/dist/index.d.ts +27 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +28 -0
  34. package/dist/limit/AIMDLimit.d.ts +37 -0
  35. package/dist/limit/AIMDLimit.d.ts.map +1 -0
  36. package/dist/limit/AIMDLimit.js +49 -0
  37. package/dist/limit/FixedLimit.d.ts +15 -0
  38. package/dist/limit/FixedLimit.d.ts.map +1 -0
  39. package/dist/limit/FixedLimit.js +23 -0
  40. package/dist/limit/Gradient2Limit.d.ts +122 -0
  41. package/dist/limit/Gradient2Limit.d.ts.map +1 -0
  42. package/dist/limit/Gradient2Limit.js +107 -0
  43. package/dist/limit/GradientLimit.d.ts +122 -0
  44. package/dist/limit/GradientLimit.d.ts.map +1 -0
  45. package/dist/limit/GradientLimit.js +108 -0
  46. package/dist/limit/SettableLimit.d.ts +18 -0
  47. package/dist/limit/SettableLimit.d.ts.map +1 -0
  48. package/dist/limit/SettableLimit.js +30 -0
  49. package/dist/limit/StreamingLimit.d.ts +26 -0
  50. package/dist/limit/StreamingLimit.d.ts.map +1 -0
  51. package/dist/limit/StreamingLimit.js +1 -0
  52. package/dist/limit/TracingLimitDecorator.d.ts +16 -0
  53. package/dist/limit/TracingLimitDecorator.d.ts.map +1 -0
  54. package/dist/limit/TracingLimitDecorator.js +23 -0
  55. package/dist/limit/VegasLimit.d.ts +85 -0
  56. package/dist/limit/VegasLimit.d.ts.map +1 -0
  57. package/dist/limit/VegasLimit.js +127 -0
  58. package/dist/limit/WindowedLimit.d.ts +48 -0
  59. package/dist/limit/WindowedLimit.d.ts.map +1 -0
  60. package/dist/limit/WindowedLimit.js +67 -0
  61. package/dist/limit/statistics/ExpMovingAverage.d.ts +21 -0
  62. package/dist/limit/statistics/ExpMovingAverage.d.ts.map +1 -0
  63. package/dist/limit/statistics/ExpMovingAverage.js +43 -0
  64. package/dist/limit/statistics/Minimum.d.ts +12 -0
  65. package/dist/limit/statistics/Minimum.d.ts.map +1 -0
  66. package/dist/limit/statistics/Minimum.js +22 -0
  67. package/dist/limit/statistics/MinimumValue.d.ts +12 -0
  68. package/dist/limit/statistics/MinimumValue.d.ts.map +1 -0
  69. package/dist/limit/statistics/MinimumValue.js +22 -0
  70. package/dist/limit/statistics/SingleMeasurement.d.ts +12 -0
  71. package/dist/limit/statistics/SingleMeasurement.d.ts.map +1 -0
  72. package/dist/limit/statistics/SingleMeasurement.js +21 -0
  73. package/dist/limit/statistics/StreamingStatistic.d.ts +29 -0
  74. package/dist/limit/statistics/StreamingStatistic.d.ts.map +1 -0
  75. package/dist/limit/statistics/StreamingStatistic.js +1 -0
  76. package/dist/limit/utils/index.d.ts +10 -0
  77. package/dist/limit/utils/index.d.ts.map +1 -0
  78. package/dist/limit/utils/index.js +19 -0
  79. package/dist/limit/window/AverageSampleWindow.d.ts +4 -0
  80. package/dist/limit/window/AverageSampleWindow.d.ts.map +1 -0
  81. package/dist/limit/window/AverageSampleWindow.js +46 -0
  82. package/dist/limit/window/PercentileSampleWindow.d.ts +38 -0
  83. package/dist/limit/window/PercentileSampleWindow.d.ts.map +1 -0
  84. package/dist/limit/window/PercentileSampleWindow.js +81 -0
  85. package/dist/limit/window/SampleWindow.d.ts +30 -0
  86. package/dist/limit/window/SampleWindow.d.ts.map +1 -0
  87. package/dist/limit/window/SampleWindow.js +1 -0
  88. package/dist/limiter/AbstractLimiter.d.ts +48 -0
  89. package/dist/limiter/AbstractLimiter.d.ts.map +1 -0
  90. package/dist/limiter/AbstractLimiter.js +78 -0
  91. package/dist/limiter/AbstractPartitionedLimiter.d.ts +66 -0
  92. package/dist/limiter/AbstractPartitionedLimiter.d.ts.map +1 -0
  93. package/dist/limiter/AbstractPartitionedLimiter.js +209 -0
  94. package/dist/limiter/BlockingLimiter.d.ts +55 -0
  95. package/dist/limiter/BlockingLimiter.d.ts.map +1 -0
  96. package/dist/limiter/BlockingLimiter.js +111 -0
  97. package/dist/limiter/DelayedRejectStrategy.d.ts +32 -0
  98. package/dist/limiter/DelayedRejectStrategy.d.ts.map +1 -0
  99. package/dist/limiter/DelayedRejectStrategy.js +60 -0
  100. package/dist/limiter/DelayedThenBlockingRejection.d.ts +19 -0
  101. package/dist/limiter/DelayedThenBlockingRejection.d.ts.map +1 -0
  102. package/dist/limiter/DelayedThenBlockingRejection.js +26 -0
  103. package/dist/limiter/FifoBlockingRejection.d.ts +26 -0
  104. package/dist/limiter/FifoBlockingRejection.d.ts.map +1 -0
  105. package/dist/limiter/FifoBlockingRejection.js +77 -0
  106. package/dist/limiter/LifoBlockingLimiter.d.ts +53 -0
  107. package/dist/limiter/LifoBlockingLimiter.d.ts.map +1 -0
  108. package/dist/limiter/LifoBlockingLimiter.js +108 -0
  109. package/dist/limiter/LifoBlockingRejection.d.ts +31 -0
  110. package/dist/limiter/LifoBlockingRejection.d.ts.map +1 -0
  111. package/dist/limiter/LifoBlockingRejection.js +63 -0
  112. package/dist/limiter/PartitionedStrategy.d.ts +90 -0
  113. package/dist/limiter/PartitionedStrategy.d.ts.map +1 -0
  114. package/dist/limiter/PartitionedStrategy.js +183 -0
  115. package/dist/limiter/SimpleLimiter.d.ts +31 -0
  116. package/dist/limiter/SimpleLimiter.d.ts.map +1 -0
  117. package/dist/limiter/SimpleLimiter.js +119 -0
  118. package/dist/limiter/factories/index.d.ts +7 -0
  119. package/dist/limiter/factories/index.d.ts.map +1 -0
  120. package/dist/limiter/factories/index.js +6 -0
  121. package/dist/limiter/factories/makeBlockingLimiter.d.ts +6 -0
  122. package/dist/limiter/factories/makeBlockingLimiter.d.ts.map +1 -0
  123. package/dist/limiter/factories/makeBlockingLimiter.js +8 -0
  124. package/dist/limiter/factories/makeLifoBlockingLimiter.d.ts +8 -0
  125. package/dist/limiter/factories/makeLifoBlockingLimiter.d.ts.map +1 -0
  126. package/dist/limiter/factories/makeLifoBlockingLimiter.js +15 -0
  127. package/dist/limiter/factories/makePartitionedBlockingLimiter.d.ts +12 -0
  128. package/dist/limiter/factories/makePartitionedBlockingLimiter.d.ts.map +1 -0
  129. package/dist/limiter/factories/makePartitionedBlockingLimiter.js +35 -0
  130. package/dist/limiter/factories/makePartitionedLifoBlockingLimiter.d.ts +14 -0
  131. package/dist/limiter/factories/makePartitionedLifoBlockingLimiter.d.ts.map +1 -0
  132. package/dist/limiter/factories/makePartitionedLifoBlockingLimiter.js +38 -0
  133. package/dist/limiter/factories/makePartitionedLimiter.d.ts +11 -0
  134. package/dist/limiter/factories/makePartitionedLimiter.d.ts.map +1 -0
  135. package/dist/limiter/factories/makePartitionedLimiter.js +30 -0
  136. package/dist/limiter/factories/makeSimpleLimiter.d.ts +3 -0
  137. package/dist/limiter/factories/makeSimpleLimiter.d.ts.map +1 -0
  138. package/dist/limiter/factories/makeSimpleLimiter.js +9 -0
  139. package/dist/limiter/factories.d.ts +31 -0
  140. package/dist/limiter/factories.d.ts.map +1 -0
  141. package/dist/limiter/factories.js +74 -0
  142. package/dist/statistics/ExpMovingAverage.d.ts +21 -0
  143. package/dist/statistics/ExpMovingAverage.d.ts.map +1 -0
  144. package/dist/statistics/ExpMovingAverage.js +43 -0
  145. package/dist/statistics/MinimumValue.d.ts +12 -0
  146. package/dist/statistics/MinimumValue.d.ts.map +1 -0
  147. package/dist/statistics/MinimumValue.js +22 -0
  148. package/dist/statistics/MostRecentValue.d.ts +12 -0
  149. package/dist/statistics/MostRecentValue.d.ts.map +1 -0
  150. package/dist/statistics/MostRecentValue.js +21 -0
  151. package/dist/statistics/StreamingStatistic.d.ts +29 -0
  152. package/dist/statistics/StreamingStatistic.d.ts.map +1 -0
  153. package/dist/statistics/StreamingStatistic.js +1 -0
  154. package/dist/utils/index.d.ts +10 -0
  155. package/dist/utils/index.d.ts.map +1 -0
  156. package/dist/utils/index.js +19 -0
  157. package/package.json +31 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricRegistry.d.ts","sourceRoot":"","sources":["../src/MetricRegistry.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;;CAQZ,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,SAAS,IAAI,IAAI,CAAC;CACnB;AAED,0FAA0F;AAC1F,MAAM,WAAW,WAAW;CAAG;AAE/B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC;IAE7E;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,MAAM,EAAE,GAAG,iBAAiB,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC;IAEvF;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;CAClE;AAMD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAUhC,CAAC"}
@@ -0,0 +1,30 @@
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
+ };
13
+ const NOOP_SAMPLE_LISTENER = { addSample() { } };
14
+ const NOOP_COUNTER = { increment() { } };
15
+ const NOOP_GAUGE = {};
16
+ /**
17
+ * No-op MetricRegistry that discards all metrics. Used as the default when
18
+ * no registry is configured.
19
+ */
20
+ export const NoopMetricRegistry = {
21
+ distribution() {
22
+ return NOOP_SAMPLE_LISTENER;
23
+ },
24
+ gauge() {
25
+ return NOOP_GAUGE;
26
+ },
27
+ counter() {
28
+ return NOOP_COUNTER;
29
+ },
30
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Returned when {@link Limiter.run} cannot obtain an allotment (sync reject or
3
+ * async wait exhausted / aborted). Distinct from {@link runDropped}, which
4
+ * represents work that ran and then reported a drop.
5
+ */
6
+ export declare const QuotaNotAvailable: unique symbol;
7
+ /**
8
+ * Error type that signals the operation was dropped due to timeout or external
9
+ * load shedding. When thrown from {@link Limiter.run}'s callback, run treats
10
+ * it as dropped (calls `reportDropped`) and then rethrows it.
11
+ */
12
+ export declare class AdaptiveTimeoutError extends Error {
13
+ readonly code: "ADAPTIVE_TIMEOUT";
14
+ constructor(message?: string);
15
+ }
16
+ export declare function isAdaptiveTimeoutError(error: unknown): error is AdaptiveTimeoutError;
17
+ export interface RunSuccess<T> {
18
+ readonly kind: "success";
19
+ readonly value: T;
20
+ }
21
+ export interface RunIgnore<T> {
22
+ readonly kind: "ignore";
23
+ readonly value: T;
24
+ }
25
+ export interface RunDropped<E extends Error = Error> {
26
+ readonly kind: "dropped";
27
+ readonly error: E;
28
+ }
29
+ export type RunResult<T, E extends Error = Error> = RunSuccess<T> | RunIgnore<T> | RunDropped<E>;
30
+ export declare function success<T>(value: T): RunSuccess<T>;
31
+ export declare function ignore<T>(value: T): RunIgnore<T>;
32
+ export declare function dropped<E extends Error>(error: E): RunDropped<E>;
33
+ //# sourceMappingURL=RunResult.d.ts.map
@@ -0,0 +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,WAAW,UAAU,CAAC,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED,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,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"}
@@ -0,0 +1,35 @@
1
+ const ADAPTIVE_TIMEOUT_ERROR_CODE = "ADAPTIVE_TIMEOUT";
2
+ /**
3
+ * Returned when {@link Limiter.run} cannot obtain an allotment (sync reject or
4
+ * async wait exhausted / aborted). Distinct from {@link runDropped}, which
5
+ * represents work that ran and then reported a drop.
6
+ */
7
+ export const QuotaNotAvailable = Symbol("QuotaNotAvailable");
8
+ /**
9
+ * Error type that signals the operation was dropped due to timeout or external
10
+ * load shedding. When thrown from {@link Limiter.run}'s callback, run treats
11
+ * it as dropped (calls `reportDropped`) and then rethrows it.
12
+ */
13
+ export class AdaptiveTimeoutError extends Error {
14
+ code = ADAPTIVE_TIMEOUT_ERROR_CODE;
15
+ constructor(message) {
16
+ super(message ?? "Operation timed out");
17
+ this.name = "AdaptiveTimeoutError";
18
+ }
19
+ }
20
+ export function isAdaptiveTimeoutError(error) {
21
+ return (error instanceof AdaptiveTimeoutError ||
22
+ (typeof error === "object" &&
23
+ error !== null &&
24
+ "code" in error &&
25
+ error.code === ADAPTIVE_TIMEOUT_ERROR_CODE));
26
+ }
27
+ export function success(value) {
28
+ return { kind: "success", value };
29
+ }
30
+ export function ignore(value) {
31
+ return { kind: "ignore", value };
32
+ }
33
+ export function dropped(error) {
34
+ return { kind: "dropped", error };
35
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Contract for an algorithm that maintains a concurrency limit from a stream
3
+ * of RTT samples, analogous to StreamingStatistic for numeric sample streams.
4
+ */
5
+ export interface StreamingLimit {
6
+ /** Current estimated concurrency limit. */
7
+ get currentLimit(): number;
8
+ /**
9
+ * Subscribe to limit changes. The callback runs whenever the limit updates.
10
+ *
11
+ * Returns a function to unsubscribe. Optional AbortSignal support is
12
+ * provided for ergonomic cancellation.
13
+ */
14
+ subscribe(consumer: (newLimit: number) => void, options?: {
15
+ signal?: AbortSignal;
16
+ }): () => void;
17
+ /**
18
+ * Adjust the estimated limit using a completed request sample.
19
+ * @param startTime Start time in fractional milliseconds (from performance.now())
20
+ * @param rtt Round trip time in fractional milliseconds
21
+ * @param inflight Number of inflight requests at the time the request started
22
+ * @param didDrop Whether the request was dropped (timeout or rejection)
23
+ */
24
+ accumulateSample(startTime: number, rtt: number, inflight: number, didDrop: boolean): void;
25
+ }
26
+ //# sourceMappingURL=StreamingLimit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamingLimit.d.ts","sourceRoot":"","sources":["../src/StreamingLimit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,2CAA2C;IAC3C,IAAI,YAAY,IAAI,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,SAAS,CACP,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,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC5F"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,50 @@
1
+ import type { AcquireResult } from "../Limiter.js";
2
+ /**
3
+ * Error type that represents an operation timeout or external drop event.
4
+ */
5
+ export declare class AdaptiveTimeoutError extends Error {
6
+ readonly code: "ADAPTIVE_TIMEOUT";
7
+ constructor(message?: string);
8
+ }
9
+ /**
10
+ * @deprecated Use AdaptiveTimeoutError instead.
11
+ */
12
+ export declare class UncheckedTimeoutError extends AdaptiveTimeoutError {
13
+ constructor(message?: string);
14
+ }
15
+ export declare function isAdaptiveTimeoutError(error: unknown): error is AdaptiveTimeoutError;
16
+ /**
17
+ * Error type used when work is rejected because no execution slot is available.
18
+ */
19
+ export declare class ConcurrencyLimitExceededError extends Error {
20
+ constructor(message?: string);
21
+ }
22
+ export interface AdaptiveExecutorOptions {
23
+ /**
24
+ * Limiter to gate concurrent executions. Must have an `acquire()` method
25
+ * that returns an AcquireResult. Default: Limiter with AIMDLimit.
26
+ */
27
+ limiter?: {
28
+ acquire(): AcquireResult;
29
+ };
30
+ }
31
+ /**
32
+ * Executor which uses a Limiter to determine the concurrency level.
33
+ * Any function submitted once the limit has been reached will be rejected.
34
+ *
35
+ * Operations submitted to this executor should be homogeneous and have similar
36
+ * long term latency characteristics. RTT samples will only be taken from
37
+ * successful operations. The function should throw an
38
+ * {@link AdaptiveTimeoutError} if a request timed out or some external limit
39
+ * was reached. All other exceptions will be ignored (treated as reportIgnore).
40
+ */
41
+ export declare class AdaptiveExecutor {
42
+ private readonly limiter;
43
+ constructor(options?: AdaptiveExecutorOptions);
44
+ /**
45
+ * Execute an async function, gated by the adaptive concurrency limit.
46
+ * Throws if the limit has been reached and no token can be acquired.
47
+ */
48
+ execute<T>(fn: () => Promise<T>): Promise<T>;
49
+ }
50
+ //# sourceMappingURL=AdaptiveExecutor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdaptiveExecutor.d.ts","sourceRoot":"","sources":["../../src/executors/AdaptiveExecutor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAKnD;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,qBAA+B;gBAEhC,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,oBAAoB;gBACjD,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAQpF;AAED;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE;QAAE,OAAO,IAAI,aAAa,CAAA;KAAE,CAAC;CACxC;AAED;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;gBAE3C,OAAO,GAAE,uBAA4B;IAMjD;;;OAGG;IACG,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAmBnD"}
@@ -0,0 +1,80 @@
1
+ import { Limiter } from "../Limiter.js";
2
+ import { AIMDLimit } from "../limit/AIMDLimit.js";
3
+ const ADAPTIVE_TIMEOUT_ERROR_CODE = "ADAPTIVE_TIMEOUT";
4
+ /**
5
+ * Error type that represents an operation timeout or external drop event.
6
+ */
7
+ export class AdaptiveTimeoutError extends Error {
8
+ code = ADAPTIVE_TIMEOUT_ERROR_CODE;
9
+ constructor(message) {
10
+ super(message ?? "Operation timed out");
11
+ this.name = "AdaptiveTimeoutError";
12
+ }
13
+ }
14
+ /**
15
+ * @deprecated Use AdaptiveTimeoutError instead.
16
+ */
17
+ export class UncheckedTimeoutError extends AdaptiveTimeoutError {
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = "UncheckedTimeoutError";
21
+ }
22
+ }
23
+ export function isAdaptiveTimeoutError(error) {
24
+ return (error instanceof AdaptiveTimeoutError ||
25
+ (typeof error === "object" &&
26
+ error !== null &&
27
+ "code" in error &&
28
+ error.code === ADAPTIVE_TIMEOUT_ERROR_CODE));
29
+ }
30
+ /**
31
+ * Error type used when work is rejected because no execution slot is available.
32
+ */
33
+ export class ConcurrencyLimitExceededError extends Error {
34
+ constructor(message) {
35
+ super(message ?? "Concurrency limit exceeded");
36
+ this.name = "ConcurrencyLimitExceededError";
37
+ }
38
+ }
39
+ /**
40
+ * Executor which uses a Limiter to determine the concurrency level.
41
+ * Any function submitted once the limit has been reached will be rejected.
42
+ *
43
+ * Operations submitted to this executor should be homogeneous and have similar
44
+ * long term latency characteristics. RTT samples will only be taken from
45
+ * successful operations. The function should throw an
46
+ * {@link AdaptiveTimeoutError} if a request timed out or some external limit
47
+ * was reached. All other exceptions will be ignored (treated as reportIgnore).
48
+ */
49
+ export class AdaptiveExecutor {
50
+ limiter;
51
+ constructor(options = {}) {
52
+ this.limiter =
53
+ options.limiter ??
54
+ new Limiter({ limit: new AIMDLimit() });
55
+ }
56
+ /**
57
+ * Execute an async function, gated by the adaptive concurrency limit.
58
+ * Throws if the limit has been reached and no token can be acquired.
59
+ */
60
+ async execute(fn) {
61
+ const allotment = await this.limiter.acquire();
62
+ if (!allotment) {
63
+ throw new ConcurrencyLimitExceededError();
64
+ }
65
+ try {
66
+ const result = await fn();
67
+ allotment.reportSuccess();
68
+ return result;
69
+ }
70
+ catch (e) {
71
+ if (isAdaptiveTimeoutError(e)) {
72
+ allotment.reportDropped();
73
+ }
74
+ else {
75
+ allotment.reportIgnore();
76
+ }
77
+ throw e;
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,27 @@
1
+ export type { AdaptiveLimit } from "./limit/StreamingLimit.js";
2
+ export type { LimitAllotment } from "./LimitAllotment.js";
3
+ export { Limiter, SemaphoreStrategy, whenAcquireSettled, withLimiter, type AcquireOptions, type AcquireResult, type AcquireStrategy, type AllotmentUnavailableStrategy, type AsyncAcquireResult, type LimitedFunction, type LimiterOptions, type LimiterState, type RunCallbackArgs, type SyncAcquireResult, type SyncLimiter, } from "./Limiter.js";
4
+ export { ListenerSet } from "./ListenerSet.js";
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";
7
+ export { AIMDLimit, type AIMDLimitOptions } from "./limit/AIMDLimit.js";
8
+ export { FixedLimit } from "./limit/FixedLimit.js";
9
+ export { GradientLimit, type Gradient2LimitOptions, } from "./limit/GradientLimit.js";
10
+ export { SettableLimit } from "./limit/SettableLimit.js";
11
+ export { TracingLimitDecorator } from "./limit/TracingLimitDecorator.js";
12
+ export { VegasLimit, type VegasLimitOptions, type VegasLimitPolicy, } from "./limit/VegasLimit.js";
13
+ export { WindowedLimit, type WindowedLimitOptions, } from "./limit/WindowedLimit.js";
14
+ export { ExpMovingAverage } from "./statistics/ExpMovingAverage.js";
15
+ export { MinimumValue } from "./statistics/MinimumValue.js";
16
+ export type { StreamingStatistic } from "./statistics/StreamingStatistic.js";
17
+ export { makeAverageSampleWindow } from "./limit/window/AverageSampleWindow.js";
18
+ export { createPercentileSampleWindow } from "./limit/window/PercentileSampleWindow.js";
19
+ export type { SampleWindow } from "./limit/window/SampleWindow.js";
20
+ export { squareRoot, squareRootWithBaseline } from "./utils/index.js";
21
+ export * from "./limiter/factories/index.js";
22
+ export { PartitionedStrategy, type PartitionConfig, } from "./limiter/PartitionedStrategy.js";
23
+ export { DelayedRejectStrategy, type DelayedRejectStrategyOptions, } from "./limiter/DelayedRejectStrategy.js";
24
+ export { DelayedThenBlockingRejection } from "./limiter/DelayedThenBlockingRejection.js";
25
+ export { FifoBlockingRejection } from "./limiter/FifoBlockingRejection.js";
26
+ export { LifoBlockingRejection, type LifoBlockingRejectionOptions, } from "./limiter/LifoBlockingRejection.js";
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,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,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;AAGtE,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EACL,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,4BAA4B,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,oCAAoC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ export { Limiter, SemaphoreStrategy, whenAcquireSettled, withLimiter, } from "./Limiter.js";
2
+ export { ListenerSet } from "./ListenerSet.js";
3
+ export { MetricIds, NoopMetricRegistry, } from "./MetricRegistry.js";
4
+ export { AdaptiveTimeoutError, dropped, ignore, isAdaptiveTimeoutError, QuotaNotAvailable, success, } from "./RunResult.js";
5
+ // Limit algorithms
6
+ export { AIMDLimit } from "./limit/AIMDLimit.js";
7
+ export { FixedLimit } from "./limit/FixedLimit.js";
8
+ export { GradientLimit, } from "./limit/GradientLimit.js";
9
+ export { SettableLimit } from "./limit/SettableLimit.js";
10
+ export { TracingLimitDecorator } from "./limit/TracingLimitDecorator.js";
11
+ export { VegasLimit, } from "./limit/VegasLimit.js";
12
+ export { WindowedLimit, } from "./limit/WindowedLimit.js";
13
+ // Streaming statistics
14
+ export { ExpMovingAverage } from "./statistics/ExpMovingAverage.js";
15
+ export { MinimumValue } from "./statistics/MinimumValue.js";
16
+ // Sample window types
17
+ export { makeAverageSampleWindow } from "./limit/window/AverageSampleWindow.js";
18
+ export { createPercentileSampleWindow } from "./limit/window/PercentileSampleWindow.js";
19
+ // Limit functions
20
+ export { squareRoot, squareRootWithBaseline } from "./utils/index.js";
21
+ // Acquire strategies
22
+ export * from "./limiter/factories/index.js";
23
+ export { PartitionedStrategy, } from "./limiter/PartitionedStrategy.js";
24
+ // Rejection strategies
25
+ export { DelayedRejectStrategy, } from "./limiter/DelayedRejectStrategy.js";
26
+ export { DelayedThenBlockingRejection } from "./limiter/DelayedThenBlockingRejection.js";
27
+ export { FifoBlockingRejection } from "./limiter/FifoBlockingRejection.js";
28
+ export { LifoBlockingRejection, } from "./limiter/LifoBlockingRejection.js";
@@ -0,0 +1,37 @@
1
+ import type { AdaptiveLimit } from "./StreamingLimit.js";
2
+ /**
3
+ * Loss based dynamic Limit that does an additive increment as long as
4
+ * there are no errors and a multiplicative decrement when there is an error.
5
+ */
6
+ export interface AIMDLimitOptions {
7
+ initialLimit?: number;
8
+ minLimit?: number;
9
+ maxLimit?: number;
10
+ /**
11
+ * Ratio by which to reduce the limit on a drop. Must be in [0.5, 1.0).
12
+ * Default: 0.9
13
+ */
14
+ backoffRatio?: number;
15
+ /**
16
+ * Timeout threshold in milliseconds that when exceeded equates to a drop.
17
+ * Default: 5000
18
+ */
19
+ timeout?: number;
20
+ }
21
+ export declare class AIMDLimit implements AdaptiveLimit {
22
+ private _limit;
23
+ private readonly limitListeners;
24
+ private readonly backoffRatio;
25
+ private readonly timeout;
26
+ private readonly minLimit;
27
+ private readonly maxLimit;
28
+ constructor(options?: AIMDLimitOptions);
29
+ addSample(_startTime: number, rtt: number, inflight: number, didDrop: boolean): void;
30
+ get currentLimit(): number;
31
+ private applyNewLimit;
32
+ subscribe(consumer: (newLimit: number) => void, options?: {
33
+ signal?: AbortSignal;
34
+ }): () => void;
35
+ toString(): string;
36
+ }
37
+ //# sourceMappingURL=AIMDLimit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AIMDLimit.d.ts","sourceRoot":"","sources":["../../src/limit/AIMDLimit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,SAAU,YAAW,aAAa;IAC7C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IAEpD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,GAAE,gBAAqB;IAiB1C,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAapF,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,OAAO,CAAC,aAAa;IAOrB,SAAS,CACP,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACpC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACrC,MAAM,IAAI;IAIb,QAAQ,IAAI,MAAM;CAGnB"}
@@ -0,0 +1,49 @@
1
+ import { ListenerSet } from "../ListenerSet.js";
2
+ export class AIMDLimit {
3
+ _limit;
4
+ limitListeners = new ListenerSet();
5
+ backoffRatio;
6
+ timeout;
7
+ minLimit;
8
+ maxLimit;
9
+ constructor(options = {}) {
10
+ const initialLimit = options.initialLimit ?? 20;
11
+ this._limit = initialLimit;
12
+ this.backoffRatio = options.backoffRatio ?? 0.9;
13
+ this.timeout = options.timeout ?? 5_000;
14
+ this.minLimit = options.minLimit ?? 20;
15
+ this.maxLimit = options.maxLimit ?? 200;
16
+ if (this.backoffRatio >= 1.0 || this.backoffRatio < 0.5) {
17
+ throw new Error("Backoff ratio must be in the range [0.5, 1.0)");
18
+ }
19
+ if (this.timeout <= 0) {
20
+ throw new Error("Timeout must be positive");
21
+ }
22
+ }
23
+ addSample(_startTime, rtt, inflight, didDrop) {
24
+ let currentLimit = this._limit;
25
+ if (didDrop || rtt > this.timeout) {
26
+ currentLimit = Math.floor(currentLimit * this.backoffRatio);
27
+ }
28
+ else if (inflight * 2 >= currentLimit) {
29
+ currentLimit = currentLimit + 1;
30
+ }
31
+ const newLimit = Math.min(this.maxLimit, Math.max(this.minLimit, currentLimit));
32
+ this.applyNewLimit(newLimit);
33
+ }
34
+ get currentLimit() {
35
+ return this._limit;
36
+ }
37
+ applyNewLimit(newLimit) {
38
+ if (newLimit !== this._limit) {
39
+ this._limit = newLimit;
40
+ this.limitListeners.notify(newLimit);
41
+ }
42
+ }
43
+ subscribe(consumer, options = {}) {
44
+ return this.limitListeners.subscribe(consumer, options);
45
+ }
46
+ toString() {
47
+ return `AIMDLimit [limit=${this.currentLimit}]`;
48
+ }
49
+ }
@@ -0,0 +1,15 @@
1
+ import type { AdaptiveLimit } from "./StreamingLimit.js";
2
+ /**
3
+ * Non-dynamic limit with a fixed value.
4
+ */
5
+ export declare class FixedLimit implements AdaptiveLimit {
6
+ private _limit;
7
+ constructor(limit: number);
8
+ addSample(_startTime: number, _rtt: number, _inflight: number, _didDrop: boolean): void;
9
+ get currentLimit(): number;
10
+ subscribe(_consumer: (newLimit: number) => void, _options?: {
11
+ signal?: AbortSignal;
12
+ }): () => void;
13
+ toString(): string;
14
+ }
15
+ //# sourceMappingURL=FixedLimit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixedLimit.d.ts","sourceRoot":"","sources":["../../src/limit/FixedLimit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;GAEG;AACH,qBAAa,UAAW,YAAW,aAAa;IAC9C,OAAO,CAAC,MAAM,CAAS;gBAEX,KAAK,EAAE,MAAM;IAIzB,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,OAAO,GAChB,IAAI;IAGP,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,SAAS,CACP,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACrC,QAAQ,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACtC,MAAM,IAAI;IAMb,QAAQ,IAAI,MAAM;CAGnB"}
@@ -0,0 +1,23 @@
1
+ const NOOP_UNSUBSCRIBE = () => { };
2
+ /**
3
+ * Non-dynamic limit with a fixed value.
4
+ */
5
+ export class FixedLimit {
6
+ _limit;
7
+ constructor(limit) {
8
+ this._limit = limit;
9
+ }
10
+ addSample(_startTime, _rtt, _inflight, _didDrop) {
11
+ }
12
+ get currentLimit() {
13
+ return this._limit;
14
+ }
15
+ subscribe(_consumer, _options = {}) {
16
+ // Listeners would never be called -- limit is fixed -- so we return a no-op
17
+ // function.
18
+ return NOOP_UNSUBSCRIBE;
19
+ }
20
+ toString() {
21
+ return `FixedLimit [limit=${this.currentLimit}]`;
22
+ }
23
+ }
@@ -0,0 +1,122 @@
1
+ import type { MetricRegistry } from "../MetricRegistry.js";
2
+ import type { AdaptiveLimit } from "./StreamingLimit.js";
3
+ /**
4
+ * Concurrency limit algorithm that adjusts the limit based on the gradient of
5
+ * change of the current average RTT and a long term exponentially smoothed
6
+ * average RTT. Unlike traditional congestion control algorithms we use average
7
+ * instead of minimum since RPC methods can be very bursty due to various
8
+ * factors such as non-homogenous request processing complexity as well as a
9
+ * wide distribution of data size. We have also found that using minimum can
10
+ * result in a bias towards an impractically low base RTT resulting in excessive
11
+ * load shedding. An exponential decay is applied to the base RTT so that the
12
+ * value is kept stable yet is allowed to adapt to long term changes in latency
13
+ * characteristics.
14
+ *
15
+ * The core algorithm re-calculates the limit every sampling window
16
+ * (e.g. 1 second) using the formula:
17
+ *
18
+ * // Calculate the gradient limiting to the range [0.5, 1.0] to filter outliers
19
+ * gradient = max(0.5, min(1.0, longtermRtt / currentRtt));
20
+ *
21
+ * // Calculate the new limit by applying the gradient and allowing for some queuing
22
+ * newLimit = gradient * currentLimit + queueSize;
23
+ *
24
+ * // Update the limit using a smoothing factor (default 0.2)
25
+ * newLimit = currentLimit * (1 - smoothing) + newLimit * smoothing
26
+ *
27
+ * The limit can be in one of three main states:
28
+ *
29
+ * 1. Steady state
30
+ * The average RTT is very stable and the current measurement whipsaws around
31
+ * this value, sometimes reducing the limit, sometimes increasing it.
32
+ *
33
+ * 2. Transition from steady state to load
34
+ * Either the RPS or latency has spiked. The gradient is < 1.0 due to a
35
+ * growing request queue that cannot be handled by the system. Excessive
36
+ * requests are rejected due to the low limit. The baseline RTT grows using
37
+ * exponential decay but lags the current measurement, which keeps the
38
+ * gradient < 1.0 and limit low.
39
+ *
40
+ * 3. Transition from load to steady state
41
+ * The system goes back to steady state after a prolonged period of excessive
42
+ * load. Requests aren't rejected and the sample RTT remains low. During this
43
+ * state the long term RTT may take some time to go back to normal and could
44
+ * potentially be several multiples higher than the current RTT.
45
+ */
46
+ export interface Gradient2LimitOptions {
47
+ /** Initial limit used by the limiter. Default: 20 */
48
+ initialLimit?: number;
49
+ /**
50
+ * Minimum concurrency limit allowed. The minimum helps prevent the algorithm
51
+ * from adjusting the limit too far down. Note that this limit is not
52
+ * desirable when used as backpressure for batch apps. Default: 20
53
+ */
54
+ minLimit?: number;
55
+ /**
56
+ * Maximum allowable concurrency. Any estimated concurrency will be capped at
57
+ * this value. Default: 200
58
+ */
59
+ maxConcurrency?: number;
60
+ /**
61
+ * Smoothing factor to limit how aggressively the estimated limit can shrink
62
+ * when queuing has been detected. Value of 0.0 to 1.0 where 1.0 means the
63
+ * limit is completely replaced by the new estimate. Default: 0.2
64
+ */
65
+ smoothing?: number;
66
+ /**
67
+ * Fixed amount the estimated limit can grow while latencies remain low.
68
+ * Can be a constant or a function of the current limit. Default: 4
69
+ */
70
+ queueSize?: number | ((concurrency: number) => number);
71
+ /**
72
+ * Tolerance for changes in minimum latency. Value >= 1.0 indicating how
73
+ * much change in minimum latency is acceptable before reducing the limit.
74
+ * For example, a value of 2.0 means that a 2x increase in latency is
75
+ * acceptable. Default: 1.5
76
+ */
77
+ rttTolerance?: number;
78
+ /**
79
+ * Number of samples in the long-term exponential average window.
80
+ * Default: 600
81
+ */
82
+ longWindow?: number;
83
+ metricRegistry?: MetricRegistry;
84
+ }
85
+ export declare class Gradient2Limit implements AdaptiveLimit {
86
+ private _limit;
87
+ private readonly limitListeners;
88
+ /** Estimated concurrency limit based on our algorithm */
89
+ private estimatedLimit;
90
+ /**
91
+ * Tracks a measurement of the short time, and more volatile, RTT meant to
92
+ * represent the current system latency.
93
+ */
94
+ private lastRtt;
95
+ /**
96
+ * Tracks a measurement of the long term, less volatile, RTT meant to
97
+ * represent the baseline latency. When the system is under load this number
98
+ * is expected to trend higher.
99
+ */
100
+ private readonly longRtt;
101
+ /** Maximum allowed limit providing an upper bound failsafe */
102
+ private readonly maxLimit;
103
+ private readonly minLimit;
104
+ private readonly queueSize;
105
+ private readonly smoothing;
106
+ private readonly tolerance;
107
+ private readonly longRttSampleListener;
108
+ private readonly shortRttSampleListener;
109
+ private readonly queueSizeSampleListener;
110
+ constructor(options?: Gradient2LimitOptions);
111
+ addSample(_startTime: number, rtt: number, inflight: number, _didDrop: boolean): void;
112
+ get currentLimit(): number;
113
+ private applyNewLimit;
114
+ subscribe(consumer: (newLimit: number) => void, options?: {
115
+ signal?: AbortSignal;
116
+ }): () => void;
117
+ private computeNextLimitUnrounded;
118
+ getLastRtt(): number;
119
+ getRttNoLoad(): number;
120
+ toString(): string;
121
+ }
122
+ //# sourceMappingURL=Gradient2Limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Gradient2Limit.d.ts","sourceRoot":"","sources":["../../src/limit/Gradient2Limit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,WAAW,qBAAqB;IACpC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;IAEvD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,qBAAa,cAAe,YAAW,aAAa;IAClD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IAEpD,yDAAyD;IACzD,OAAO,CAAC,cAAc,CAAS;IAE/B;;;OAGG;IACH,OAAO,CAAC,OAAO,CAAK;IAEpB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,8DAA8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqB;IAC5D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAqB;gBAEjD,OAAO,GAAE,qBAA0B;IAwB/C,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,GAChB,IAAI;IASP,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,OAAO,CAAC,aAAa;IAOrB,SAAS,CACP,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACpC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACrC,MAAM,IAAI;IAIb,OAAO,CAAC,yBAAyB;IAyCjC,UAAU,IAAI,MAAM;IAIpB,YAAY,IAAI,MAAM;IAItB,QAAQ,IAAI,MAAM;CAGnB"}