Skip to content

Commit 32a8b04

Browse files
committed
Let go of idle browser objects while the server is asking
Objects are registered so that the server can keep reading them, and nothing else is coming to take them away. Pruning only happened when a new event was dispatched, so a page which went quiet held every object it had ever handed over - detached nodes included - for the whole of its idle time. The registry is now swept whenever the server asks it anything, not only when something is added to it. An object which is being read is not idle, and is kept as before.
1 parent e07346d commit 32a8b04

2 files changed

Lines changed: 36 additions & 0 deletions

File tree

‎src/js/packages/@reactpy/client/src/event-proxy.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,11 @@ export class EventProxyRegistry {
436436
*
437437
* Whatever is dropped here is gone for good as far as the server is concerned: the id
438438
* it holds keeps working, and keeps answering "not-found".
439+
*
440+
* This runs whenever the registry is used, not only when something is added to it.
441+
* Objects are kept for the server's benefit, so a page which stops dispatching events
442+
* still lets go of the ones which have gone idle rather than holding them for their
443+
* whole idle time.
439444
*/
440445
prune(): void {
441446
const now = this.clock();
@@ -462,6 +467,12 @@ export class EventProxyRegistry {
462467
handleMessage(message: EventProxyRequest | EventProxyRequestBatch): void {
463468
if (!message || message.type !== EVENT_PROXY_REQUEST_TYPE) return;
464469

470+
// Whatever is registered here is being kept alive for the server's benefit, and
471+
// nobody else is coming to take it away - so the moment the server asks about them
472+
// is the moment to notice which of them have gone stale. Waiting for the next event
473+
// to be dispatched would leave detached objects held for their whole idle time.
474+
this.prune();
475+
465476
const requests = (message as EventProxyRequestBatch).requests;
466477
if (!Array.isArray(requests)) {
467478
const request = message as EventProxyRequest;

‎src/js/packages/@reactpy/client/tests/event-proxy.test.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,31 @@ test("an object which aged out is answered as not-found", () => {
906906
expect(replies[0].code).toBe("not-found");
907907
});
908908

909+
test("answering a request takes the idle objects out with it", () => {
910+
// Objects are registered for the server's benefit and nothing else is coming to take
911+
// them away, so a page which stops dispatching events used to hold every one of them
912+
// for their whole idle time - a detached node is no less detached for nobody having
913+
// asked about it in the last minute.
914+
const { registry, replies, now } = make_registry();
915+
const idle = registry.register(make_event());
916+
917+
now.value = LIVE_OBJECT_TTL_MS - 1;
918+
const busy = registry.register(make_event());
919+
920+
now.value = LIVE_OBJECT_TTL_MS + 5;
921+
registry.handleMessage(
922+
request({ requestId: "ask", eventId: busy, path: [] }),
923+
);
924+
925+
expect(replies).toHaveLength(1);
926+
expect(replies[0].ok).toBe(true);
927+
expect(registry.has(idle)).toBe(
928+
false,
929+
"the idle one went while nobody watched",
930+
);
931+
expect(registry.has(busy)).toBe(true, "the one being read is not idle");
932+
});
933+
909934
test("the cap leaves room for every event a busy page can have in flight", () => {
910935
const { registry } = make_registry();
911936
const ids: string[] = [];

0 commit comments

Comments
 (0)