History

Use React Pacer throttler for domain event snapshot sync

- Replace manual timeout-based domain event batching with `Throttler` - Keep provider query invalidation batched with trailing 100ms flushes - Cancel throttler and reset invalidation flag during EventRouter cleanup

Julius Marminge committed 8 months ago

Commit e7650511 parent f5e4df51 file · +19 −24Browse files

Unified · Split

apps/web/src/routes/__root.tsx+19−24
@@ -8,6 +8,7 @@ import { · 7 unmodified lines
88 } from "@tanstack/react-router";
99 import { useEffect, useRef } from "react";
1010 import { QueryClient, useQueryClient } from "@tanstack/react-query";
11+import { Throttler } from "@tanstack/react-pacer";
1112
1213 import { APP_DISPLAY_NAME } from "../branding";
1314 import { Button } from "../components/ui/button";
@@ -150,6 +151,7 @@ function EventRouter() { · 136 unmodified lines
150151 let latestSequence = 0;
151152 let syncing = false;
152153 let pending = false;
154+ let needsProviderInvalidation = false;
153155
154156 const flushSnapshotSync = async (): Promise<void> => {
155157 const snapshot = await api.orchestration.getSnapshot();
@@ -185,21 +187,20 @@ function EventRouter() { · 29 unmodified lines
185187 syncing = false;
186188 };
187189
188− void syncSnapshot().catch(() => undefined);
189−
190− // Batch domain events: collect over a short window then sync once.
191− const DOMAIN_EVENT_BATCH_MS = 100;
192− let batchTimer: ReturnType<typeof setTimeout> | null = null;
193− let needsProviderInvalidation = false;
194−
195− const flushBatch = () => {
196− batchTimer = null;
197− if (needsProviderInvalidation) {
198− needsProviderInvalidation = false;
199− void queryClient.invalidateQueries({ queryKey: providerQueryKeys.all });
200− }
201− void syncSnapshot();
202− };
190+ const domainEventFlushThrottler = new Throttler(
191+ () => {
192+ if (needsProviderInvalidation) {
193+ needsProviderInvalidation = false;
194+ void queryClient.invalidateQueries({ queryKey: providerQueryKeys.all });
195+ }
196+ void syncSnapshot();
197+ },
198+ {
199+ wait: 100,
200+ leading: false,
201+ trailing: true,
202+ },
203+ );
203204
204205 const unsubDomainEvent = api.orchestration.onDomainEvent((event) => {
205206 if (event.sequence <= latestSequence) {
@@ -209,12 +210,7 @@ function EventRouter() { · 3 unmodified lines
209210 if (event.type === "thread.turn-diff-completed" || event.type === "thread.reverted") {
210211 needsProviderInvalidation = true;
211212 }
212− // Throttle (not debounce): schedule a flush if none is pending.
213− // This guarantees at most one syncSnapshot per batch window
214− // without starving the UI during sustained event bursts.
215− if (batchTimer === null) {
216− batchTimer = setTimeout(flushBatch, DOMAIN_EVENT_BATCH_MS);
217− }
213+ domainEventFlushThrottler.maybeExecute();
218214 });
219215 const unsubTerminalEvent = api.terminal.onEvent((event) => {
220216 const hasRunningSubprocess = terminalRunningSubprocessFromEvent(event);
@@ -299,9 +295,8 @@ function EventRouter() { · 78 unmodified lines
299295 });
300296 return () => {
301297 disposed = true;
302− if (batchTimer !== null) {
303− clearTimeout(batchTimer);
304− }
298+ needsProviderInvalidation = false;
299+ domainEventFlushThrottler.cancel();
305300 unsubDomainEvent();
306301 unsubTerminalEvent();
307302 unsubWelcome();

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t