Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto
#575 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
52/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
clickhouse, sql, typescript

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.
Lenguaje dominante
TypeScript
Estrellas
7.1k
Forks
516
Merge medio
21 h 36 min
PR fusionados (30 d)
22

Preparar el entorno

  • Incluye un Dockerfile o un archivo de Docker Compose
  • Sin plantilla de pull request
  • Sin guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de Openpanel-dev/openpanel

Todos los issues de Openpanel-dev/openpanel

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.