Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

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

オープン
#575 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
52/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
clickhouse, sql, typescript

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

説明

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.
主要言語
TypeScript
スター
7.2k
フォーク
523
平均マージ
19時間 23分
マージ済み PR(30日)
25

環境構築

  • Dockerfile または Docker Compose ファイルあり
  • プルリクエストのテンプレートなし
  • コントリビューションガイドなし

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Openpanel-dev/openpanel のほかの issue

Openpanel-dev/openpanel の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。