मुफ़्त पूरी गाइड

Top K Songs (Spotify)

Top K Songs (Spotify) डिज़ाइन करें। इसमें Framing top-K as a heavy-hitters/frequency-estimation problem over a play-count stream, Count-Min Sketch + heap (bounded error, memory trade-off) vs exact...

00

अभ्यास checkpoints

Interview की लय संक्षिप्त रहती है, ताकि page असली design निर्णयों पर ध्यान लगा सके.

  1. 01
    Scope स्पष्ट करें
  2. 02
    Requirements + scale
  3. 03
    API + data model
  4. 04
    Architecture बनाएँ
  5. 05
    Deep dive
  6. 06
    Trade-off निर्णय
01

Requirements जो design तय करते हैं

सिर्फ requirements मत बताइए — पूछिए। हर card एक design constraint को उस clarification सवाल से जोड़ता है जो आप architecture बनाने से पहले बोल सकते हैं.

Functional requirements

01Charts को कौन-से windows चाहिए — मनमाने ranges, या स्थिर घंटा/दिन/महीना?

केवल fixed windows: पिछला घंटा, दिन, महीना, और all-time। Arbitrary time ranges scope से बाहर हैं।

02K कितना बड़ा हो सकता है?

प्रति chart 1,000 songs तक।

03एक नई play को chart में कितनी जल्दी दिखना चाहिए?

लगभग एक मिनट के भीतर — near-real-time, तुरंत नहीं।

04दो गाने rank K पर बराबर हैं - tie कैसे टूटता है?

एक तय tiebreak — पहले count, फिर song id। वही query हर बार वही list लौटाती है, इसलिए charts कभी flicker नहीं करते।

05Region और genre charts - अभी या बाद में?

बाद में। Global chart पहले ship होता है; region और genre charts एक बाद के phase में आते हैं।

06एक गाना हटा दिया जाता है - वह chart से कब जाता है?

तुरंत — एक taken-down song को एक ही बार में हर chart से गायब होना होगा, कभी counts के दोबारा compute होने का इंतज़ार नहीं।

Scope से बाहरमनमाने time ranges (from/to queries) · प्रति-उपयोगकर्ता या प्रति-region वैयक्तिकृत charts · Play-fraud detection

Non-functional requirements

01Ingestion को किस event rate की योजना बनानी चाहिए?

Peak पर प्रति second सैकड़ों हज़ार play events।

02एक chart read कितना तेज़ होना चाहिए?

Tens of milliseconds — एक chart खोलना instant महसूस हो।

03क्या संख्याओं का exact होना ज़रूरी है?

पहले पूछो, क्योंकि इससे design बदल जाता है। यहाँ मुख्य path exact counts है। ये संख्याएँ royalty reporting में जा सकती हैं, इसलिए एक systematic overcount स्वीकार्य नहीं है।

04अगर mid-hour कुछ crash हो जाए, तो क्या कुछ plays खोना स्वीकार्य है — या हर play को अंततः count होना ही होगा?

कुछ भी न खोए। हर play अंततः गिना जाना चाहिए। Recover करते समय freshness में एक छोटी गिरावट स्वीकार्य है।

05एक गाना सभी plays का आधा ले ले तो क्या?

एक ही hit song एक साथ सारे plays का बहुत बड़ा हिस्सा हड़प सकता है। इससे counting धीमी नहीं होनी चाहिए या charts बासी नहीं रहने चाहिए।

पूछते रहिए — interview एक बातचीत है

असली interview एक साफ list से कहीं गहरा probe करते हैं. ये scope सवाल उन्हें अलग करते हैं जो problem को कुरेदते हैं बनाम जो रटते हैं.

  • पिछले charts कितने समय उपलब्ध रहने चाहिए — क्या कोई पिछले March का daily chart निकाल सकता है, या केवल current windows?
  • क्या bot और fraud plays scope में हैं, या हम तक पहुँचने से पहले upstream filter हो जाते हैं?
  • daily chart के लिए "एक दिन" कौन-सा timezone तय करता है — UTC, या listener का local time?
  • all-time कितना लंबा है — क्या यह कभी reset होता है?
  • अगर कोई label एक chart position पर विवाद करे, तो क्या हमें chart से वापस raw plays तक एक audit trail चाहिए?
02

वे numbers जो architecture निर्णय मजबूर करते हैं

हर estimate को एक दबाव मानिए जो किसी component को justify करता है: cache, queue, partition, replica, worker pool, या fallback path.

01

Event ingest दर

प्रतिदिन दसियों अरब plays (एक जानबूझकर रखी 70B/day तनाव-सीमा — आज के streaming volumes से एक order आगे)70B ÷ 86,400 s ≈ 810K events/s

सिर्फ़ एक partitioned log ही इसे सोख सकता है। फिर aggregator हर partition को parallel में consume करता है।

02

Pre-aggregation का फ़ायदा

