Design a Music Streaming System

Problem Design a music streaming service supporting playlists, adding songs, searching the catalog, and streaming audio to clients.

Functional requirements

  • Search the catalog by song, artist or album.
  • Create/rename/delete playlists; add, remove and reorder songs in a playlist.
  • Read a playlist with its song metadata.
  • Stream a song's audio, with seek support.
  • Track play counts for royalties and recommendations.

Non-functional requirements

  • ~500M users, ~100M DAU; ~100M songs in the catalog.
  • ~50k playlist reads/sec at peak, ~5k playlist writes/sec, ~20k searches/sec.
  • ~4M concurrent streams at peak; a 3-minute song at 160 kbps ≈ 3.5 MB → ~2 Tbps of egress, so CDN offload is mandatory, not optional.
  • Playback start (time-to-first-audio) p95 < 200 ms; search p99 < 150 ms.
  • Catalog is read-mostly; playlist data is per-user and write-light but read-heavy.

Key components

  • Catalog service: Song, Artist, Album metadata in a relational store, replicated widely and heavily cached — it barely changes.
  • Playlist service: Users, Playlists, PlaylistSongs (join table carrying position), sharded by user_id.
  • Search service: inverted index (Elasticsearch/Lucene) over song/artist/album text with typo tolerance and prefix matching for search-as-you-type.
  • Audio storage + CDN: encoded audio in an object store, segmented for adaptive bitrate, served from CDN edges. The API only issues signed URLs and never touches the bytes.
  • Play-event pipeline: play/skip events → Kafka → offline aggregation for counts, royalties and recommendations.
  • Cache layer: Redis for hot playlists and catalog rows.

Deep dives / trade-offs

  • Schema and normalization: PlaylistSongs as a join table avoids duplicating song metadata across millions of playlists — one metadata fix propagates everywhere, and an artist rename doesn't require a mass update. The cost is a join on every playlist read at 50k QPS. Denormalizing minimal song metadata into a playlist read-model (or a cached materialized playlist document) removes the join at the price of an invalidation path when catalog metadata changes. Discuss which fields are safe to denormalize (title, duration — rarely change) versus which are not (availability by region — changes constantly).
  • Playlist ordering: an integer position column forces renumbering the tail on every insert. Discuss fractional/lexicographic ranks or a linked-list representation, and what happens with concurrent reorders from two devices.
  • Relational vs search store: the same song data needs transactional integrity (catalog) and fuzzy ranked retrieval (search). You need both, synced via CDC — explain why you don't run search off LIKE queries in the primary DB.
  • Streaming path: never proxy audio through the application tier. Signed, expiring CDN URLs plus adaptive bitrate segments; discuss prefetching the next track to hide latency and offline caching for downloads.
  • Play counts at this volume are an append-only stream, not a counter UPDATE — a hot row on a viral song would serialize every write.
asked …
LeaderboardSalaryAccount