Case study 05 · Marketplace and revenue
Live analytics for advertisers at two million writes a day
I built the analytics layer of an advertising marketplace. Each customer's annual report came back in under a second, even for accounts with 900+ listings.
2M+ writes per day
into the time-series database
Annual report under one second at 900+ listings
reading 365 daily rollups per listing
The story in 5 steps
Stack
- InfluxDB
- MySQL
- WebSockets
- Node.js
- AWS
01 · Context
I designed and built the analytics layer for an advertising marketplace.
I designed and built the analytics layer for an advertising marketplace. It took over two million writes a day, fed a dashboard that updated live, and returned a full year of history in under a second.
High-volume writes, live reads and fast yearly reports normally exclude one another. A database that ingests quickly is slow at complex queries, and a year of raw data at that rate is hundreds of millions of rows. No single database does all three jobs, so I split them between two.
02 · Could not change
On an advertising platform, what a customer pays for is proof that their listing was seen.
On an advertising platform, what a customer pays for is proof that their listing was seen. When a repair shop pays for a listing, the only question that matters at renewal is how many people saw it and how many called.
The analytics are what justifies the invoice, which rules out buying an off-the-shelf tool. General web analytics measures traffic to a site. It does not measure the value delivered to one advertiser, and it cannot put that number back into the advertiser's account, in their dashboard, next to their renewal.
The system had to take writes at volume, serve live reads and aggregate a whole year quickly, all at the same time. Each of these is easy on its own.
03 · What I did
A time-series database for ingest, with a deliberately small index. I put identifiers such as listing, user and device into fields.
A time-series database for ingest, with a deliberately small index. I put identifiers such as listing, user and device into fields. In a time-series store tags are indexed and fields are not, so putting a listing ID in a tag means every new listing creates a new series, and eventually the index suffocates. This is the single most common way these installations fail.
Keeping listing IDs out of the index meant the time-series store could not group by listing efficiently, so that grouping was done in the relational database.
Daily rollups in the relational database. Raw events were kept for seven days, with a daily export to object storage as the archive. A daily summary per listing landed in the relational database, and that is what the dashboard and the invoices queried.
Because of the rollups, the annual report never touched a raw row: it read 365 summarized rows per listing.
Speed measured on the largest accounts. The under-one-second figure holds for accounts with over 900 listings, where a year of daily rollups is roughly 330,000 rows aggregated in a single query. A report that returns in under a second on an account with three listings proves nothing about the architecture, so I measured at the worst case rather than the average.
Total and unique views in separate buckets. I counted unique views by session and by page URL, so someone who searched in two cities was not counted as one view: they were looking for two different things. Totals are the bigger number and look better, while uniques tell an advertiser how many different people saw them. I kept both, so neither number had to be hidden.
Bot filtering at three layers. I filtered bots at the firewall, by known addresses and by user agent. On an advertising platform an inflated view count damages trust, because an advertiser who sees five hundred views and no calls stops believing the number, and then stops believing the invoice.
04 · Result
2M+ writes per day into the time-series database
- 2M+ writes per day into the time-series database
- Advertisers read their numbers live in the dashboard
- Annual report under one second at 900+ listings, reading 365 daily rollups per listing
- The same data feeds the yearly report sent before each renewal
05 · Differently
Seven days of raw retention was chosen early and never revisited.
Seven days of raw retention was chosen early and never revisited. It was almost certainly right, but I never checked what we lost by it: whether anyone ever needed to reprocess a window longer than a week, and what it would have cost to keep thirty days. I picked a number and moved on.
Related services
The services that draw on this work.
- Web developmentFull-stack web developmentAn analytics layer that takes two million writes a day, updates the customer’s dashboard live and returns a year of history in under a second.
- Cloud and DevOpsCloud cost reviewMarketplace analytics at 2M writes a day: storage was 95% of the managed time-series bill, and our own instance came out about $27,000 a year cheaper, by our estimate. A retention change cut storage to $1.31 a month, and the managed service still cost more, this time because it charges for every query a customer’s dashboard runs.
- Web developmentAdmin dashboards and internal toolsA live dashboard for advertisers on a US platform for truck services. For accounts with more than 900 listings, a full year of history loads in under a second.
- Backend, data and integrationsProduct analytics and event trackingTotal and unique views stored separately, and bot traffic filtered at three layers: the firewall, known addresses and the user agent. An inflated count costs you the customer’s trust.
- Backend, data and integrationsHigh-write data systemsOver two million writes a day go into a time-series store with a deliberately small index, and each advertiser reads the results live in their dashboard.
- Backend, data and integrationsSQL and data warehouse optimizationFor advertisers on a US marketplace, the yearly report opens in under a second even with over 900 listings, because it reads only the daily rollups.
- Backend, data and integrationsReal-time and WebSocketsA WebSocket service on a US marketplace carrying live dashboard updates for advertisers, one-to-one notifications, forced logout and a lock that stops two admins editing the same record. The analytics behind those dashboards take in more than two million writes a day.
Related writing
Posts that came out of this project.
- 19 SEP 2026 · 8 minSelf-hosted InfluxDB stayed cheaper because of per-query pricingAt 2 TB, self-hosting saved about $27,000 a year. Once a retention change cut managed storage to $1.31, it still won because every dashboard query is billed.
- 17 SEP 2026 · 8 minServerless chat on AWS: $19 a month at 100 users, and its limitsWith groups of ten, a single server becomes cheaper at about 1,200 users. New connections stay capped at 500 a second per account until AWS raises the quota.
- 15 SEP 2026 · 9 minServerless WebSocket costs: connection minutes rarely dominateConnection minutes dominated in only 3 of 18 modeled scenarios. Below 1,000 users, $16.64 a month for Redis and a VPC endpoint is most of the bill.
- 29 JUL 2026 · 6 minInfluxDB cardinality: keep user and device IDs out of tagsIDs in tags grow the InfluxDB index with every new visitor. With them in fields and a daily MySQL rollup, a year for 900+ listings loads in under a second.
Case study 05
Working on a similar problem? We reply within one business day.
The first call takes 30 minutes, and you’re under no obligation.