Aggregator लिखने से पहले प्रति गाना प्रति मिनट counts को batch करता हैएक गाना 10,000×/min बजा → 10,000 के बजाय 1 write

Stream pre-aggregation hot गानों के लिए store writes को 3-4 order घटा देती है।

03

Exact-count memory

मान लें ~100M अलग गाने × counter + key ≈ 50 B100M × 50 B ≈ 5 GB per window

यहाँ exact counting affordable है। इसीलिए यह मुख्य path है, sketch नहीं।

04

Sketch विकल्प

Count-Min Sketch, 4 hash rows × 2M buckets × 4 B≈ 32 MB per window vs 5 GB exact

जब per-window memory मायने रखती है (कई windows, कई regions), sketch करीब 150× कम state इस्तेमाल करता है। कीमत एक bounded overcount है।

05

Chart refresh लागत

count updates पर एक min-heap के साथ Top-1,000 बनाए रखा गयाheap update O(log 1,000) ≈ प्रति गिने song-minute लगभग 10 comparisons

chart को लगातार बनाए रखना सस्ता है। हर query पर उसे शून्य से recompute करना नहीं होता।

निर्णय उदाहरण

Numbers

आठ लाख plays प्रति सेकंड आ रहे हैं। Product सवाल छोटा है: चार तय windows के लिए top 1,000 songs, एक मिनट के भीतर fresh।

मेरी पसंद

हर play को एक partitioned event log में उतारो। Stream processor के अंदर, एक-मिनट batches में exact per-song plays गिनो, फिर मिनटों को घंटों में और घंटों को दिनों में roll up करो — हर window अपने खुद के counts रखता है। प्रति window एक छोटा heap top 1,000 को लगातार अद्यतन रखता है। API एक cached snapshot serve करता है जो हर मिनट refresh होता है और window boundaries पर pre-warm हो जाता है।

बचें

जो मैं नहीं करूँगा: plays को database increments से गिनना। 810K increments प्रति सेकंड पर यह एक write storm है, और pre-aggregate करता stream उसे मुफ़्त में सोख लेता है। मैं default के तौर पर एक Count-Min Sketch की ओर भी नहीं जाऊँगा। यहाँ exact counts प्रति window करीब 5 GB खर्चते हैं, जो affordable है, और exact संख्याएँ royalty-grade उपयोगों का दरवाज़ा खुला रखती हैं। Sketch तब सही tool है जब windows कई गुना होकर per-region charts और दर्जनों windows बन जाएँ, जहाँ एक bounded overcount स्वीकार्य हो।

कब बदलें

अगर product per-region और per-genre charts (सैकड़ों windows) जोड़ता है, तो मैं popularity से बाँट दूँगा। Hot songs को exact counters पर रखो और long tail को एक Count-Min Sketch में डालो — hot songs exact रहते हैं, और tail की कीमत लगभग शून्य है।

03

Architecture path

पहले एक पूरी तस्वीर, फिर हर path को अपना अलग diagram — write path और read path अलग traffic ढोते हैं और अलग components justify करते हैं.

पूरी तस्वीर

Overview — हर component

Client AppsPlay Event Log(Kafka)WindowedAggregatorWindow CountsChart API +CacheTop-K Heap (perwindow)chart बनाए रखोBatch Reconcilerरात्रिकालीन recount
  • Plays बाएँ से दाएँ बहती हैं; per-window heap top-1,000 answer को लगातार गर्म रखता है।
  • Dashed = real-time path से बाहर: event log पर एक nightly batch recount किसी भी drift को सुधारता है।
  • Takedowns serve time पर होते हैं: Chart API एक denylist के ज़रिए delisted songs को filter करता है, इसलिए removal तुरंत है और कभी counts के दोबारा compute होने का इंतज़ार नहीं करता।

Path 1

Write path — गिनो, फिर rank करो

Player ClientEvent Log(partitioned)Aggregator(1-min batches)Window CountsTop-K Heap

Stream के अंदर pre-aggregation एक hit गाने की 10,000 plays को एक गिने write में बदल देती है; counts बदलते ही heap update होता है।

Path 2

Read path — snapshot serve करो

ClientChart APICache (perwindow)ChartSnapshot

Reads कभी raw counts नहीं देखतीं। Snapshots हर मिनट refresh होती हैं और window boundaries पर pre-warm होती हैं, इसलिए घंटे-की-शुरुआत वाला request किसी भी अन्य जितना ही तेज़ है।

04

API और data model

optimize करने से पहले contract को inspectable बनाइए: endpoints, entities, ownership, retries, और state.

GET/charts/top?window={hour|day|month|all}&k=100

res200 [{ song_id, plays }] (≤1,000 entries)

Cache से नवीनतम ChartSnapshot serve करता है — cache key charts:{window}:{window_start}, window roll-over पर warm किया गया ताकि boundary मिनट कभी धीमा न हो।

STREAMplay-events topic (partitioned by song_id + hot-key salt)

resconsumed by the windowed aggregator

