Conversation
…ng identify A session hash collision (shared NAT IP + identical user agent) lets a second user's identify() reassign the session's primary distinctId. The session GET route only consulted the session_link history when the primary was unset, so the previously linked identity disappeared from the profile with no record that two people shared the session. The route now unions the primary distinctId with the link history for both stitching and the response, and the profile renders every linked identity instead of only the latest one. Fixes umami-software#4512
|
@atirna is attempting to deploy a commit to the Umami Software Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR preserves identity history when multiple users become associated with the same session, returning the current primary identity first and rendering all linked identities in the session profile.
Confidence Score: 4/5The implementation should not merge until the generated public API contract includes the newly added The session behavior and UI coverage are coherent, but the advertised additive response field is absent from the OpenAPI contract and generated client, leaving typed API consumers unable to access it. Files Needing Attention: src/app/api/websites/[websiteId]/sessions/[sessionId]/route.ts and its generated contract
|
| Filename | Overview |
|---|---|
| src/app/api/websites/[websiteId]/sessions/[sessionId]/route.ts | Unions the primary and historical identities for stitching and exposes them as distinctIds, but the generated public response contract was not updated. |
| src/app/api/websites/[websiteId]/sessions/[sessionId]/route.test.ts | Adds coverage for preserving earlier identities, primary-first ordering, deduplication, and stitched-session counts. |
| src/app/(main)/websites/[websiteId]/sessions/SessionInfo.tsx | Renders every linked identity when the response contains more than one while preserving the existing single-ID display. |
| src/app/(main)/websites/[websiteId]/sessions/SessionInfo.test.tsx | Covers multiple linked identities and backward-compatible rendering when only distinctId is supplied. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
S[Session record] --> P[Primary distinctId]
L[session_link history] --> U[Deduplicated identity union]
P --> U
U --> Q[getLinkedSessionIds per identity]
Q --> C[stitchedSessionCount]
U --> R[GET session response]
R --> UI[SessionInfo renders linked identities]
Reviews (1): Last reviewed commit: "fix(sessions): keep earlier linked ident..." | Re-trigger Greptile
…ests The session GET response gained distinctIds but the generated contract, openapi spec and api-client types still declared only canDelete and stitchedSessionCount, and CI runs openapi:check --explicit. The two union cases now share one test.each setup. Signed-off-by: Atirna <288419661+atirna@users.noreply.github.com>
Summary
When two different real users collide on the same session hash (shared corporate NAT IP + identical User-Agent), the second
identify()reassigns the session's primarydistinctId. The session profile then showed only the latest identity, and the previously linked one disappeared with no indication two people ever shared that session.The link history in
session_linkalready records every identity ever linked to the session, but the session GET route only consulted it whilesession.distinct_idwas still null. Once the first identify set the primary, every later collision skipped the history entirely.This changes the route to always union the primary
distinctIdwithgetLinkedDistinctIds, so:distinctIds(all identities ever linked, primary first)The primary
distinctIdkeeps its current last-wins behavior, matching the ClickHouse read path (argMax(distinctId, max_time)), so no data semantics change on the write side.Fixes #4512
Why
Without this there is no way to distinguish "this session was always User B" from "this session used to be User A" on a colliding session, which is the exact data-integrity problem reported: events keep the right per-event
distinctId, but the stitched profile silently rewrites history.Verification
dev: the session GET with primaryuser-band link history[user-a, user-b]returned stitching foruser-bonly;GET keeps earlier linked identities visible after a colliding identifyfails on the pre-fix treenpx vitest run src/app/api/websites/[websiteId]/sessions/[sessionId]/route.test.ts: union +distinctIds: ['user-b', 'user-a'], both links drivestitchedSessionCountnpx vitest run src/app/(main)/websites/[websiteId]/sessions/SessionInfo.test.tsx: profile renders bothuser-banduser-anpx biome checkclean on all four changed filesNotes
distinctIdsis additive on the response; existing consumers ofdistinctIdare unchangedNeed help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.