Skip to content

Commit c232057

Browse files
authored
Use monotonic clock for rate limiting (#8456)
1 parent bde2c3c commit c232057

File tree

2 files changed

+10
-10
lines changed

2 files changed

+10
-10
lines changed

src/core/task/Task.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,7 +2059,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
20592059

20602060
const drainStreamInBackgroundToFindAllUsage = async (apiReqIndex: number) => {
20612061
const timeoutMs = DEFAULT_USAGE_COLLECTION_TIMEOUT_MS
2062-
const startTime = Date.now()
2062+
const startTime = performance.now()
20632063
const modelId = getModelId(this.apiConfiguration)
20642064

20652065
// Local variables to accumulate usage data without affecting the main flow
@@ -2130,7 +2130,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
21302130
// Use the same iterator that the main loop was using
21312131
while (!item.done) {
21322132
// Check for timeout
2133-
if (Date.now() - startTime > timeoutMs) {
2133+
if (performance.now() - startTime > timeoutMs) {
21342134
console.warn(
21352135
`[Background Usage Collection] Timed out after ${timeoutMs}ms for model: ${modelId}, processed ${chunkCount} chunks`,
21362136
)
@@ -2601,10 +2601,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
26012601
// Use the shared timestamp so that subtasks respect the same rate-limit
26022602
// window as their parent tasks.
26032603
if (Task.lastGlobalApiRequestTime) {
2604-
const now = Date.now()
2604+
const now = performance.now()
26052605
const timeSinceLastRequest = now - Task.lastGlobalApiRequestTime
26062606
const rateLimit = apiConfiguration?.rateLimitSeconds || 0
2607-
rateLimitDelay = Math.ceil(Math.max(0, rateLimit * 1000 - timeSinceLastRequest) / 1000)
2607+
rateLimitDelay = Math.ceil(Math.min(rateLimit, Math.max(0, rateLimit * 1000 - timeSinceLastRequest) / 1000))
26082608
}
26092609

26102610
// Only show rate limiting message if we're not retrying. If retrying, we'll include the delay there.
@@ -2619,7 +2619,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
26192619

26202620
// Update last request time before making the request so that subsequent
26212621
// requests — even from new subtasks — will honour the provider's rate-limit.
2622-
Task.lastGlobalApiRequestTime = Date.now()
2622+
Task.lastGlobalApiRequestTime = performance.now()
26232623

26242624
const systemPrompt = await this.getSystemPrompt()
26252625
this.lastUsedInstructions = systemPrompt

src/core/task/__tests__/Task.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,9 +1106,9 @@ describe("Cline", () => {
11061106
await parentIterator.next()
11071107

11081108
// Simulate time passing (more than rate limit)
1109-
const originalDateNow = Date.now
1110-
const mockTime = Date.now() + (mockApiConfig.rateLimitSeconds + 1) * 1000
1111-
Date.now = vi.fn(() => mockTime)
1109+
const originalPerformanceNow = performance.now
1110+
const mockTime = performance.now() + (mockApiConfig.rateLimitSeconds + 1) * 1000
1111+
performance.now = vi.fn(() => mockTime)
11121112

11131113
// Create a subtask after time has passed
11141114
const child = new Task({
@@ -1129,8 +1129,8 @@ describe("Cline", () => {
11291129
// Verify no rate limiting was applied
11301130
expect(mockDelay).not.toHaveBeenCalled()
11311131

1132-
// Restore Date.now
1133-
Date.now = originalDateNow
1132+
// Restore performance.now
1133+
performance.now = originalPerformanceNow
11341134
})
11351135

11361136
it("should share rate limiting across multiple subtasks", async () => {

0 commit comments

Comments
 (0)