TRICKYwalkthrough
Rights Management and Licensing Metadata
A track available in the US might be blocked in Germany due to a licensing dispute with the local collecting society. The same track might have different rights holders in different territories: the label owns the recording, the publisher owns the composition, and a distributor handles a specific region.
The constraint: we must resolve territorial licensing for every play request across 180 countries, and a mistake means either blocking legitimate plays (lost revenue) or allowing unauthorized plays (legal liability). We store rights metadata alongside each track: track_id, territory_codes (list of countries where the track is licensed), license_start and license_end dates, rights_holder_ids (label, publisher, distributor), and royalty_split percentages.
“Spotify manages over 5 million rights holder relationships globally.”
We chose to denormalize by track_id and country_code (not normalize into separate territory and rights-holder tables) because the hot-path query is: "Can this user in country X play track Y right now?" A denormalized lookup is a single key-value fetch; a normalized schema requires 3 joins across territory, rights-holder, and license-period tables. Total storage: .
This fits comfortably in PostgreSQL with a Redis cache in front. We cache the top 10M most-played tracks' rights in Redis for sub-millisecond lookups: .
Rights changes (new licenses, territory additions, takedowns) flow through an event-driven pipeline: the rights management system publishes a change event to Kafka, which updates both PostgreSQL and the Redis cache within seconds. Trade-off: denormalization means updating a rights holder's information requires updating every row for their tracks (fan-out write), but rights changes are rare (hundreds per day) while play authorization checks happen at 139K per second.
We optimize for the hot path. What if the interviewer asks: how do we handle a takedown request for a viral track?
We process takedowns with P0 priority: the Kafka consumer invalidates the Redis cache immediately, and the PostgreSQL update follows. The track becomes unplayable within 30 seconds of the takedown event.
Related concepts