Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Session-grouped queries treat every sessionless event as one session (wrong results, ClickHouse OOM)

Aperta
#575 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
52/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
clickhouse, sql, typescript

Direzione di ricerca

Start with funnel.sql.ts, conversion.sql.ts, sankey.sql.ts, overview.sql.ts and pages.sql.ts, following the query names listed in the issue. Add the empty-session guard to per-session query paths and make sessionless view duration zero; leave profile grouping and the listed follow-ups out of scope. Done means sessionless events no longer join into shared paths or conversions, while profile-grouped results and view and visitor counts remain unaffected.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Problem

Events tracked from a backend are stored with session_id = '' by design. This covers secret-authenticated /track calls with a server user agent when the profile has no live browser session (incoming-event-handler.ts falls back to the '' that getDeviceId returns), and events whose timestamp is in the past.

The queries that group, partition or join by session_id don't exclude ''. Every sessionless event in the project and date range therefore lands in one group, which the query treats as a single session:

Surface Query Effect
Funnel, grouped by session (the default) funnel.sql.ts funnelBase (GROUP BY session_id + windowFunnel) Step 1 from user A and step 2 from user B count as one completed funnel
Conversion, grouped by session conversion.sql.ts conversionQuery Same as the funnel: conversions that never happened
Flows (sankey) sankey.sql.ts (GROUP BY session_id + groupArray) One path stitched together from every server-side user's events. The start_event_sessions / end_event_sessions anchors match '' too
Overview → User Journey overview.sql.ts topEntriesQuery / transitionsQuery (GROUP BY session_id + groupArray(path)) Fake entry pages and transitions, plus one array holding every sessionless screen view in the range
Page-filtered overview metrics, Pages table overview.sql.ts metricsWithPageFilterQuery, pages.sql.ts topPagesQuery (lead() OVER (PARTITION BY session_id …)) A sessionless view's "duration" is the gap to the next sessionless view by anyone

Profile-grouped funnels and conversions are not affected, since they group by profile_id.

OpenPanel already treats '' as "no session" elsewhere. The import pipeline only builds sessions from session_id != '' (and never for device = 'server'), and code migration 4-add-sessions filters session_id != ''. The analytics queries just never got the same guard.

Reproduction

  1. Track two server-side events for two different profiles with a client secret and a non-browser user agent. Neither profile has a browser session:

    for p in user-a:screen_view user-b:sign_up; do
      curl -sS -X POST "$API_URL/track" -H 'content-type: application/json' \
        -H "openpanel-client-id: $CLIENT_ID" -H "openpanel-client-secret: $CLIENT_SECRET" \
        -H 'user-agent: my-backend/1.0' \
        -d "{\"type\":\"track\",\"payload\":{\"name\":\"${p#*:}\",\"profileId\":\"${p%%:*}\",\"properties\":{\"__path\":\"/x\"}}}"
    done
    
  2. SELECT profile_id, session_id, name FROM events WHERE project_id = '<project>' ORDER BY created_at shows both rows with session_id = ''.

  3. Create a funnel screen_view → sign_up grouped by session. It shows 1 conversion, although no user did both steps. Grouped by profile it correctly shows 0.

  4. Open Flows starting at screen_view. It draws screen_view → sign_up, a path nobody took.

  5. Send more sessionless screen_views with different paths, then open Overview → User Journey. The paths are stitched together across users.

On a project that sends a lot of server-side screen_views, step 5 also shows the memory problem. All of those rows go into a single groupArray, which can't be split across threads or shards.

Impact

  • Wrong numbers. Per-session funnels and conversions are inflated, and flows and user journeys show paths that don't exist. Nothing in the UI shows it, so the numbers look fine.
  • Memory blow-up. The combined "session" grows with all sessionless traffic in the range. On 2026-10-20 a self-hosted ClickHouse was OOM-killed by the Overview User Journey query grouping about 97k sessionless screen_view rows into one session. A local reproduction with 1M sessionless rows: the user-journey grouping peaked at about 92 MB for one 1,000,000-element path. With the guard it peaked at about 9 MB, and the longest path was 5.

Proposed fix

Add session_id != '' wherever a query builds per-session state, and leave profile grouping alone:

  • Funnel and conversion: add the filter to the scan only when the group is session_id.
  • Sankey: add it to the path scan and to the start/end anchor CTEs.
  • Overview user journey: add it to ordered_events.
  • The lead() OVER (PARTITION BY session_id) screen-view duration: give a sessionless view 0, the same as a session's last view, rather than the gap to someone else's event. View and visitor counts stay as they are.

Out of scope for this issue, possible follow-ups:

  • uniq(session_id) / countDistinct(session_id) session counts (the overview, realtime, widgets, the chart "sessions" segment) count '' as one extra session. That's off by at most one and doesn't blow up memory.
  • The realtime map's "latest event per session" list can show one '' row.
  • The session.performed_event filter's session_id IN (SELECT DISTINCT session_id …) matches every sessionless event once any sessionless event performed the step.
Lingua principale
TypeScript
Stelle
7.1k
Fork
516
Merge medio
21h 36m
PR unite (30g)
22

Preparare l'ambiente

  • Include un Dockerfile o un file Docker Compose
  • Nessun modello di pull request
  • Nessuna guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Openpanel-dev/openpanel

Tutte le issue di Openpanel-dev/openpanel

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.