Skip to content

fix: Inflated live-visitors chart - #601

Merged
Blaumaus merged 8 commits into
mainfrom
fix/session-fixes
Aug 3, 2026
Merged

fix: Inflated live-visitors chart#601
Blaumaus merged 8 commits into
mainfrom
fix/session-fixes

Conversation

@Blaumaus

@Blaumaus Blaumaus commented Aug 1, 2026

Copy link
Copy Markdown
Member

Changes

If applicable, please describe what changes were made in this pull request.

Community Edition support

  • Your feature is implemented for the Swetrix Community Edition
  • This PR only updates the Cloud (Enterprise) Edition code (e.g. Paddle webhooks, blog, payouts, etc.)

Database migrations

  • Clickhouse / MySQL migrations added for this PR
  • No table schemas changed in this PR

Documentation

  • You have updated the documentation according to your PR
  • This PR did not change any publicly documented endpoints

Summary by CodeRabbit

  • New Features

    • Analytics now distinguishes individual sessions more accurately, including multiple sessions from the same visitor.
    • Session activity is tracked consistently across pageviews, events, errors, performance data, profile identification, and heartbeats.
    • Live visitor counts now reflect activity within the most recent five minutes.
    • Added clearer session metrics, including distinct sessions and 30-minute inactivity expiration.
  • Documentation

    • Clarified the difference between visitors and sessions, including session expiration and daily visitor recognition.
    • Updated concurrency metric definitions and reporting behavior.

@Blaumaus Blaumaus self-assigned this Aug 1, 2026
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a9a59160-ff7f-4bdc-96b9-a980e8766923

📥 Commits

Reviewing files that changed from the base of the PR and between e8237e2 and aa57bc3.

📒 Files selected for processing (6)
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • backend/apps/cloud/src/analytics/analytics.service.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/migrations/clickhouse/2026_08_01_session_id.js
  • backend/migrations/clickhouse/initialise_database.js

📝 Walkthrough

Walkthrough

Analytics now assigns generated session IDs (sid) alongside visitor session IDs (psid). Events, heartbeats, persistence, queries, concurrency, metrics, migration logic, and documentation use the SID-aware session model.

Changes

SID-based analytics sessions