एकमात्र write path। Producers fire-and-forget; durability log से आती है, producer से नहीं।

Core entities

PlayEvent

song_id · user_id · played_at

Append-only stream record; log (retention के साथ) replay के लिए source of truth है।

WindowCount

song_id · window (hour/day/month/all) · window_start · plays

Exact per-window counts, जो windows के rollup होते समय मोटे grains पर aggregate होते हैं (घंटे → दिन → महीने)।

ChartSnapshot

window · window_start · top_k: [song_id, plays][]

वह precomputed उत्तर जो API serve करता है; हर मिनट refresh, TTL के साथ cached।

05

Deep dive दिशाएँ

interview के आखिरी एक-तिहाई के लिए एक lane चुनें. हर lane आपको topic, वह interviewer सवाल जिसका जवाब देना है, और बचने वाला failure mode देती है.

Focus

Exact या approximate

Ask

यहाँ Count-Min Sketch कब सही फ़ैसला है, और आप ठीक-ठीक क्या छोड़ते हैं — संख्याओं में?

Answer

Exact counts के साथ ही रहो। यहाँ वह करीब 5 GB प्रति window है, 100M songs ~50 bytes पर, और सस्ता है। Count-Min Sketch की तरफ़ तभी जाओ जब windows सैकड़ों region और genre charts में गुणा हो जाएँ। यह state को 150x घटाकर 32 MB कर देता है, पर हर count ऊपर जा सकता है, नीचे कभी नहीं। एक chart के लिए ठीक, royalties के लिए गलत।

बचें

प्रतिवर्त में sketch की ओर जाना — प्रति window 5 GB पर, exact counting वहनीय है और स्पष्ट रूप से अधिक उपयोगी।

Focus

एक गाना पूरी stream खा जाता है

Ask

एक hit single सभी plays का 30% ले लेता है। इसके partition का क्या होता है, और आप counts बिगाड़े बिना इसे कैसे फैलाते हैं?

Answer

Partition key को salt करो। Hit song की key में एक छोटा random suffix जोड़ो ताकि उसके plays एक partition को overload करने के बजाय कई partitions में फैल जाएँ। फिर उन partial counts को ranking से पहले एक song total में merge करो। Merge छोड़ दो और hit कई entries में बँट जाता है, हर एक अपने असली plays के एक अंश के साथ।

बचें

Partition key को salt करना पर salted counts को एक गाने के कुल में वापस merge करना भूल जाना।

Focus

Window boundary

Ask

यह 00:00:01 है और हर कोई नए hourly chart की माँग कर रहा है। वह उत्तर कहाँ से आता है?

Answer

एक ऐसे snapshot से जो पहले ही warm हो चुका था। Boundary से पहले, aggregator बंद होते window के top-1,000 को finalize करता है और उसे cache में लिख देता है। तो roll-over के बाद का पहला request एक साधारण cache hit होता है, किसी और की तरह ही। इसके बजाय chart को उस पहले request पर compute करो और तुम घंटे के सबसे busy second को सबसे धीमा बना दोगे।

बचें

Roll-over के बाद पहले request पर chart गणना करना — इसके बजाय snapshot pre-warm करो।

Focus

Aggregator मर जाता है

Ask

Stream processor एक घंटे के window में दस मिनट में crash हो जाता है। charts क्या दिखाते हैं, और counts कैसे उबरते हैं?

Answer

Charts आख़िरी cached snapshot serve करते रहते हैं। वे एक-दो minute के लिए stale हो जाते हैं, पर कभी blank नहीं। एक replacement processor अपना आख़िरी checkpoint load करता है और उस offset से event log replay करता है, सिर्फ़ मौजूदा window की tail दोबारा गिनते हुए। कुछ भी नहीं खोता, क्योंकि हर play गिने जाने से पहले log में उतरता है।

बचें

checkpoints के बिना processor memory में गिनना — log से replay ही पूरी safety कहानी है।

Focus

Time-series database क्यों नहीं

Ask

Prometheus-शैली के time-series engines समय के साथ counts store करते हैं — वे यहाँ बुरी तरह क्यों फ़िट होते हैं?

Answer

दो वजहें, और cardinality पहली है। 100M song IDs को tag values के तौर पर रखने से index और memory फट जाते हैं — write rate के चोट पहुँचाने से बहुत पहले। Query shape भी गलत है। वे engines 'समय के साथ एक series' का जवाब अच्छे से देते हैं और 'सभी series में top 1,000' का बुरी तरह — जो ठीक वही सवाल है जिसके लिए यह system बना है।

बचें

Cardinality को नज़रअंदाज़ करना: tag values के रूप में 100M song ID ठीक वही है जिस पर time-series engines अटकते हैं।

अभ्यास के लिए तैयार?

Top K Songs (Spotify) को ज़ोर से समझाइए और अपनी व्याख्या पर AI scoring पाइए.

इसे AI के साथ अभ्यास करें →