Add pacing logic to Lighter - #164
Merged
Merged
Conversation
hacheigriega
force-pushed
the
hy/rate-limit-metrics
branch
from
August 11, 2026 14:22
0b74534 to
30ceab5
Compare
hacheigriega
marked this pull request as ready for review
August 11, 2026 16:34
hacheigriega
force-pushed
the
hy/rate-limit-metrics
branch
from
August 11, 2026 18:13
144a4f7 to
9e17d40
Compare
gluax
approved these changes
Aug 11, 2026
gluax
left a comment
Contributor
There was a problem hiding this comment.
LGTM, but maybe a second opinion from someone more familiar with TS/DP?
Thomasvdam
reviewed
Aug 12, 2026
Thomasvdam
left a comment
Member
There was a problem hiding this comment.
I think we can express the pacing queue more elegantly with a semaphore.
import { NodeRuntime } from "@effect/platform-node";
import { Duration, Effect, Schedule, Stream } from "effect";
const MAX_MESSAGES_PER_WINDOW = 5;
const WINDOW_SIZE_SECONDS = 5
const task = Effect.fn(function* (a: number) {
yield* Effect.logInfo();
yield* Effect.sleep(Duration.seconds(WINDOW_SIZE));
});
const program = Effect.gen(function* () {
const mutex = yield* Effect.makeSemaphore(MAX_MESSAGES_PER_WINDOW);
const stream = Stream.iterate(0, (n) => n + 1).pipe(
Stream.schedule(
Schedule.spaced(Duration.seconds(.5)).pipe(
Schedule.jitteredWith({ min: 0.8, max: 1.2 }),
),
),
);
yield* stream.pipe(
Stream.mapEffect(
(n) =>
mutex
.withPermits(1)(
Effect.gen(function* () {
yield* task(n);
}),
),
{ concurrency: "unbounded" },
),
Stream.runDrain,
);
}).pipe(
Effect.withSpan("program", {
attributes: { source: "Playground" },
}),
);
program.pipe(NodeRuntime.runMain);This produces the following output:
[11:32:14.533] INFO (#6):
[11:32:15.134] INFO (#9):
[11:32:15.636] INFO (#12):
[11:32:16.078] INFO (#15):
[11:32:16.623] INFO (#18):
[11:32:19.541] INFO (#21):
[11:32:20.137] INFO (#24):
[11:32:20.639] INFO (#27):
[11:32:21.082] INFO (#30):
[11:32:21.626] INFO (#33):
[11:32:24.545] INFO (#36):
[11:32:25.144] INFO (#39):
[11:32:25.643] INFO (#42):
[11:32:26.087] INFO (#45):
[11:32:26.631] INFO (#48):
[11:32:29.550] INFO (#51):
[11:32:30.148] INFO (#54):
Which seems like the behaviour we're looking for? I think this pattern also makes it easier to abstract into a utility and test separately.
Thomasvdam
reviewed
Aug 13, 2026
Thomasvdam
approved these changes
Aug 13, 2026
Add pacing logic using a single outbound queue parametrized by `maxMessagesPerMinute`.
hacheigriega
force-pushed
the
hy/rate-limit-metrics
branch
from
August 13, 2026 18:30
63cfd53 to
c7a7f77
Compare
Add pacing logic using an outbound queue parametrized by maxMessagesPerSecond.
hacheigriega
force-pushed
the
hy/rate-limit-metrics
branch
from
August 13, 2026 18:33
c7a7f77 to
ad46bb0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
To prevent hitting Lighter's rate limits
Explanation of Changes
To address the limit of "Max Messages Sent By Client Per Minute" set at 200, we add a pacing logic to the Lighter module WS client by using a single outbound queue parametrized by
maxMessagesPerMinute(defaults to 180).Also adds parsing and logging of error frames sent by Lighter and Binance. (These are separate from transport-level WS failures that end up in the error handler)
Testing
A few unit tests.
Also tested locally that we no longer get "Too Many Websocket Messages" errors upon burst of requests that we get on main.