Layer / File(s) Summary
Session identity and storage
backend/apps/*/analytics/analytics.service.ts, backend/migrations/clickhouse/*
Redis session handling generates and preserves SIDs. ClickHouse stores SID data and migrates session storage to a SID-keyed table.
Event and heartbeat propagation
backend/apps/*/analytics/analytics.controller.ts, backend/apps/*/analytics/heartbeat.gateway.ts, backend/apps/*/analytics/utils/transformers.ts
Controllers, transformers, and heartbeat gateways propagate SIDs through events and activity records. Heartbeats and identification require a valid SID.
SID-aware analytics and concurrency
backend/apps/*/analytics/analytics.service.ts
Counts, durations, charts, funnels, journeys, profiles, replays, reports, and errors use SID-aware grouping. Concurrency uses recent activity within the five-minute online window.
Shared configuration, metrics, and documentation
backend/apps/*/common/constants.ts, backend/apps/*/analytics/v2/*, docs/content/docs/*
The online window uses a shared constant. Metrics and documentation define visitors, sessions, 30-minute inactivity expiry, daily salt rotation, and five-minute activity liveness.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description leaves the Changes section blank and incorrectly marks that no table schemas changed despite adding a ClickHouse session-ID migration. Summarize the session-ID and live-visitor changes, and select the ClickHouse migration option instead of the no-schema-change option.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary fix to the live-visitors chart.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/session-fixes

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Blaumaus
Blaumaus marked this pull request as ready for review August 2, 2026 03:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/apps/cloud/src/analytics/analytics.service.ts (1)

5065-5091: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

duration_avg selects sid but still groups by psid in four session-summary queries. The migration changed the selected column of the inner duration subquery from psid to sid and left GROUP BY psid unchanged. ClickHouse rejects sid because it is neither an aggregate nor a grouping key, and the query fails with NOT_AN_AGGREGATE. getAnalyticsSummary then returns a 500 for every non-all period in both applications.

  • backend/apps/cloud/src/analytics/analytics.service.ts#L5065-L5091: change GROUP BY psid on line 5091 to GROUP BY sid.
  • backend/apps/cloud/src/analytics/analytics.service.ts#L5130-L5156: change GROUP BY psid on line 5156 to GROUP BY sid.
  • backend/apps/community/src/analytics/analytics.service.ts#L3641-L3667: change GROUP BY psid on line 3667 to GROUP BY sid.
  • backend/apps/community/src/analytics/analytics.service.ts#L3706-L3732: change GROUP BY psid on line 3732 to GROUP BY sid.

Note that the outer psid IN (...) predicate still scopes the rows to the filtered visitors, so grouping by sid keeps the intended scope and produces one duration per session.

🐛 Proposed fix (same shape at all four sites)
           duration_avg AS (
             SELECT avgOrNull(duration) as sdur
             FROM (
               SELECT
                 sid,
                 dateDiff('second', min(firstSeen), max(lastSeen)) as duration
               FROM sessions
               WHERE pid = {pid:FixedString(12)}
                 AND psid IN (
                   ...
                 )
-              GROUP BY psid
+              GROUP BY sid
             )
           ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/cloud/src/analytics/analytics.service.ts` around lines 5065 -
5091, Update the duration_avg inner subquery in getAnalyticsSummary so its GROUP
BY uses the selected session identifier sid instead of psid. Apply this change
at backend/apps/cloud/src/analytics/analytics.service.ts lines 5065-5091 and
5130-5156, and backend/apps/community/src/analytics/analytics.service.ts lines
3641-3667 and 3706-3732; leave the outer psid IN filtering unchanged.
🧹 Nitpick comments (3)
backend/migrations/clickhouse/2026_08_01_session_id.js (1)

30-30: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a failure handler for the migration promise.

queriesRunner(queries) returns a promise. queriesRunner rethrows query errors. Without a .catch, a failure produces an unhandled rejection and the process may exit with code 0, which hides the failure from the deploy pipeline.

♻️ Proposed change
-queriesRunner(queries)
+queriesRunner(queries).catch((error) => {
+  console.error(error)
+  process.exit(1)
+})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/migrations/clickhouse/2026_08_01_session_id.js` at line 30, Update
the migration invocation of queriesRunner(queries) to handle its rejected
promise with a failure handler that reports the error and exits with a nonzero
status, ensuring query failures are visible to the deploy pipeline.
backend/apps/cloud/src/analytics/analytics.service.ts (1)

4151-4168: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename avg_duration, because the expression is a sum.

sum(session_duration) as avg_duration returns the total time of all SIDs for the PSID. The alias says average. The alias then flows to the sdur output column. Rename it to total_duration in both CTEs to keep the query readable.

Also applies to: 4319-4336

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/cloud/src/analytics/analytics.service.ts` around lines 4151 -
4168, Rename the sum-duration alias from avg_duration to total_duration in both
affected CTEs, including the query section around session_duration and the
corresponding earlier section, and update any downstream references that expose
it as sdur. Preserve the existing sum expression and output behavior.
backend/apps/community/src/analytics/analytics.service.ts (1)

2485-2528: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the class logger instead of console.error.

AnalyticsService declares private readonly logger = new Logger(AnalyticsService.name) at line 516 and uses this.logger.error elsewhere in this file. Line 2526 writes to console.error, so the failure loses the Nest context and structured formatting.

♻️ Proposed change
     } catch (error) {
-      console.error('Failed to record session:', error)
+      this.logger.error(`[recordSessionActivity] Failed to record session: ${error}`)
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/apps/community/src/analytics/analytics.service.ts` around lines 2485
- 2528, In AnalyticsService.recordSessionActivity, replace the console.error
call in the catch block with this.logger.error, reusing the class logger
declared on AnalyticsService and preserving the existing failure message and
error details.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/apps/cloud/src/analytics/analytics.service.ts`:
- Around line 2928-2935: Update getSessionDurationFromClickHouse to calculate
duration for the replayed session rather than aggregating all sessions with
max(duration). Pass the replay sid into this query and filter the grouped
session data by that sid, preserving the existing fallback behavior in
getSessionReplaySummary.
- Around line 6633-6634: Restore the concurrency range guard in
shouldComputeConcurrency so concurrency is only computed for bounded ranges, or
only for hour-and-finer time buckets, rather than period=all. Apply the
identical guard at
backend/apps/cloud/src/analytics/analytics.service.ts:6633-6634 and
backend/apps/community/src/analytics/analytics.service.ts:5170-5171 to preserve
consistent semantics.
- Line 4905: Update the period='all' duration_avg CTE to group and calculate
durations by the same coalesced session identifier as the unique count, using
coalesce(sid, psid) instead of psid throughout its grouping and session-duration
aggregation. Preserve the existing duration_avg output and averaging behavior
for other periods.

In `@backend/apps/cloud/src/analytics/v2/registry/metrics.ts`:
- Around line 29-38: Update the sessions metric’s sqlExpr to always count
distinct sessions using coalesce(sid, psid), removing the customEVFilterApplied
branch and its count(*) path. Preserve the existing integer format and session
description.

In `@backend/apps/community/src/analytics/analytics.controller.ts`:
- Line 1620: Update the Community performance eventTransformer call in
backend/apps/community/src/analytics/analytics.controller.ts:1620-1620 to pass
psid, sid, and profileId. Update the explicit performance payload branch in
backend/apps/community/src/analytics/utils/transformers.ts:123-123 to include
and preserve those three fields in the transformed event.

In `@backend/apps/community/src/analytics/analytics.service.ts`:
- Line 3485: Update the period = 'all' duration_avg CTE to select sid instead of
psid and group by sid, matching the unique session key used by the surrounding
aggregation and the other period paths.

In `@backend/migrations/clickhouse/2026_08_01_session_id.js`:
- Around line 21-27: Update the migration sequence around the INSERT backfill
and RENAME TABLE statements to add a catch-up INSERT ... SELECT from
sessions_by_visitor_day into the newly renamed sessions table immediately after
the rename. Reuse the same column mapping and validity filters, allowing
ReplacingMergeTree to deduplicate overlapping rows by (pid, sid).

In `@docs/content/docs/visitor-identification.mdx`:
- Around line 51-56: Update the session definition in the visitor-identification
documentation to state that a session starts on a visitor’s first tracked event,
rather than their first pageview; leave the remaining session duration and
salt-rotation behavior unchanged.

---

Outside diff comments:
In `@backend/apps/cloud/src/analytics/analytics.service.ts`:
- Around line 5065-5091: Update the duration_avg inner subquery in
getAnalyticsSummary so its GROUP BY uses the selected session identifier sid
instead of psid. Apply this change at
backend/apps/cloud/src/analytics/analytics.service.ts lines 5065-5091 and
5130-5156, and backend/apps/community/src/analytics/analytics.service.ts lines
3641-3667 and 3706-3732; leave the outer psid IN filtering unchanged.

---

Nitpick comments:
In `@backend/apps/cloud/src/analytics/analytics.service.ts`:
- Around line 4151-4168: Rename the sum-duration alias from avg_duration to
total_duration in both affected CTEs, including the query section around
session_duration and the corresponding earlier section, and update any
downstream references that expose it as sdur. Preserve the existing sum
expression and output behavior.

In `@backend/apps/community/src/analytics/analytics.service.ts`:
- Around line 2485-2528: In AnalyticsService.recordSessionActivity, replace the
console.error call in the catch block with this.logger.error, reusing the class
logger declared on AnalyticsService and preserving the existing failure message
and error details.

In `@backend/migrations/clickhouse/2026_08_01_session_id.js`:
- Line 30: Update the migration invocation of queriesRunner(queries) to handle
its rejected promise with a failure handler that reports the error and exits
with a nonzero status, ensuring query failures are visible to the deploy
pipeline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ca66370-556b-4585-b4e0-2adda11ec162

📥 Commits

Reviewing files that changed from the base of the PR and between 2513ba4 and 5ea2719.

📒 Files selected for processing (17)
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • backend/apps/cloud/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/analytics/heartbeat.gateway.ts
  • backend/apps/cloud/src/analytics/utils/transformers.ts
  • backend/apps/cloud/src/analytics/v2/analytics-v2.service.ts
  • backend/apps/cloud/src/analytics/v2/registry/metrics.ts
  • backend/apps/cloud/src/common/constants.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/apps/community/src/analytics/heartbeat.gateway.ts
  • backend/apps/community/src/analytics/utils/transformers.ts
  • backend/apps/community/src/analytics/v2/analytics-v2.service.ts
  • backend/apps/community/src/analytics/v2/registry/metrics.ts
  • backend/apps/community/src/common/constants.ts
  • backend/migrations/clickhouse/2026_08_01_session_id.js
  • docs/content/docs/api/stats-v2.mdx
  • docs/content/docs/visitor-identification.mdx

Comment thread backend/apps/cloud/src/analytics/analytics.service.ts
Comment thread backend/apps/cloud/src/analytics/analytics.service.ts
Comment thread backend/apps/cloud/src/analytics/analytics.service.ts Outdated
Comment thread backend/apps/cloud/src/analytics/v2/registry/metrics.ts
Comment thread backend/apps/community/src/analytics/analytics.controller.ts
Comment thread backend/apps/community/src/analytics/analytics.service.ts
Comment thread backend/migrations/clickhouse/2026_08_01_session_id.js Outdated
Comment thread docs/content/docs/visitor-identification.mdx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/migrations/clickhouse/2026_08_01_session_id.js`:
- Around line 27-40: The migration sequence around the sessions table rename and
post-rename replay must support retries after the rename succeeds but the replay
fails. Add resume handling that detects the already-renamed state and skips or
safely reconstructs the pre-rename setup before rerunning the replay INSERT,
ensuring retries do not fail on the existing sessions_v2 rename target and the
replay remains idempotent.
- Around line 33-40: Replace the fixed one-day cutoff in the migration’s replay
INSERT with a migration-start watermark captured before the initial backfill,
and filter replay rows using that timestamp so all sessions written during the
migration are included. Keep the existing validity filters and session column
mapping unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 541aafdf-a818-4e8f-9d58-038d89c03d2e

📥 Commits

Reviewing files that changed from the base of the PR and between 5ea2719 and dc774a4.

📒 Files selected for processing (9)
  • backend/apps/cloud/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/analytics/v2/registry/metrics.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/apps/community/src/analytics/utils/transformers.ts
  • backend/apps/community/src/analytics/v2/registry/metrics.ts
  • backend/migrations/clickhouse/2026_08_01_session_id.js
  • docs/content/docs/api/stats-v2.mdx
  • docs/content/docs/visitor-identification.mdx
🚧 Files skipped from review as they are similar to previous changes (6)
  • docs/content/docs/visitor-identification.mdx
  • docs/content/docs/api/stats-v2.mdx
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/utils/transformers.ts
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/analytics/analytics.service.ts

Comment thread backend/migrations/clickhouse/2026_08_01_session_id.js Outdated
Comment thread backend/migrations/clickhouse/2026_08_01_session_id.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/migrations/clickhouse/2026_08_01_session_id.js`:
- Line 32: Replace the multi-table RENAME TABLE statement with an atomic
active-table swap between sessions and sessions_v2, then archive the previous
sessions table in a separate rename. Add resume detection in this migration for
both the pre-swap and post-swap/crash states so reruns select the correct
remaining operation without renaming or losing the active sessions table.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d7bf9a71-0bef-48c6-8fb1-0f10d796f5e8

📥 Commits

Reviewing files that changed from the base of the PR and between dc774a4 and 25d8c40.

📒 Files selected for processing (1)
  • backend/migrations/clickhouse/2026_08_01_session_id.js

Comment thread backend/migrations/clickhouse/2026_08_01_session_id.js
@Blaumaus
Blaumaus merged commit 04b869c into main Aug 3, 2026
10 of 11 checks passed
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.

1 participant