(SP: 1) [Backend] Move session heartbeat from DB writes to Redis sorted set#411
(SP: 1) [Backend] Move session heartbeat from DB writes to Redis sorted set#411ViktorSvertoka merged 1 commit intodevelopfrom
Conversation
fix(about): update LinkedIn follower fallback to 1800
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📝 WalkthroughWalkthroughThis PR implements a Redis-based session heartbeat mechanism for the Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Handler as POST Handler
participant Redis as Redis Client
participant DB as Database
Client->>Handler: POST /api/sessions/activity
Handler->>Redis: heartbeatViaRedis(sessionId)
alt Redis Available
Redis->>Redis: ZADD + ZREMRANGEBYSCORE + ZCARD (pipeline)
Redis-->>Handler: online count
else Redis Unavailable/Fails
Redis-->>Handler: null
Handler->>DB: heartbeatViaDb(sessionId)
DB->>DB: UPSERT activeSessions
DB->>DB: COUNT sessions (timeout check)
DB->>DB: Cleanup stale sessions
DB-->>Handler: online count
end
Handler-->>Client: { online: count }
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip You can make CodeRabbit's review stricter and more nitpicky using the `assertive` profile, if that's what you prefer.Change the |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/app/api/sessions/activity/route.ts (1)
57-62: Consider moving fallback-table cleanup off the request path.Now that steady-state traffic stays on Redis, this delete only runs during outages. That means stale
active_sessionsrows from fallback periods can sit around indefinitely between incidents. A small scheduled cleanup job would keep the fallback table bounded without reintroducing hot-path DB work.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/app/api/sessions/activity/route.ts` around lines 57 - 62, Remove the ad-hoc probabilistic cleanup from the request handler and instead implement a scheduled/background cleanup that runs off the request path; specifically, delete the Math.random() block that calls db.delete(activeSessions).where(lt(activeSessions.lastActivity, cleanupThreshold)), and create a new periodic job (e.g., cleanupActiveSessionsJob or a cron/scheduled function) that computes cleanupThreshold using SESSION_TIMEOUT_MS and performs db.delete(activeSessions).where(lt(activeSessions.lastActivity, cleanupThreshold)) with proper error handling and logging; ensure the route no longer performs this DB work so fallback-table maintenance is bounded by the independent scheduled task.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/app/api/sessions/activity/route.ts`:
- Around line 83-84: The current logic flips back to Redis immediately when
heartbeatViaRedis(sessionId) returns a value, causing undercount after transient
Redis outages; change the logic so that when heartbeatViaRedis(sessionId)
returns null/undefined (indicating an outage) you record the outage time and use
heartbeatViaDb(sessionId) as the source of truth for at least one timeout window
(e.g., SESSION_TIMEOUT_MS) after that outage, or alternatively seed Redis from
recent active_sessions before switching; update the code around redisCount,
online, and the functions heartbeatViaRedis/heartbeatViaDb to check a
lastRedisOutage timestamp (or a boolean flag) and continue using DB results
until the grace period expires, then resume using Redis normally.
---
Nitpick comments:
In `@frontend/app/api/sessions/activity/route.ts`:
- Around line 57-62: Remove the ad-hoc probabilistic cleanup from the request
handler and instead implement a scheduled/background cleanup that runs off the
request path; specifically, delete the Math.random() block that calls
db.delete(activeSessions).where(lt(activeSessions.lastActivity,
cleanupThreshold)), and create a new periodic job (e.g.,
cleanupActiveSessionsJob or a cron/scheduled function) that computes
cleanupThreshold using SESSION_TIMEOUT_MS and performs
db.delete(activeSessions).where(lt(activeSessions.lastActivity,
cleanupThreshold)) with proper error handling and logging; ensure the route no
longer performs this DB work so fallback-table maintenance is bounded by the
independent scheduled task.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 54041997-e70a-4989-b5bc-ad458d23d191
📒 Files selected for processing (2)
frontend/app/api/sessions/activity/route.tsfrontend/lib/about/stats.ts
| const redisCount = await heartbeatViaRedis(sessionId); | ||
| const online = redisCount ?? (await heartbeatViaDb(sessionId)); |
There was a problem hiding this comment.
Avoid switching back to Redis count immediately after an outage.
Sessions written via heartbeatViaDb() exist only in active_sessions, so redisCount ?? ... undercounts as soon as Redis starts responding again. After a transient outage, the online number can collapse until every still-active user heartbeats back into Redis. Consider keeping DB as the source of truth for one timeout window after a Redis failure, or seeding Redis from recent active_sessions rows before switching back.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/app/api/sessions/activity/route.ts` around lines 83 - 84, The
current logic flips back to Redis immediately when heartbeatViaRedis(sessionId)
returns a value, causing undercount after transient Redis outages; change the
logic so that when heartbeatViaRedis(sessionId) returns null/undefined
(indicating an outage) you record the outage time and use
heartbeatViaDb(sessionId) as the source of truth for at least one timeout window
(e.g., SESSION_TIMEOUT_MS) after that outage, or alternatively seed Redis from
recent active_sessions before switching; update the code around redisCount,
online, and the functions heartbeatViaRedis/heartbeatViaDb to check a
lastRedisOutage timestamp (or a boolean flag) and continue using DB results
until the grace period expires, then resume using Redis normally.
Summary
/api/sessions/activity) moved from PostgreSQL writes to Redis sorted set pipeline (ZADD + ZREMRANGEBYSCORE + ZCARD = 1 HTTP call)SESSION_TIMEOUT_MSconstant replaces 2 duplicate inline calculationsTest plan
npm run buildpassesZRANGE online_sessions 0 -1 WITHSCORESreturns session with updated scoresCloses #410
Acknowledged. Upstash managed Redis has high availability, and the counter
self-heals within 60s as active users re-heartbeat. Adding outage tracking
would require persistent state across serverless instances — more complexity
than the edge case warrants. Will revisit if Redis outages become a real issue
Summary by CodeRabbit
Bug Fixes
Chores