1
1
/**
2
- * Limits the number of calls to a resource to a maximum amount in some interval using the token bucket algorithm.
3
- *
4
- * Note that only the moment of starting the effect is rate limited: the number of concurrent executions is not bounded.
5
- *
6
- * Calls are queued up in an unbounded queue until capacity becomes available.
2
+ * Limits the number of calls to a resource to a maximum amount in some interval
3
+ * using the token bucket algorithm.
7
4
*
8
5
* @since 2.0.0
9
6
*/
@@ -13,12 +10,6 @@ import * as internal from "./internal/rateLimiter.js"
13
10
import type { Scope } from "./Scope.js"
14
11
15
12
/**
16
- * Limits the number of calls to a resource to a maximum amount in some interval using the token bucket algorithm.
17
- *
18
- * Note that only the moment of starting the effect is rate limited: the number of concurrent executions is not bounded.
19
- *
20
- * Calls are queued up in an unbounded queue until capacity becomes available.
21
- *
22
13
* @since 2.0.0
23
14
* @category models
24
15
*/
@@ -27,6 +18,37 @@ export interface RateLimiter {
27
18
}
28
19
29
20
/**
21
+ * Constructs a new `RateLimiter` with the specified limit and window.
22
+ *
23
+ * Limits the number of calls to a resource to a maximum amount in some interval
24
+ * using the token bucket algorithm.
25
+ *
26
+ * Notes
27
+ * - Only the moment of starting the effect is rate limited. The number of concurrent executions is not bounded.
28
+ * - Instances of `RateLimiter` can be composed.
29
+ * - The "cost" per effect can be changed. See {@link withCost}
30
+ *
31
+ * @example
32
+ * import { Effect, RateLimiter } from "effect";
33
+ * import { compose } from "effect/Function"
34
+ *
35
+ * const program = Effect.scoped(
36
+ * Effect.gen(function* ($) {
37
+ * const perMinuteRL = yield* $(RateLimiter.make(30, "1 minutes"))
38
+ * const perSecondRL = yield* $(RateLimiter.make(2, "1 seconds"))
39
+ *
40
+ * // This rate limiter respects both the 30 calls per minute
41
+ * // and the 2 calls per second constraints.
42
+ * const rateLimit = compose(perMinuteRL, perSecondRL)
43
+ *
44
+ * // simulate repeated calls
45
+ * for (let n = 0; n < 100; n++) {
46
+ * // wrap the effect we want to limit with rateLimit
47
+ * yield* $(rateLimit(Effect.log("Calling RateLimited Effect")));
48
+ * }
49
+ * })
50
+ * );
51
+ *
30
52
* @since 2.0.0
31
53
* @category constructors
32
54
*/
@@ -35,3 +57,41 @@ export const make: (limit: number, window: DurationInput) => Effect<
35
57
never ,
36
58
Scope
37
59
> = internal . make
60
+
61
+ /**
62
+ * Alters the per-effect cost of the rate-limiter.
63
+ *
64
+ * This can be used for "credit" based rate-limiting where different API endpoints
65
+ * cost a different number of credits within a time window.
66
+ * Eg: 1000 credits / hour, where a query costs 1 credit and a mutation costs 5 credits.
67
+ *
68
+ * @example
69
+ * import { Effect, RateLimiter } from "effect";
70
+ * import { compose } from "effect/Function";
71
+ *
72
+ * const program = Effect.scoped(
73
+ * Effect.gen(function* ($) {
74
+ * // Create a rate limiter that has an hourly limit of 1000 credits
75
+ * const rateLimiter = yield* $(RateLimiter.make(1000, "1 hours"));
76
+ * // Query API costs 1 credit per call ( 1 is the default cost )
77
+ * const queryAPIRL = compose(rateLimiter, RateLimiter.withCost(1));
78
+ * // Mutation API costs 5 credits per call
79
+ * const mutationAPIRL = compose(rateLimiter, RateLimiter.withCost(5));
80
+
81
+ * // Use the pre-defined rate limiters
82
+ * yield* $(queryAPIRL(Effect.log("Sample Query")));
83
+ * yield* $(mutationAPIRL(Effect.log("Sample Mutation")));
84
+ *
85
+ * // Or set a cost on-the-fly
86
+ * yield* $(
87
+ * rateLimiter(Effect.log("Another query with a different cost")).pipe(
88
+ * RateLimiter.withCost(3)
89
+ * )
90
+ * );
91
+ * })
92
+ * );
93
+ *
94
+ * @since 2.0.0
95
+ * @category combinators
96
+ */
97
+ export const withCost : ( cost : number ) => < A , E , R > ( effect : Effect < A , E , R > ) => Effect < A , E , R > = internal . withCost
0 commit comments