Skip to content

Commit d24963c

Browse files
ellyxirseefeldb
andauthored
after 20 writes per second, only write the last value every 0.1s (#549)
* after 20 writes per second, only write the last value every 0.1s * factor out throttle * only send data to iframe if it changed --------- Co-authored-by: Bernhard Seefeld <berni@common.tools>
1 parent 6283486 commit d24963c

File tree

1 file changed

+83
-6
lines changed

1 file changed

+83
-6
lines changed

typescript/packages/jumble/src/iframe-ctx.ts

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,76 @@ const serializeProxyObjects = (proxy: any) => {
1717
return proxy == undefined ? undefined : JSON.parse(JSON.stringify(proxy));
1818
};
1919

20+
// Type for tracking write operations per context+key
21+
type TimeoutId = ReturnType<typeof setTimeout>;
22+
type WriteTracking = {
23+
pendingTimeout: TimeoutId | null;
24+
pendingCallback: (() => void) | null; // Store the callback to execute when timeout fires
25+
writeCount: number;
26+
lastResetTime: number;
27+
};
28+
29+
// Map to store write tracking by context and key
30+
const writeTrackers = new Map<any, Map<string, WriteTracking>>();
31+
32+
// Configuration
33+
const MAX_IMMEDIATE_WRITES_PER_SECOND = 20; // Allow 20 immediate writes per second
34+
const THROTTLED_WRITE_INTERVAL_MS = 100; // 0.1s interval after threshold
35+
36+
// Throttle function that handles write rate limiting
37+
function throttle(context: any, key: string, callback: () => void): void {
38+
// Get or create context map for this specific context
39+
if (!writeTrackers.has(context)) {
40+
writeTrackers.set(context, new Map());
41+
}
42+
const contextMap = writeTrackers.get(context)!;
43+
44+
// Get or initialize tracking info for this key
45+
if (!contextMap.has(key)) {
46+
contextMap.set(key, {
47+
pendingTimeout: null,
48+
pendingCallback: null,
49+
writeCount: 0,
50+
lastResetTime: Date.now(),
51+
});
52+
}
53+
54+
const tracking = contextMap.get(key)!;
55+
const now = Date.now();
56+
57+
// Reset counter if a second has passed
58+
if (now - tracking.lastResetTime > 1000) {
59+
tracking.writeCount = 0;
60+
tracking.lastResetTime = now;
61+
if (tracking.pendingTimeout) {
62+
clearTimeout(tracking.pendingTimeout);
63+
tracking.pendingTimeout = null;
64+
}
65+
}
66+
67+
// If we're under the threshold, process immediately
68+
if (tracking.writeCount < MAX_IMMEDIATE_WRITES_PER_SECOND) {
69+
tracking.writeCount++;
70+
// Execute callback immediately
71+
callback();
72+
} else {
73+
// Update the callback to be executed when the timeout fires
74+
tracking.pendingCallback = callback;
75+
76+
// Only set a new timeout if there isn't one already
77+
if (!tracking.pendingTimeout) {
78+
tracking.pendingTimeout = setTimeout(() => {
79+
// Execute the latest callback
80+
tracking.pendingCallback?.();
81+
82+
// Clear the timeout reference
83+
tracking.pendingTimeout = null;
84+
tracking.pendingCallback = null;
85+
}, THROTTLED_WRITE_INTERVAL_MS);
86+
}
87+
}
88+
}
89+
2090
export const setupIframe = () =>
2191
setIframeContextHandler({
2292
read(context: any, key: string): any {
@@ -25,23 +95,30 @@ export const setupIframe = () =>
2595
return serialized;
2696
},
2797
write(context: any, key: string, value: any) {
28-
if (isCell(context)) {
29-
context.key(key).setRaw(value);
30-
} else {
31-
context[key] = value;
32-
}
98+
throttle(context, key, () => {
99+
if (isCell(context)) {
100+
context.key(key).setRaw(value);
101+
} else {
102+
context[key] = value;
103+
}
104+
});
33105
},
34106
subscribe(
35107
context: any,
36108
key: string,
37109
callback: (key: string, value: any) => void,
38110
): any {
111+
let previousValue: any;
112+
39113
const action: Action = (log: ReactivityLog) => {
40114
const data = isCell(context)
41115
? context.withLog(log).key(key).get()
42116
: context?.[key];
43117
const serialized = serializeProxyObjects(data);
44-
callback(key, serialized);
118+
if (serialized !== previousValue) {
119+
previousValue = serialized;
120+
callback(key, serialized);
121+
}
45122
};
46123

47124
addAction(action);

0 commit comments

Comments
 (0)