feat(analytics): add real-time active users tracking - #367
Conversation
Track concurrent active users in Redis with a sliding inactivity window, updated on every authenticated request via a global middleware, and expose the current count through a new analytics endpoint. - activeUsersService: Redis sorted-set tracking with configurable timeout (ACTIVE_USER_TIMEOUT_SECONDS) and graceful degradation when Redis is down - activityTracker middleware: decodes the Bearer token and records activity fire-and-forget, wired globally in app.js - GET /api/analytics/active-users endpoint (authenticated)
|
@Fury03 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
Warning Review limit reachedNext included review available in 59 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (7)
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 |
|
@Fury03 this PR has merge conflicts with the |
…ve-users # Conflicts: # app.js
1. Linked Issue
Closes #243
2. Problem Statement
There is no visibility into how many users are concurrently active, which makes capacity planning and peak-usage identification guesswork. Counting active users cannot be done with a local patch because it needs a shared, low-latency counter visible across app instances (Redis), a per-request activity signal from every authenticated route, and a configurable expiry so stale sessions fall out of the count.
3. Solution Comparison and Decision
ZADDwith a timestamp score gives O(log n) per-request updates,ZCARDafter a range-prune gives an exact concurrent count, and the operation is atomic across instances. This is the only option that is both real-time and horizontally correct.4. The Change
A
ActiveUsersServiceover a Redis sorted set keyed by user id with a timestamp score:Acceptance criteria mapped to behavior:
activeUsersService.trackActivity— tuple(user id, timestamp)in a sorted settrackActivitymiddleware, mounted globally inapp.js; decodes the Bearer JWT (no DB hit) and writes fire-and-forgetACTIVE_USER_TIMEOUT_SECONDS(default 300) pruned on every write/readGET /api/analytics/active-users(authenticated) returns{ activeUsers, timeoutSeconds }The middleware never blocks a request (fire-and-forget) and the service degrades to a no-op when Redis is unavailable, so analytics can never take the app down.
5. Compatibility Note (INTERFACE_VERSION)
No version bump. This adds a new
/api/analytics/active-usersendpoint and one opt-in.envvar; it does not change any existing response shape or route.6. Incidental Fixes
Bearertoken, so unauthenticated traffic is not charged a Redis write.setRedis/setTimeoutSeconds) lets the middleware→service→route chain be tested without a live Redis.7. Testing
test/activeUsers.test.jsdrives the real exportedappvia supertest with a Redis-compatible fake injected at the client seam, so the middleware, service and endpoint path are all exercised:requires authentication to read the active-user countcounts the requesting user via the activity middlewarecounts each unique user once and ignores repeated activityexpires users who have been idle longer than the timeoutrespects a shorter timeout via the environment override seamreturns 0 without error when Redis is unavailableResult:
Tests: 6 passed. A regression run ofreadingProgressandhealthsuites (17 total) also passed, confirming the new global middleware does not break existing routes.8. Additional Notes / Scope
One commit adding the middleware, service, controller, routes and test. It touches
app.jsonly to mount the middleware and router; no other module is affected.