
Querying Tophog
FreeIdentify hot actors in your ingestion pipeline.
Free · Opens the source repo
What Querying Tophog does
Querying Tophog is a specialized skill designed for developers and engineers working with PostHog’s ingestion pipeline. It enables users to efficiently analyze the tophog ClickHouse table, which aggregates data on actors involved in event processing. This skill is particularly useful during incident triage, allowing users to pinpoint which team or distinct ID is responsible for ingestion lag, high costs, or other performance issues. By leveraging the internal Metabase, users can execute targeted queries to investigate metrics that are not available through standard Prometheus monitoring.
The tophog table stores time-series data about various actors, including team IDs, distinct IDs, session IDs, and partitions. This data is crucial for understanding performance bottlenecks and optimizing resource allocation. The skill facilitates access to this data through a structured query interface, enabling users to discover metrics, analyze their cost versus volume, and identify both busy and expensive actors in the system. This is essential for maintaining the health of the ingestion pipeline and ensuring timely data processing.
To use this skill, users must log in to the internal Metabase using their SSO session, ensuring that queries are attributable to the individual user. This approach maintains security and accountability while providing access to critical data. The skill also includes a robust schema for the tophog table, allowing users to understand the structure of the data they are querying. With retention periods of 30 days, users can analyze recent performance trends and make informed decisions based on historical data.
This skill is ideal for teams managing large-scale data ingestion systems who need to troubleshoot and optimize their pipelines. It is particularly suited for engineers and data analysts who require direct access to detailed metrics that inform operational decisions.
When to use it
Use this skill when investigating performance issues in the ingestion pipeline or during incident triage.
When not to use it
This skill is not suitable for general monitoring tasks outside of the ingestion pipeline context or for users without access to the internal Metabase.
What you can build with it
Investigating Ingestion Lag
Use the skill to identify which team or distinct ID is causing delays in data ingestion.
Analyzing Costly Actors
Query to find actors that are contributing to high processing costs in the ingestion pipeline.
Performance Triage During Incidents
Utilize this skill during incidents to quickly pinpoint the source of performance degradation.
How to install Querying Tophog
View source1. Install with the skills CLI
npx skills add posthog/posthog/querying-tophog --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by posthogQuerying tophog
tophog is the ingestion pipeline's heavy-hitter tracker: workers accumulate
per-key aggregates (counts, timers) in memory and periodically flush them to
the tophog ClickHouse table via Kafka (clickhouse_tophog topic). It answers
"which actor is responsible" questions that fleet-level Prometheus metrics
cannot — per-metric label cardinality is unbounded (distinct_id,
session_id), so this data lives only in ClickHouse. Retention is 30 days.
The staff-only Django admin has a dashboard over it, but for agent-driven triage query it directly through the internal Metabase.
Access — internal Metabase, never Grafana
The production ClickHouse clusters hold customer data, so there is
deliberately no ClickHouse datasource for agents in Grafana. The
sanctioned path is the internal Metabase using the engineer's own SSO
session — per-person identity, attributable in Metabase's query history, no
standing credential. General mechanics live in the
query-clickhouse-via-metabase skill; the short version:
-
The user must run login themselves (the agent shell cannot access the Keychain):
hogli metabase:login --region eu(orus). macOS will prompt about "Chrome Safe Storage" — that'sbrowser_cookie3decrypting the browser's cookie store to capture the SSO session; one-time Allow is the right choice. -
Discover the database id — it is not stable across Metabase rebuilds:
hogli metabase:databases --region euPick "PostHog ClickHouse PROD <REGION> Data Tier" (the data tier, not the query tier — tophog lives with the events data).
-
Run queries; the cookie is read internally and never enters the transcript:
hogli metabase:query --region eu --database-id <id> <<'SQL' SELECT ... SQL
Schema
Table tophog (Distributed over sharded_tophog), ordered by
(pipeline, lane, metric, timestamp, key), partitioned by day:
| Column | Type | Notes |
|---|---|---|
timestamp | DateTime64(6) | Flush-window time; always bound it (daily partitions) |
metric | LowCardinality(String) | See inventory below |
type | LowCardinality(String) | Aggregation semantics: sum (default), max, avg |
key | Map(String, String) | The actor: access as key['team_id'], key['distinct_id'], key['partition'], key['session_id'] |
value | Float64 | The aggregated value for this flush window |
count | UInt64 | Observations in the window |
pipeline | LowCardinality(String) | e.g. analytics |
lane | LowCardinality(String) | main, overflow, historical, async, turbo |
labels | Map(String, String) | Extra non-key labels |
One row is one worker's flush window for one (metric, key) — always aggregate
on read. Read-side semantics per type (matches the admin dashboard):
CASE type
WHEN 'max' THEN max(value)
WHEN 'avg' THEN sum(value * count) / sum(count)
ELSE sum(value)
END
Metric inventory — discover live, don't trust lists
Metrics are defined inline in the ingestion pipelines (grep topHog( /
timer( / sum( in nodejs/src/ingestion/pipelines/analytics/), so the set
evolves. Always start with discovery:
SELECT metric, type, count() AS rows, sum(count) AS observations
FROM tophog
WHERE timestamp > now() - INTERVAL 1 HOUR
GROUP BY metric, type
ORDER BY metric
As of 2026-07-06 (master), the analytics metrics include
process_persons_time (timer; key: team_id, distinct_id, partition),
emitted_events[_per_distinct_id|_per_partition],
transformations_run[_per_partition],
events_dropped_by_transformation[_per_partition],
merge_events_per_distinct_id (merge-intent events: $create_alias /
$merge_dangerously with alias, $identify with $anon_distinct_id),
group_identify_events_per_distinct_id, resolved_teams, and session-replay
*_by_session_id timers.
Dimensions are deploy-gated and rows are immutable: the partition key on
process_persons_time and the merge/group-identify metrics merged 2026-07-06
and only exist in data written after that deploy reaches the environment.
Check before relying on them:
SELECT countIf(key['partition'] != '') AS with_partition, count() AS total
FROM tophog
WHERE timestamp > now() - INTERVAL 1 HOUR AND metric = 'process_persons_time'
The lens: cost vs volume
This is the load-bearing idea. Volume ranking finds busy actors; cost ranking finds slow ones — and a single lagging partition is usually a cost problem (a real incident: the top-cost actor was invisible in every volume view). Rank by summed timer value, and compute the per-event ratio to classify what you found:
| Pattern | Reading |
|---|---|
| High events, normal ms/event | Hot key (volume) — overflow/rebalance is the lever |
| Low events, high ms/event | Expensive actor — fat person properties, merge-heavy, or contended writes; scaling out will not help |
Canned queries
Top actors by person-processing cost (the incident query):
SELECT
key['team_id'] AS team_id,
key['distinct_id'] AS distinct_id,
round(sum(value)) AS total_ms,
sum(count) AS events,
round(sum(value) / sum(count), 1) AS ms_per_event,
arraySort(groupUniqArray(lane)) AS lanes
FROM tophog
WHERE timestamp > now() - INTERVAL 1 HOUR
AND metric = 'process_persons_time'
GROUP BY team_id, distinct_id
ORDER BY total_ms DESC
LIMIT 10
Scoped to one lagging partition (data written after the partition dimension deployed):
-- add to WHERE:
AND key['partition'] = '434'
Merge storms (merges are the classic person-processing cost driver):
SELECT key['team_id'] AS team_id, key['distinct_id'] AS distinct_id,
key['partition'] AS partition, sum(value) AS merge_events
FROM tophog
WHERE timestamp > now() - INTERVAL 1 HOUR
AND metric = 'merge_events_per_distinct_id'
GROUP BY team_id, distinct_id, partition
ORDER BY merge_events DESC
LIMIT 10
Generic top-10 per metric with correct type semantics (the admin dashboard's
query shape) — filter by pipeline / lane as needed:
SELECT metric, type, key, total, obs
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY metric, type ORDER BY total DESC) AS rn
FROM (
SELECT metric, type, key,
CASE type
WHEN 'max' THEN max(value)
WHEN 'avg' THEN sum(value * count) / sum(count)
ELSE sum(value)
END AS total,
sum(count) AS obs
FROM tophog
WHERE timestamp > now() - INTERVAL 1 HOUR
AND pipeline = 'analytics' AND lane = 'main'
GROUP BY metric, type, key
)
)
WHERE rn <= 10
ORDER BY metric, type, rn
Cautions
distinct_idandsession_idvalues are customer PII (often emails). Internal triage use only — never paste them into public PRs, issues, or commit messages.- Always bound
timestamp— the table is partitioned by day and holds 30 days. - Queries run under the engineer's Metabase identity and appear in their query history.
Related
monitoring-ingestion-pipeline— the Grafana-side diagnosis, including the single-partition-lag playbook that hands off to this skill for actor identification.- The pganalyze MCP — the next hop when person processing is implicated (query-level view of the persons Postgres).
Frequently asked questions about Querying Tophog
Similar skills
Spring Boot Testing
Master testing techniques for Spring Boot 4 applications.
GitHub Issues
Manage GitHub issues efficiently with MCP tools.
Geofeed Tuner
Optimize your IP geolocation feeds in CSV format.
Batch Files
Master Windows batch scripting for automation and task management.
Adobe Illustrator Scripting
Automate your Illustrator workflows with ExtendScript.
Plugin Structure
Create and organize Claude Code plugins effectively.
