Skip to content

fix: room expiry edge cases#58

Merged
madawei2699 merged 2 commits into
cloudflarefrom
fix/room-expiry-edge-cases
May 19, 2026
Merged

fix: room expiry edge cases#58
madawei2699 merged 2 commits into
cloudflarefrom
fix/room-expiry-edge-cases

Conversation

@madawei2699
Copy link
Copy Markdown
Collaborator

@madawei2699 madawei2699 commented May 19, 2026

Summary

Oracle analysis of the room expiry flow identified several bugs and edge cases.

Fixes

🔴 Bugs

Double leaveRoom() (#6)
Both the setInterval countdown-to-zero path and expiryFinalRef setTimeout called leaveRoom() + redirect, causing the second call to throw on an already-destroyed meeting object. Added hasLeftRef guard so the leave executes exactly once regardless of which path fires first.

Cron disconnect shows "Reconnecting" instead of expiry message (#5)
When the cron job deletes participants, RTK fires a disconnect event and onRoomLeft was unconditionally setting connectionStatus = "reconnecting", leaving users staring at a spinner with no explanation. Now checks Date.now() >= expiresAtRef.current in onRoomLeft — if the room has expired, calls doExpireLeave() and redirects home instead.

🟡 Improvements

Warning timer drift (#7A)
expiryTimerRef was hardcoded to 6600 * 1000ms. If the device sleeps and resumes, the setTimeout fires late. Changed to expiresAt - now - 10min so the delay is always calibrated to the absolute expiry time.

Backgrounded tab throttling (#7B)
Browsers throttle setInterval in backgrounded tabs to ≥1 minute, causing the countdown to desync. Added a visibilitychange listener that recalibrates timeLeft from expiresAt immediately when the tab becomes visible again. If the room has already expired, calls doExpireLeave() directly.

KV TTL reduced from 30 days to 4 hours (#4)
Rooms live for 2 hours. Keeping KV records for 30 days was wasteful and caused the cron's kv.list() to scan through large amounts of dead records. Changed to 4 hours (2h room lifetime + 2h buffer) so KV auto-purges stale records. Cron is kept as defense-in-depth but is no longer the primary cleanup mechanism.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the chat room expiration logic by introducing a hasLeftRef to track leave status, adding a visibility change listener to detect expiration upon tab focus, and refactoring timers to use dynamic expiration values. Additionally, the KV storage TTL for rooms has been reduced to 4 hours. Feedback suggests resetting the hasLeftRef in the cleanup function to ensure the hook remains robust if reused without a full page reload.

meeting.chat.off("chatUpdate", syncMessages)
if (hasJoinedRef.current) meeting.leaveRoom()
if (hasJoinedRef.current && !hasLeftRef.current) meeting.leaveRoom()
hasJoinedRef.current = false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The hasLeftRef should be reset to false in the cleanup function, similar to how hasJoinedRef is reset. While the current implementation uses a hard redirect (window.location.href = "/") which re-initializes the hook state, resetting the ref explicitly ensures that the expiration logic remains correct if the hook is reused across different room sessions without a full page reload (e.g., if the redirect logic is changed in the future or if the hook is used in a context where navigation doesn't trigger a reload).

      hasJoinedRef.current = false
      hasLeftRef.current = false

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes several edge cases in the room expiry flow: prevents double leaveRoom() calls, properly handles cron-initiated disconnects as expiry rather than reconnection attempts, recalibrates timers against the absolute expiry timestamp to handle device sleep and background-tab throttling, and reduces the KV TTL from 30 days to 4 hours to align with the 2-hour room lifetime.

Changes:

  • Introduces hasLeftRef guard plus a shared doExpireLeave() helper so leave/redirect runs exactly once regardless of trigger (interval, final setTimeout, or roomLeft event).
  • Recalibrates the warning and final-expiry timers against expiresAtRef and adds a visibilitychange handler so backgrounded/asleep tabs resync timeLeft and expire correctly.
  • Reduces ROOM_KV_TTL_S to 4 hours so KV auto-purges dead room records and the cron's kv.list() stays small.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
app/src/pages/api/token.ts Extracts ROOM_KV_TTL_S = 4h and uses it for both KV writes in place of the previous 30-day TTL.
app/src/hooks/useChatRoom.ts Adds hasLeftRef + doExpireLeave(), expiry-aware onRoomLeft, visibility-change recalibration, and absolute-time-based warning/final timers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@madawei2699 madawei2699 merged commit 86d56a5 into cloudflare May 19, 2026
4 checks passed
@madawei2699 madawei2699 deleted the fix/room-expiry-edge-cases branch May 19, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants