01Charts को कौन-से windows चाहिए — मनमाने ranges, या स्थिर घंटा/दिन/महीना?
केवल fixed windows: पिछला घंटा, दिन, महीना, और all-time। Arbitrary time ranges scope से बाहर हैं।
मुफ़्त पूरी गाइड
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...
Interview की लय संक्षिप्त रहती है, ताकि page असली design निर्णयों पर ध्यान लगा सके.
सिर्फ requirements मत बताइए — पूछिए। हर card एक design constraint को उस clarification सवाल से जोड़ता है जो आप architecture बनाने से पहले बोल सकते हैं.
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
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 एक साफ list से कहीं गहरा probe करते हैं. ये scope सवाल उन्हें अलग करते हैं जो problem को कुरेदते हैं बनाम जो रटते हैं.
हर estimate को एक दबाव मानिए जो किसी component को justify करता है: cache, queue, partition, replica, worker pool, या fallback path.
Event ingest दर
प्रतिदिन दसियों अरब plays (एक जानबूझकर रखी 70B/day तनाव-सीमा — आज के streaming volumes से एक order आगे)70B ÷ 86,400 s ≈ 810K events/s
सिर्फ़ एक partitioned log ही इसे सोख सकता है। फिर aggregator हर partition को parallel में consume करता है।
Pre-aggregation का फ़ायदा
Aggregator लिखने से पहले प्रति गाना प्रति मिनट counts को batch करता हैएक गाना 10,000×/min बजा → 10,000 के बजाय 1 write
Stream pre-aggregation hot गानों के लिए store writes को 3-4 order घटा देती है।
Exact-count memory
मान लें ~100M अलग गाने × counter + key ≈ 50 B100M × 50 B ≈ 5 GB per window
यहाँ exact counting affordable है। इसीलिए यह मुख्य path है, sketch नहीं।
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 है।
Chart refresh लागत
count updates पर एक min-heap के साथ Top-1,000 बनाए रखा गयाheap update O(log 1,000) ≈ प्रति गिने song-minute लगभग 10 comparisons
chart को लगातार बनाए रखना सस्ता है। हर query पर उसे शून्य से recompute करना नहीं होता।
निर्णय उदाहरण
आठ लाख 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 की कीमत लगभग शून्य है।
पहले एक पूरी तस्वीर, फिर हर path को अपना अलग diagram — write path और read path अलग traffic ढोते हैं और अलग components justify करते हैं.
पूरी तस्वीर
Path 1
Stream के अंदर pre-aggregation एक hit गाने की 10,000 plays को एक गिने write में बदल देती है; counts बदलते ही heap update होता है।
Path 2
Reads कभी raw counts नहीं देखतीं। Snapshots हर मिनट refresh होती हैं और window boundaries पर pre-warm होती हैं, इसलिए घंटे-की-शुरुआत वाला request किसी भी अन्य जितना ही तेज़ है।
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
PlayEventsong_id · user_id · played_at
Append-only stream record; log (retention के साथ) replay के लिए source of truth है।
WindowCountsong_id · window (hour/day/month/all) · window_start · plays
Exact per-window counts, जो windows के rollup होते समय मोटे grains पर aggregate होते हैं (घंटे → दिन → महीने)।
ChartSnapshotwindow · window_start · top_k: [song_id, plays][]
वह precomputed उत्तर जो API serve करता है; हर मिनट refresh, TTL के साथ cached।
interview के आखिरी एक-तिहाई के लिए एक lane चुनें. हर lane आपको topic, वह interviewer सवाल जिसका जवाब देना है, और बचने वाला failure mode देती है.
यहाँ Count-Min Sketch कब सही फ़ैसला है, और आप ठीक-ठीक क्या छोड़ते हैं — संख्याओं में?
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 वहनीय है और स्पष्ट रूप से अधिक उपयोगी।
एक hit single सभी plays का 30% ले लेता है। इसके partition का क्या होता है, और आप counts बिगाड़े बिना इसे कैसे फैलाते हैं?
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 करना भूल जाना।
यह 00:00:01 है और हर कोई नए hourly chart की माँग कर रहा है। वह उत्तर कहाँ से आता है?
एक ऐसे 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 करो।
Stream processor एक घंटे के window में दस मिनट में crash हो जाता है। charts क्या दिखाते हैं, और counts कैसे उबरते हैं?
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 कहानी है।
Prometheus-शैली के time-series engines समय के साथ counts store करते हैं — वे यहाँ बुरी तरह क्यों फ़िट होते हैं?
दो वजहें, और 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 पाइए.