Design a Product Analytics System, stage 4 of 9: break it
Slow again, for a different reason
Properties are stored as one JSON string column, because every customer sends different keys. Select the lines that point at the cause.
System so far· 8 parts
Select a component to see what it is responsible for and which state it owns.
- 1Customers' apps → Capture API: Batches of events
- 2Customers' dashboards → Query service: Chart request
- 3Query service → Postgres: Teams and saved charts
- 4Query service → Events (column store): Aggregate by column
- 5Capture API → Event stream: Append, then acknowledge
- 6Ingestion workers → Event stream: Read a partition
- 7Ingestion workers → Postgres: Who is this ID?
- 8Ingestion workers → Events (column store): Batch insert
What you need to know
A column store only helps when the data is actually in columns. A single JSON column holding every property is, for those fields, a row store again: to read one property, the query reads and parses the whole blob for every row.
Check
A query profile shows 98% of blocks skipped by the sort key, but 37 of 38 GB read are the properties column. What's the problem?The usual fix is a hybrid: keep the JSON for the long tail of rare keys, and promote the most-queried keys to real, typed columns, extracted from the JSON as rows are inserted. Which keys? Look at what queries actually filter on. Old data needs a backfill for the new columns to be useful over long ranges.