সেশন ২৫

Twitter / Instagram Feed - এক ছবি, দুই কোটি feed

এই সেশনের লক্ষ্য: একটা photo feed system শুরু থেকে design করা: requirement, সংখ্যার হিসাব, API আর data model। তারপর home timeline বানানোর দুই পথ (fan-out on read আর fan-out on write), celebrity problem আর hybrid approach, Redis এ timeline cache, cursor pagination, ছবির জন্য object storage আর CDN, আর লাখো like গোনার কৌশল বোঝা।

০০ যে প্রশ্নগুলোর উত্তর খুঁজবো

Facebook, Instagram বা X (আগের Twitter) খুললে প্রথমেই যে লম্বা তালিকাটা দেখেন, সেটাই feed। দেখতে খুব সাধারণ: যাদের follow করেন, তাদের নতুন post গুলো একটার নিচে আরেকটা। কিন্তু কোটি মানুষের জন্য এই তালিকা এক সেকেন্ডের কম সময়ে বানানো system design এর সবচেয়ে প্রিয় interview প্রশ্নগুলোর একটা।

এই সেশনটা একটা পূর্ণ case study। সেশন ২৩ এর মতোই আমরা ধাপে ধাপে এগোবো: requirement, সংখ্যার হিসাব, API, data model, design, তারপর কঠিন জায়গাগুলোতে গভীরে নামা। আগের সেশনগুলোর cache, shard, CDN আর event এখানে একসাথে কাজ করবে।

  • ১ Feed system টা ঠিক কী কী করবে? (functional ও non-functional requirement)
  • ২ কত user, কত post, কত ছবি? (back-of-envelope estimation)
  • ৩ API আর data model দেখতে কেমন?
  • ৪ Home timeline কখন বানাবো: fan-out on read নাকি fan-out on write?
  • ৫ ২ কোটি follower এর একজন post করলে কী হয়? (celebrity problem, hybrid approach)
  • ৬ Timeline Redis এ কীভাবে রাখবো, আর পরের page কীভাবে আনবো? (timeline cache, cursor pagination)
  • ৭ এত ছবি কোথায় রাখবো, আর মানুষের কাছে কীভাবে পৌঁছাবো? (object storage, CDN)
  • ৮ এক post এ সেকেন্ডে হাজার like কীভাবে গুনবো, আর feed এর ক্রম কে ঠিক করে? (sharded counter, ranking)
  • ৯ সব মিলিয়ে: পুরো design, bottleneck আর trade-off গুলো কী?

০১ Feed system টা ঠিক কী কী করবে?

রাত দশটা সাতচল্লিশ। মিরপুরে শেষ বলে ছক্কা হাঁকিয়ে বাংলাদেশকে জিতিয়েছেন তাওসিফ আলম। ড্রেসিংরুমে ফিরে ট্রফি হাতে দলের একটা ছবি তুললেন, আর “আড্ডা” app এ post করলেন। Caption ছোট: “এই জয় আপনাদের।” তাঁর follower ২ কোটি।

ঢাকার এক অফিসে তানভীর তখন আড্ডার feed team এর on-call engineer। খেলা দেখে সে-ও লাফিয়ে উঠেছিলো। এমন সময় রাজশাহী থেকে আম্মুর ফোন এলো: “তাওসিফের ছবিটা নাকি এসেছে? আমার feed এ তো দেখছি না!” তানভীর dashboard এর দিকে তাকালো। একটা graph খাড়া উপরে উঠে যাচ্ছে।

এই রাতটা কীভাবে ভালোভাবে পার করা যায়, সেটাই এই সেশনের গল্প। তবে তার আগে জানতে হবে, system টার আসলে কী কী করার কথা।

Functional requirement: system কী করবে

Functional requirement মানে system এর কাজের তালিকা, অর্থাৎ user কী কী করতে পারবেন। আড্ডার জন্য তালিকাটা এমন:

একটা নামের পার্থক্য এখনই মনে রাখুন। Home timeline হলো আপনি যাদের follow করেন, তাদের সবার post মিলিয়ে বানানো তালিকা। User timeline হলো একজন মানুষের নিজের post গুলো, যেমন তাওসিফের profile এ গেলে যা দেখেন। User timeline বানানো সহজ, এক জনের post এক জায়গা থেকে পড়লেই হয়। কঠিন হলো home timeline, কারণ সেটা শত শত মানুষের post মিলিয়ে বানাতে হয়।

Comment, story, message আর search এই সেশনে নেই। Search আমরা সেশন ২৪ এ দেখেছি।

Non-functional requirement: কতটা ভালোভাবে করবে

Non-functional requirement মানে system এর গুণ: কত দ্রুত, কত নির্ভরযোগ্য, কত বড়। আড্ডার জন্য:

Requirementলক্ষ্যকেন
Feed খোলার latency১০০টা request এর ৯৯টাই ২০০ ms এর কম সময়েApp খুলে ফাঁকা screen কেউ দেখতে চান না
Availability৯৯.৯৫%Feed না খুললে মানুষ app ছেড়ে চলে যান
নতুন post পৌঁছানো৫ সেকেন্ডের মধ্যেকয়েক সেকেন্ড দেরি চলে, কয়েক মিনিট নয়
ConsistencyEventual consistency চলবেআম্মু ছবিটা দুই সেকেন্ড পরে দেখলে কোনো ক্ষতি নেই

শেষ লাইনটা পুরো design এর দিক ঠিক করে দেয়। Eventual consistency মানে সব জায়গায় data একটু পরে হলেও শেষ পর্যন্ত মিলে যায় (সেশন ০৩)। ব্যাংকের balance এ এটা চলে না। কিন্তু feed এ চলে। এই ছাড়টুকু আছে বলেই আমরা পরে অনেক কাজ background এ সরিয়ে দিতে পারবো।

০২ কত user, কত post, কত ছবি?

Design শুরুর আগে সংখ্যাগুলো একবার মোটামুটি হিসাব করে নিতে হয়। খাতার কোণে করা এই দ্রুত হিসাবকে বলে back-of-envelope estimation (সেশন ২৩)। এখানে নিখুঁত সংখ্যা দরকার নেই। দরকার শুধু বোঝা, কোন জিনিসটা হাজারের ঘরে আর কোনটা কোটির ঘরে।

আড্ডার জন্য কিছু সংখ্যা ধরে নিই, তারপর ধাপে ধাপে গুণ করি:

# ধরে নেওয়া সংখ্যা (আড্ডা app)
Registered user        = 8 কোটি   (80M)
Daily active user (DAU) = 3 কোটি  (30M)
1 দিন                   = 86,400 s  (হিসাব সহজ করতে ~1 লাখ s ধরি)

# 1. Post (write)
প্রতি DAU দিনে 0.2 টা post   → 30M x 0.2   = 6M post/day
Average                       → 6M / 86,400  ≈ 70 post/s
Peak (ম্যাচের রাত, x5)        →               ≈ 350 post/s

# 2. Feed open (read)
প্রতি DAU দিনে 10 বার feed    → 30M x 10    = 300M feed load/day
Average                       → 300M / 86,400 ≈ 3,500 load/s
Peak (x5)                     →               ≈ 17,500 load/s
Read : Write                  → 3,500 : 70    ≈ 50 : 1   (read-heavy)

# 3. Fan-out (timeline এ post_id বসানো)
গড়ে 200 জন follower          → 6M x 200    = 1.2B insert/day
Average                       → 1.2B / 86,400 ≈ 14,000 insert/s

# 4. Storage
Post এর text + metadata ~1 KB → 6M x 1 KB    = 6 GB/day     ≈ 2.2 TB/year
70% post এ photo, 3 size মিলে ~2 MB
                              → 4.2M x 2 MB  = 8.4 TB/day   ≈ 3 PB/year

# 5. Egress (ছবি download)
প্রতি feed load এ ~10 টা medium ছবি x 300 KB = 3 MB
                              → 300M x 3 MB  = 900 TB/day
                              → 900 TB / 86,400 s ≈ 10 GB/s ≈ 80 Gbps

এই হিসাব থেকে চারটা সিদ্ধান্ত বেরিয়ে আসে:

  1. Read অনেক বেশি: প্রতি একটা post এর বিপরীতে প্রায় ৫০ বার feed খোলা হয়। তাই design এর মূল লক্ষ্য হবে feed পড়া সস্তা করা, দরকার হলে লেখার সময় একটু বেশি খাটুনি করে।
  2. Fan-out এর সংখ্যা বিশাল: দিনে ৬০ লাখ post, কিন্তু প্রতিটা গড়ে ২০০ জনের কাছে যায়। মোট দিনে ১২০ কোটি বার কোথাও না কোথাও লিখতে হয়। এই সংখ্যাটা মনে রাখুন, পরের section এ কাজে লাগবে।
  3. ছবি database এ রাখা যাবে না: বছরে ৩ PB ছবি। এর জন্য আলাদা ধরনের storage লাগবে (Q7)।
  4. ছবি পাঠানোও বড় কাজ: সেকেন্ডে প্রায় ৮০ Gbps ছবি বের হয়। এক data center থেকে এটা পাঠানো সম্ভব নয়, CDN লাগবে।

০৩ API আর data model দেখতে কেমন?

API

API হলো app আর server এর মধ্যে কথা বলার নিয়ম। আড্ডার মূল API গুলো এমন:

# 1. ছবি upload এর জন্য একটা URL চাওয়া
POST /v1/media/upload-url
  → 200 { "media_id": "m_77", "upload_url": "https://...", "expires_in": 900 }

# 2. Post তৈরি করা (ছবি আগেই upload হয়ে গেছে)
POST /v1/posts
  body: { "caption": "এই জয় আপনাদের", "media_ids": ["m_77"] }
  → 201 { "post_id": 9001, "created_at": "2026-11-07T22:47:03+06:00" }

# 3. Home timeline পড়া (cursor দিয়ে পরের page)
GET /v1/feed?limit=20&cursor=8410
  → 200 { "posts": [ ...20 টা post... ], "next_cursor": "7702" }

# 4. Like / unlike (একই request দুইবার এলেও ফল একই)
PUT    /v1/posts/9001/like
DELETE /v1/posts/9001/like

# 5. Follow
PUT /v1/users/taosif/follow

তিনটা জিনিস খেয়াল করুন:

Data model

users    (user_id PK, name, avatar_url, follower_count, is_celebrity)

posts    (post_id PK, author_id, caption, media_keys, created_at, like_count)
         -- post_id সময়ের সাথে বাড়ে: নতুন post এর id সবসময় বড়
         -- shard key: author_id  (এক জনের সব post এক shard এ)
         -- index: (author_id, created_at DESC)

follows  (follower_id, followee_id, created_at)
         -- PK: (follower_id, followee_id)   → "আমি কাকে follow করি"
         -- আরেকটা কপি: (followee_id, follower_id) → "আমাকে কে follow করে"

likes    (post_id, user_id, created_at)
         -- PK: (post_id, user_id)   → একজন একটা post এ একবারই like দিতে পারে

# Redis (cache, database নয়)
timeline:{user_id}   → sorted set: score = post_id, member = post_id (সর্বোচ্চ 800 টা)

কয়েকটা সিদ্ধান্তের পেছনের কারণ:

০৪ Home timeline কখন বানাবো: fan-out on read নাকি fan-out on write?

এটাই এই সেশনের মূল প্রশ্ন। আম্মু ৩০০ জনকে follow করেন। তাঁর home timeline এ এই ৩০০ জনের post মিলিয়ে, নতুন থেকে পুরোনো ক্রমে সাজাতে হবে। প্রশ্ন হলো, এই মেলানোর কাজটা কখন হবে? আম্মু feed খোলার সময়, নাকি কেউ post করার সময়?

একটা জিনিস অনেক জায়গায় ছড়িয়ে দেওয়াকে বলে fan-out, হাতপাখা যেমন এক বিন্দু থেকে চারদিকে ছড়ায়। Feed এর ক্ষেত্রে একজনের post তাঁর সব follower এর কাছে পৌঁছানোই হলো fan-out।

পথ ১: fan-out on read (feed খোলার সময় বানানো)

সবচেয়ে সোজা পথ হলো, post করার সময় কিছুই না করা। Post শুধু database এ save হয়। আম্মু যখন feed খোলেন, তখনই system ৩০০ জনের post খুঁজে এনে সাজায়। এর নাম fan-out on read, কারণ ছড়ানোর কাজটা হয় পড়ার সময়। একে pull model ও বলে, কারণ পড়ার সময় সব জায়গা থেকে টেনে আনা হয়।

1 আম্মু feed খোলেন GET /v1/feed 2 Follow list আনা আম্মু 300 জনকে follow করেন ~5 ms 3 300 জনের latest post খোঁজা 300 টা query, অনেক shard এ ছড়ানো 80-150 ms সবচেয়ে ধীর ধাপ 4 সব মিলিয়ে সময় অনুযায়ী sort কয়েক হাজার post থেকে বাছাই ~10 ms 5 সবচেয়ে নতুন 20 টা ফেরত মোট ~100-170 ms প্রতিবার feed খুললেই 300 টা query 17,500 load/s x 300 = ~52 লাখ query/s
Fan-out on read এ post করার সময় কিছুই হয় না। সব কাজ হয় feed খোলার মুহূর্তে: follow list আনা, ৩০০ জনের post খোঁজা, তারপর sort করা। একজনের জন্য এটা ঠিক আছে, কিন্তু peak এ সেকেন্ডে ১৭,৫০০ জন feed খুললে database এর উপর অর্ধ কোটির বেশি query পড়ে।

একজন user এর জন্য এটা চমৎকার। Post করা খুব সস্তা, আর feed সবসময় একদম fresh। সমস্যা হয় সংখ্যাটা বড় হলে। ম্যাচের রাতে সেকেন্ডে ১৭,৫০০ জন feed খোলেন। প্রত্যেকের জন্য ৩০০টা query মানে সেকেন্ডে প্রায় ৫২ লাখ query। একই কাজ বারবার হচ্ছে: রাফি আর আম্মু দুজনেই রুমানাকে follow করলে রুমানার post দুইজনের জন্য আলাদা আলাদা খোঁজা হয়।

পথ ২: fan-out on write (post করার সময় বানানো)

উল্টো পথটা হলো, ভারী কাজটা post করার সময়েই সেরে ফেলা। প্রত্যেক user এর জন্য একটা তৈরি করা তালিকা রাখা হয়, যেন একটা ব্যক্তিগত ডাকবাক্স। রুমানা post করলে একটা worker তাঁর ৩০০ জন follower এর প্রত্যেকের ডাকবাক্সে post এর id টা ফেলে দেয়। এর নাম fan-out on write, কারণ ছড়ানোর কাজটা হয় লেখার সময়। একে push model ও বলে।

WRITE PATH READ PATH 1. রুমানা post করেন POST /v1/posts 2. Post DB তে save post_id = 8977 3. Queue তে event post_created (8977) 4. Fanout worker follower list: 300 জন আম্মু ZADD রাফি ZADD +298 ZADD আম্মু feed খোলেন GET /v1/feed Feed service শুধু timeline পড়ে timeline:আম্মু একটাই Redis read 20 টা post id ~5 ms ভারী কাজটা post করার সময়েই হয়ে গেছে, তাই read সস্তা
Fan-out on write এ কাজটা হয় post করার সময়। রুমানা post করলে একটা worker তার ৩০০ জন follower এর প্রত্যেকের timeline এ post_id টা বসিয়ে দেয়। পরে আম্মু feed খুললে শুধু নিজের timeline টা পড়লেই চলে: একটা Redis read, প্রায় ৫ ms।

এখন আম্মু feed খুললে কোনো খোঁজাখুঁজি নেই। শুধু নিজের ডাকবাক্স খুললেই হয়: একটা Redis read, ৫ ms এর মতো। Post করা এখন একটু ধীর, কারণ পেছনে ৩০০টা write হয়। কিন্তু রুমানাকে সেই অপেক্ষা করতে হয় না। Post save হলেই তিনি “posted” দেখেন। বাকি কাজ queue এর মাধ্যমে background এ চলে (সেশন ০৮)।

পাশাপাশি তুলনা

Fan-out on read (pull)Fan-out on write (push)
Post করার খরচখুব কম, একটা writeবেশি, প্রতি follower এ একটা write
Feed খোলার খরচবেশি, শত শত queryখুব কম, একটা read
Feed খোলার latency১০০-১৭০ ms৫-১০ ms
নতুন post কখন দেখা যায়সাথে সাথেকয়েক সেকেন্ড পরে
কোথায় ভালোকম পড়া হয় এমন feedবেশি পড়া হয় এমন feed

Q2 এর হিসাব মনে আছে? Read, write এর ৫০ গুণ। তাই আড্ডার জন্য fan-out on write ই স্বাভাবিক পছন্দ। লেখার সময় একবার খেটে নিলে পড়ার সময় ৫০ বার সুবিধা পাওয়া যায়।

মূল ভাবনা

কাজটা একবার না একবার করতেই হবে। প্রশ্ন শুধু, কখন করবেন। যে কাজ বেশিবার হয় (feed পড়া), সেটা সস্তা রাখুন। খরচটা সরিয়ে দিন কম ঘটা কাজের দিকে (post করা)।

০৫ ২ কোটি follower এর একজন post করলে কী হয়?

এবার ফিরে আসি রাত দশটা সাতচল্লিশে। আড্ডা তখন শুধু fan-out on write চালাতো। তাওসিফ ছবিটা post করলেন। Worker দের সামনে এখন একটা post এর জন্য ২ কোটি ZADD (Redis এর sorted set এ একটা item বসানোর command, Q6 এ বিস্তারিত)।

তাওসিফ ছবি post করেন রাত 10:47 · post_id 9001 Fanout queue: 2 কোটি ZADD সব worker মিলে সেকেন্ডে 1 লাখ ZADD করতে পারে 1 মিনিট: 60 লাখ 0s 50s 100s 150s 200s প্রথম follower দেখেন শেষ follower দেখেন: 3 মিনিট 20 সেকেন্ড পর এই 200 সেকেন্ড বাকি সবার post লাইনে আটকে থাকে রুমানার মতো সাধারণ user এর post ও দেরিতে পৌঁছায় Redis এ এক ধাক্কায় ~320 MB নতুন data
একটা post এর জন্য ২ কোটি timeline এ লিখতে হয়। সেকেন্ডে ১ লাখ ZADD হলেও পুরো কাজ শেষ হতে ২০০ সেকেন্ড লাগে। এর মধ্যে আম্মুর feed এ ছবিটা নাও আসতে পারে, আর অন্য সবার post ও এই লম্বা লাইনের পেছনে অপেক্ষা করে।

হিসাবটা সোজা। আড্ডার সব worker মিলে সেকেন্ডে ১ লাখ timeline write করতে পারে। স্বাভাবিক দিনে দরকার হয় সেকেন্ডে ১৪,০০০, তাই অনেক জায়গা ফাঁকা থাকে। কিন্তু ২ কোটি write শেষ করতে লাগে ২০০ সেকেন্ড, মানে তিন মিনিটের বেশি।

এই তিন মিনিটে তিনটা সমস্যা হয়:

  1. শেষের দিকের follower রা দেরিতে দেখেন: আম্মুর timeline এ ছবিটা হয়তো তিন মিনিট পরে পৌঁছায়। আর তিনি তখন ফোনে তানভীরকে জিজ্ঞেস করছেন।
  2. বাকি সবাই আটকে যায়: তাওসিফের ২ কোটি job এর পেছনে লাইনে দাঁড়িয়ে আছে রুমানার মতো সাধারণ মানুষের post ও। তাঁদের বন্ধুরাও তিন মিনিট কিছু পান না।
  3. অনেক কাজ বৃথা যায়: ২ কোটি follower এর বড় একটা অংশ আজ রাতে app খুলবেনই না। তাঁদের timeline এ লেখাটা শুধু Redis এর memory খরচ করলো।

আর তাওসিফ যদি পরের দশ মিনিটে আরো তিনটা ছবি দেন? লাইন আরো লম্বা হয়। এই সমস্যার নাম celebrity problem: যাঁর follower অস্বাভাবিক বেশি, তাঁর একটা post fan-out on write এর পুরো ব্যবস্থাকে আটকে দেয়।

সমাধান: hybrid approach

সমাধানটা হলো দুই পথ মিশিয়ে ফেলা। সব account কে একভাবে না দেখে দুই ভাগে ভাগ করা হয়:

আম্মু feed খুললে feed service দুই জায়গা থেকে post আনে, তারপর মিলিয়ে সাজায়:

আম্মু feed খোলেন GET /v1/feed Feed service দুই জায়গা থেকে একসাথে আনে push করা অংশ pull করা অংশ Redis: timeline:আম্মু রুমানা, রাফি... (আগেই বসানো) Celebrity post cache তাওসিফের শেষ কয়েকটা post ~3 ms ~3 ms, সবাই একই key পড়ে Merge + sort post_id বড় মানে নতুন 20 টা post, তাওসিফের ছবি সবার উপরে মোট ~10 ms নিয়ম: follower 10 লাখের বেশি হলে fan-out on write বন্ধ, বাকি সবার জন্য চালু
Hybrid approach এ দুই রকম account দুই রকম পথে চলে। সাধারণ user এর post আগেই follower দের timeline এ বসানো থাকে (push)। তাওসিফের মতো celebrity এর post কারো timeline এ লেখা হয় না; আম্মু feed খুললে তখনই আনা হয় (pull)। দুটো তালিকা মিলিয়ে sort করলেই feed তৈরি।

আম্মু যতজন celebrity কে follow করেন, তাঁদের শেষ কয়েকটা post read এর সময় টেনে আনা হয়। একজন মানুষ সাধারণত হাতে গোনা কয়েকজন celebrity কে follow করেন, তাই এই pull খুব ছোট। আর তাওসিফের post টা কোটি মানুষ পড়লেও সবাই একই cache key পড়ে, যেটা সবসময় memory তে গরম থাকে।

এখানে আরেকটা জিনিস খেয়াল রাখতে হয়। কোটি মানুষ একই key পড়লে সেটা hot key হয়ে যায় (সেশন ০৫)। একটা Redis node এর উপর সব চাপ পড়ে। তাই celebrity post cache এর কয়েকটা কপি কয়েকটা node এ রাখা হয়। কিছু কোম্পানি app server এর নিজের memory তেও কয়েক সেকেন্ডের জন্য রেখে দেয়।

নিষ্ক্রিয় user দের বাদ দেওয়া

একই বুদ্ধি আরেক জায়গায়ও কাজে লাগে। যে user ৩০ দিন app খোলেননি, তাঁর timeline এ লিখে লাভ নেই। Fanout worker তাঁকে বাদ দেয়। তিনি ফিরে এলে প্রথমবার fan-out on read দিয়ে তাঁর timeline নতুন করে বানানো হয়। এতে ৮ কোটি registered user এর মধ্যে শুধু সক্রিয় ৩ কোটির জন্য memory খরচ হয়।

Hybrid এর সারকথা

বেশিরভাগ মানুষের জন্য push, অল্প কয়েকজন বিশাল account এর জন্য pull। Push পড়াকে দ্রুত রাখে। Pull celebrity এর একটা post থেকে কোটি write এর ঢেউ ঠেকায়। কোন সীমায় ভাগ হবে (১০ লাখ, নাকি ৫ লাখ), সেটা system চালাতে চালাতে মেপে ঠিক করতে হয়।

০৬ Timeline Redis এ কীভাবে রাখবো, আর পরের page কীভাবে আনবো?

Sorted set: নিজে থেকেই সাজানো তালিকা

Timeline রাখার জন্য দরকার এমন একটা তালিকা, যেটা সবসময় নতুন থেকে পুরোনো ক্রমে সাজানো থাকে, আর মাঝখান থেকে দ্রুত পড়া যায়। Redis এ ঠিক এমন একটা জিনিস আছে, নাম sorted set। এখানে প্রতিটা item এর সাথে একটা নম্বর (score) থাকে। Redis সবসময় score অনুযায়ী item গুলো সাজিয়ে রাখে।

আমাদের ক্ষেত্রে item হলো post_id, আর score ও post_id। Post_id নিজেই সময়ের সাথে বাড়ে, তাই বড় score মানেই নতুন post।

# Fanout worker: রুমানার নতুন post (8977) আম্মুর timeline এ বসানো
# score = post_id, কারণ নতুন post এর id সবসময় বড়
ZADD timeline:ammu 8977 8977

# সবচেয়ে নতুন 800 টা রেখে বাকিগুলো ফেলে দেওয়া (memory বাঁচাতে)
ZREMRANGEBYRANK timeline:ammu 0 -801

# প্রথম page: সবচেয়ে নতুন 20 টা post_id
ZREVRANGE timeline:ammu 0 19

# পরের page: cursor=8410, মানে 8410 এর চেয়ে ছোট (পুরোনো) 20 টা id
ZREVRANGEBYSCORE timeline:ammu (8410 -inf LIMIT 0 20

# Memory হিসাব
3 কোটি active user x 800 id x 16 byte = 384 GB (raw)
Redis এর নিজের overhead মিলে       ≈ ~800 GB  → 64 GB এর ~13 টা node, replica সহ ~26 টা

চারটা সিদ্ধান্ত এখানে গুরুত্বপূর্ণ:

Hydration: id থেকে পুরো post

Redis থেকে আম্মু পান ২০টা সংখ্যা মাত্র। Screen এ দেখানোর জন্য দরকার caption, ছবি, লেখকের নাম আর like এর সংখ্যা। এই id গুলোকে পূর্ণ post এ রূপ দেওয়ার ধাপকে বলে hydration, শুকনো জিনিসে পানি দিয়ে আবার তাজা করার মতো।

REDIS (শুধু id) HYDRATION timeline:আম্মু sorted set score member 8977 8977 8410 8410 8302 8302 ... সর্বোচ্চ 800 টা id পুরোনোগুলো trim হয় 20 টা id 1. ZREVRANGE 0 19 নতুন থেকে পুরোনো, 20 টা id 2. Post cache: MGET caption, ছবির link, like 3. User cache: MGET লেখকের নাম আর profile ছবি 4. পুরো post card মোট ~10 ms
Timeline এ পুরো post রাখা হয় না, শুধু post_id রাখা হয়। ২০টা id হাতে পাওয়ার পর post cache আর user cache থেকে একবারে (MGET) বাকি তথ্য আনা হয়। এই ধাপের নাম hydration। কেউ post এর caption বদলালে শুধু post cache এ একবার বদলালেই চলে, হাজার timeline ছুঁতে হয় না।

Hydration এর সময় প্রতিটা id এর জন্য আলাদা request পাঠানো হয় না। Redis এর MGET একবারে ২০টা key পড়ে আনে। তাই পুরো ধাপটা কয়েক millisecond এ শেষ হয়।

Cursor pagination: পরের page আনা

আম্মু প্রথম ২০টা post দেখে নিচে scroll করলেন। এখন পরের ২০টা লাগবে। সবচেয়ে চেনা পথ হলো offset: “প্রথম ২০টা বাদ দিয়ে পরের ২০টা।” SQL এর LIMIT 20 OFFSET 20 এভাবেই কাজ করে।

Feed এ এই পথে একটা অদ্ভুত সমস্যা হয়। আম্মু যখন প্রথম page পড়ছিলেন, তখন উপরে নতুন post এসে গেছে। ফলে সবকিছু নিচের দিকে সরে গেছে:

OFFSET CURSOR Page 1: P20 ... P11 ?offset=0&limit=10 এর মধ্যে 3 টা নতুন post এলো (P23, P22, P21) Page 2 চাইলেন ?offset=10&limit=10 পেলেন P13, P12, P11 ... P4 3 টা post দ্বিতীয়বার! Page 1: P20 ... P11 next_cursor = P11 নতুন 3 টা post এলেও কিছু যায় আসে না P11 এর আগের 10 টা ?cursor=P11&limit=10 পেলেন P10, P9 ... P1 কোনো duplicate নেই
Offset বলে "প্রথম ১০টা বাদ দিয়ে পরের ১০টা চাই"। মাঝখানে নতুন post এলে সবকিছু নিচে সরে যায়, তাই আগের page এর শেষ তিনটা post আবার আসে। Cursor বলে "P11 এর চেয়ে পুরোনো ১০টা চাই"। উপরে যত নতুন post ই আসুক, এই প্রশ্নের উত্তর বদলায় না।

এর সমাধান cursor pagination। “কয়টা বাদ দেবো” না বলে app বলে, “এই post এর চেয়ে পুরোনো ২০টা চাই।” শেষ দেখা post এর id টাই হলো cursor। উপরে যত নতুন post আসুক, “৮৪১০ এর চেয়ে পুরোনো” প্রশ্নের উত্তর বদলায় না। তাই কোনো post দুইবার আসে না, কোনোটা বাদও পড়ে না।

আরেকটা সুবিধা আছে। Offset ১০,০০০ হলে database কে আগে ১০,০০০টা row পড়ে ফেলে দিতে হয়, তারপর আসল ২০টা দেয়। Cursor থাকলে index দিয়ে সরাসরি ঐ জায়গায় চলে যাওয়া যায় (সেশন ০৭)। তাই একশো page নিচেও feed সমান দ্রুত থাকে।

০৭ এত ছবি কোথায় রাখবো, আর মানুষের কাছে কীভাবে পৌঁছাবো?

Q2 এর হিসাবে দেখেছি, বছরে ৩ PB ছবি জমে। এগুলো MySQL এর মতো database এ রাখা ভালো পছন্দ নয়। Database বানানো হয় ছোট ছোট row দ্রুত খোঁজার জন্য। কোটি কোটি বড় file এর জন্য দরকার আলাদা ধরনের storage।

Object storage

Object storage আমরা সেশন ২৩ এ Pastebin এর লেখা রাখার জন্য দেখেছি। মনে করিয়ে দিই: এটা বড় file রাখার storage, যেখানে প্রতিটা file একটা key দিয়ে পাওয়া যায় (যেমন photos/9001/medium.jpg)। জায়গা প্রায় অসীম, খরচ কম, আর প্রতিটা file এর কয়েকটা কপি আলাদা জায়গায় থাকে। ছবির জন্যও ঠিক একই জিনিস কাজে লাগে।

ছবি upload: app server কে পাশ কাটিয়ে

তাওসিফের ৪ MB এর ছবিটা app server এর ভেতর দিয়ে গেলে server এর network আর memory দুটোই অকারণে আটকে থাকে। তাই ছবি যায় সরাসরি object storage এ:

1. upload URL চাই presigned URL (15 মিনিট মেয়াদ) তাওসিফের phone ছবি: 4 MB Media service ছবি নিজে ছোঁয় না 2. সরাসরি upload 3. event 3 টা size Object storage S3 এর মতো, 3 PB/বছর Resize worker 150px, 1080px, আসল origin (miss হলে) CDN edge ঢাকা, চট্টগ্রাম 4. ছবি ~20 ms Follower দের phone 2 কোটি জন App server এর ভেতর দিয়ে ছবির একটা byte ও যায় না
তাওসিফের phone প্রথমে media service এর কাছে একটা সাময়িক upload link চায়। তারপর ছবিটা সরাসরি object storage এ পাঠায়। Upload শেষ হলে একটা worker ছবির তিনটা size বানায়। Follower রা ছবি পান কাছের CDN edge থেকে; edge এ না থাকলে CDN একবার object storage (origin) থেকে এনে রাখে।

এখানে মূল কৌশল presigned URL। Media service object storage এর কাছ থেকে একটা বিশেষ link বানিয়ে নেয়। এই link দিয়ে শুধু একটা নির্দিষ্ট file, শুধু ১৫ মিনিটের জন্য upload করা যায়। তাওসিফের phone এই link এ ছবি পাঠায়। App server শুধু link টা দেয়, ছবির একটা byte ও তাকে ছুঁতে হয় না।

Upload শেষ হলে object storage একটা event পাঠায়। একটা worker ছবির তিনটা size বানায়: feed এর ছোট thumbnail, phone এর screen এর জন্য মাঝারি, আর আসল ছবি। কেউ ২ কোটি phone এ ৪ MB এর আসল ছবি পাঠাতে চায় না।

ছবি download: CDN

২ কোটি follower একসাথে একই ছবি চাইলে object storage এর একটা জায়গা থেকে পাঠানো ধীর আর খরচের। তাই মাঝখানে থাকে CDN (সেশন ০৬): মানুষের কাছাকাছি শহরে বসানো server, যেগুলো জনপ্রিয় file এর কপি রাখে। ঢাকার edge এ ছবিটা একবার এলে পরের লাখো মানুষ সেখান থেকেই ২০ ms এ পান। Object storage তখন শুধু origin, মানে CDN এর পেছনের আসল উৎস।

ছবির file নাম কখনো বদলায় না, আর একই নামে নতুন ছবিও বসে না। ছবি বদলালে নতুন key তৈরি হয়। তাই CDN কোনো ছবিকে অনেক দিন cache এ রাখতে পারে, cache invalidation এর ঝামেলা ছাড়াই।

০৮ এক post এ সেকেন্ডে হাজার like কীভাবে গুনবো, আর feed এর ক্রম কে ঠিক করে?

Like counter: একটা row এ লাইন

তাওসিফের ছবিতে প্রথম দশ মিনিটে সেকেন্ডে প্রায় ৫,০০০ like আসছে। সবচেয়ে সরল পথ হলো প্রতিটা like এ database এ লেখা: UPDATE posts SET like_count = like_count + 1 WHERE post_id = 9001।

সমস্যা হলো, ৫,০০০ request একই row বদলাতে চায়। Database একটা row একসাথে একজনকেই বদলাতে দেয়। বাকিরা lock এর জন্য লাইনে দাঁড়ায়, আর লাইনটা লম্বা হতেই থাকে। এটা আবারো hot key সমস্যা (সেশন ০৫), এবার write এর দিক থেকে।

সেশন ২৪ এ rate limiter এর counter আমরা Redis এ রেখেছিলাম। এখানেও Redis, শুধু একটা নতুন কৌশল যোগ হয়। সমাধানটা দুই ধাপে:

REDIS post 9001 এ like 5,000 / সেকেন্ড Like service random একটা ভাগ বাছাই likes table কে like দিলো (একবারই) likes:9001:0 INCR · 1,243 likes:9001:1 INCR · 1,251 likes:9001:2 INCR · 1,238 ... :9 প্রতিটায় ~500/s Flush job প্রতি 5 সেকেন্ডে 10 টা যোগ করে posts.like_count 5 সেকেন্ডে একটা UPDATE Database এ 5,000 write/s এর বদলে 0.2 write/s
একটা row এ সেকেন্ডে ৫,০০০ বার লিখলে সবাই একই lock এর জন্য লাইনে দাঁড়ায়। তাই counter টাকে ১০টা ভাগে ভাগ করা হয়, প্রতিটা like random একটা ভাগে INCR হয়। মোট like জানতে ১০টা ভাগ যোগ করলেই হয়। Database এ লেখা হয় পাঁচ সেকেন্ডে একবার।
  1. Counter ভাগ করা (sharded counter): একটা counter এর বদলে Redis এ ১০টা ছোট counter রাখা হয়: likes:9001:0 থেকে likes:9001:9। প্রতিটা like random একটা counter এ INCR করে (Redis এর command, যেটা একটা সংখ্যা এক বাড়ায়)। এখন প্রতিটা counter এ সেকেন্ডে মাত্র ৫০০ বার লেখা হয়। মোট like জানতে ১০টা যোগ করলেই হয়।
  2. Database এ কয়েক সেকেন্ড পর পর লেখা: একটা flush job প্রতি ৫ সেকেন্ডে ১০টা counter যোগ করে database এ একবার লেখে। Database এর উপর চাপ সেকেন্ডে ৫,০০০ write থেকে নেমে আসে ৫ সেকেন্ডে একটায়।

তবে “কে like দিলেন” সেই তথ্য আলাদা করে likes table এ রাখতে হয়। এর primary key (post_id, user_id), তাই একজন একটা post এ দুইবার like দিতে পারেন না। আম্মু দুর্বল network এ দুইবার চাপলেও like একটাই হয়।

এর একটা দাম আছে: screen এর সংখ্যাটা কয়েক সেকেন্ড পিছিয়ে থাকে। আম্মু হয়তো ১২,৪০৩ দেখছেন, অথচ আসল সংখ্যা ১২,৪৫০। Like এর ক্ষেত্রে কেউ এটা টেরও পান না।

Ranking: শুধু সময়ের ক্রম যথেষ্ট নয়

এতক্ষণ feed সাজিয়েছি শুধু সময় দিয়ে। কিন্তু আম্মু ৩০০ জনকে follow করলে প্রতিদিন হয়তো হাজারখানেক post আসে। সময়ের ক্রমে সাজালে দশ মিনিট আগের এক দূরের আত্মীয়ের সাধারণ post উপরে থাকে, আর চল্লিশ মিনিট আগের তাওসিফের ছবি অনেক নিচে চলে যায়।

তাই বড় app গুলো feed কে ranking করে, মানে প্রতিটা post এর একটা score বের করে সেই অনুযায়ী সাজায়:

Candidate timeline এর 500 টা post Signal কতটা নতুন, কে লিখেছে, কত like পাচ্ছে Score প্রতিটা post এর একটা নম্বর সেরা 20 টা feed এ যায় তাওসিফের ছবি: 40 মিনিট আগের, 12 লাখ like, আম্মু প্রায়ই দেখেন → score 0.94 এক দূরের আত্মীয়ের post: 5 মিনিট আগের, 2 like, কখনো খোলেননি → score 0.21 নতুন হলেও কম score এর post নিচে নেমে যায়
Ranking এ feed আর শুধু সময়ের ক্রমে সাজানো হয় না। Timeline থেকে কয়েকশো post নেওয়া হয়, প্রতিটার জন্য কিছু signal দেখে একটা score বের করা হয়, আর সবচেয়ে বেশি score এর ২০টা post আম্মুর phone এ যায়।

Score বের করতে কিছু signal দেখা হয়: post কতটা নতুন, লেখকের সাথে আম্মুর কতটা যোগাযোগ, post টা কত দ্রুত like পাচ্ছে, ছবি নাকি শুধু লেখা। বড় কোম্পানিগুলো এর জন্য machine learning model ব্যবহার করে। Design এর দিক থেকে শুধু মনে রাখুন: ranking timeline এর পরে বসে। Timeline থেকে কয়েকশো candidate আসে, ranking সেখান থেকে সেরা ২০টা বাছে।

Candidate generation আর sponsored post

Ranking এর আগের ধাপটার একটা নাম আছে: candidate generation। কয়েকটা সস্তা উৎস থেকে (আম্মুর timeline, trending post, কাছের মানুষের নতুন ছবি) কয়েকশো সম্ভাব্য post তোলা হয়। ভারী scoring চলে শুধু এই কয়েকশোর উপর, লাখো post এর উপর নয়। তাই feed দ্রুত আসে।

এরপর আসে বিজ্ঞাপন। কোম্পানি টাকা দিয়ে নিজের post feed এ তুলে আনতে পারে। কিন্তু feed এর অর্ধেক বিজ্ঞাপন হলে মানুষ app বন্ধ করে দেবে। তাই বিজ্ঞাপন বসে নির্দিষ্ট ঘরে, যেমন ৪, ১২ আর ২০ নম্বরে, আর প্রতিটার গায়ে “Sponsored” লেখা থাকে। বিজ্ঞাপন আসে আলাদা একটা ad service থেকে, আর ranking শেষ হওয়ার পর feed এ মেশানো হয়।

০৯ সব মিলিয়ে: পুরো design, bottleneck আর trade-off গুলো কী?

পুরো ছবি এক নজরে

CLIENT EDGE SERVICES STORAGE ASYNC আম্মু তাওসিফ ছবি দেখা Gateway + LB API request CDN ছবি, ~20 ms Post service ~350 post/s Like service sharded counter Feed service ~17,500 load/s origin Post DB author_id দিয়ে shard Graph DB কে কাকে follow করে Redis timeline, post cache, like counter Object storage সব ছবি post_created ZADD Kafka event এর লাইন Fanout workers celebrity হলে skip
পুরো feed system এক নজরে। API request যায় gateway হয়ে service গুলোতে; ছবি আসে CDN থেকে। Post save হলে Kafka তে একটা event যায়। Fanout worker রা graph DB থেকে follower list এনে Redis এর timeline এ post_id বসায়। Feed service শুধু Redis পড়ে, তাই feed দ্রুত খোলে।

Post database এ save হলে একটা post_created event Kafka তে যায়। Kafka হলো একটা event log, যেখানে event গুলো ক্রম অনুযায়ী জমা থাকে আর অনেকে আলাদাভাবে পড়তে পারে (সেশন ১০)। Fanout worker রা সেই event পড়ে। Like counting, notification বা analytics এর মতো অন্য team ও একই event পড়তে পারে, post service কে কিছু না জানিয়েই।

আবার রাত দশটা সাতচল্লিশ, এবার নতুন design এ

পরের সিরিজের ফাইনালে তাওসিফ আবার শেষ বলে জেতালেন, আর আবার ছবি দিলেন। এবার যা হলো:

  1. ১০:৪৭:০৩: ছবি সরাসরি object storage এ গেলো। Post save হলো, post_id ৯৫১২।
  2. ১০:৪৭:০৪: Fanout worker দেখলো, তাওসিফ celebrity। কারো timeline এ কিছু লিখলো না। Post টা শুধু celebrity post cache এ গেলো।
  3. ১০:৪৭:০৫: আম্মু feed খুললেন। Feed service তাঁর timeline আর celebrity cache মিলিয়ে ছবিটা সবার উপরে দিলো। মোট সময় প্রায় ১০ ms।
  4. ১০:৪৭:০৬: ঢাকার CDN edge প্রথমবার ছবিটা origin থেকে আনলো। পরের লাখো মানুষ edge থেকেই পেলেন।
  5. ১০:৫৭: দশ মিনিটে ১২ লাখ like। Redis এর ১০টা counter সেটা সামলালো, database পেলো ৫ সেকেন্ডে একটা করে UPDATE।

তানভীর সেই রাতে dashboard এ শুধু একটা ছোট ঢেউ দেখলো। আর আম্মু ফোন করলেন অন্য কারণে: “ছবিটা কী সুন্দর, দেখেছিস?”

Bottleneck গুলো কোথায় লুকিয়ে আছে

Design টা ভালো, কিন্তু নিখুঁত নয়। Interview তে নিজে থেকে এগুলো বললে ভালো ছাপ পড়ে:

এই design আমাদের কী দিলো

  • দ্রুত feed: feed খুললে একটাই Redis read হয়, তাই ২০টা post আসে ১০ ms এর কাছাকাছি সময়ে।
  • Celebrity তে ভেঙে পড়ে না: বড় account এর post read এর সময় টেনে আনা হয়, তাই ২ কোটি write এর ঢেউ ওঠে না।
  • ছবির ভার app server এ পড়ে না: upload যায় সরাসরি object storage এ, আর download আসে CDN থেকে।
  • Like গোনা চলতে থাকে: counter গুলো ভাগ করে Redis এ রাখা হয়, database এ যায় কয়েক সেকেন্ড পর পর।

Downside গুলো

  • Feed সবসময় একদম fresh নয়: fan-out শেষ হতে কয়েক সেকেন্ড লাগে। এটা eventual consistency (সেশন ০৩)।
  • Memory খরচ বেশি: একই post_id হাজার হাজার timeline এ কপি হয়, Redis এ প্রায় ৮০০ GB লাগে।
  • দুই রকম path চালাতে হয়: সাধারণ user এর জন্য push, celebrity এর জন্য pull। Code আর debugging দুটোই জটিল হয়।
  • Like count কয়েক সেকেন্ড পিছিয়ে থাকে: screen এ ১২,৪০৩ দেখালেও আসল সংখ্যা হয়তো ১২,৪৫০।
সবচেয়ে গুরুত্বপূর্ণ শিক্ষা

Feed design এর মূল কথা একটাই: পড়া সস্তা রাখতে লেখার সময় খাটুনি করা, কিন্তু অন্ধভাবে নয়। সাধারণ মানুষের post push হয়, celebrity এর post pull হয়। Timeline এ থাকে শুধু id, পুরো post আসে hydration এ। ছবি চলে আলাদা পথে, object storage আর CDN দিয়ে। আর প্রতিটা জায়গায় আমরা একটু দেরি মেনে নিয়েছি, কারণ feed এ কয়েক সেকেন্ডের দেরিতে কারো ক্ষতি হয় না।

১০ মগজে প্রেশার দিন

আগে নিজে ভাবুন। তারপর উত্তর খুলে দেখুন: বেশিরভাগ মানুষ কীভাবে ভাবে, সেই চিন্তায় কী বাদ পড়ে, আর আসল কথাটা কোথায়।

প্রশ্ন ১: Feed এ read অনেক বেশি, তাই সবার জন্য fan-out on write ই সেরা। ঠিক?
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“হ্যাঁ। Read, write এর ৫০ গুণ। Post করার সময় সবার timeline এ লিখে দিলে feed খোলা একটা Redis read হয়ে যায়। এর চেয়ে দ্রুত আর কিছু হয় না।”

সাধারণ user এর জন্য কথাটা ঠিক। কিন্তু দুই ধরনের account এ এই নিয়ম ভেঙে পড়ে।

যে জায়গাটা যোগ করা দরকার

প্রথমত, celebrity। ২ কোটি follower এর একটা post থেকে ২ কোটি write হয়। সেটা শেষ হতে কয়েক মিনিট লাগে, আর এর মধ্যে বাকি সবার post আটকে থাকে। দ্বিতীয়ত, নিষ্ক্রিয় user। যিনি মাসে একবারও app খোলেন না, তাঁর timeline এ প্রতিদিন লেখা শুধু memory নষ্ট করে।

তাই উত্তর হলো hybrid। বেশিরভাগ account এর জন্য push, বিশাল account এর জন্য pull, আর নিষ্ক্রিয় user কে fan-out থেকে বাদ।

সঠিক ভাষায়

Fan-out on write এর খরচ follower সংখ্যার সমানুপাতিক। Follower সংখ্যা যেখানে অস্বাভাবিক বড়, সেখানে push থামিয়ে pull করুন।

প্রশ্ন ২: রুমানা post করলেন, কিন্তু নিজের feed এ সাথে সাথে দেখতে পেলেন না। Fanout তো কয়েক সেকেন্ড নেয়। এটা কি মেনে নেবেন?
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“Feed এ eventual consistency চলে। কয়েক সেকেন্ড পরে post টা চলে আসবে। এটাই তো আমাদের requirement।”

অন্যদের জন্য কথাটা ঠিক। কিন্তু যিনি নিজে post করলেন, তাঁর অভিজ্ঞতা আলাদা।

যে জায়গাটা যোগ করা দরকার

রাফি যদি রুমানার post দুই সেকেন্ড পরে দেখেন, তিনি টেরও পান না। কিন্তু রুমানা নিজে post করে feed এ না দেখলে ভাববেন post হয়নি। তিনি আবার post করবেন, আর duplicate তৈরি হবে।

তাই নিজের post এর ক্ষেত্রে আলাদা ব্যবস্থা থাকে। Post service save করার সাথে সাথেই রুমানার নিজের timeline এ id টা বসিয়ে দেয়, fanout এর অপেক্ষা না করে। অনেক app আরো সহজ পথ নেয়: phone নিজেই নতুন post টা feed এর উপরে দেখিয়ে দেয়। এই নিশ্চয়তার নাম read-your-writes, মানে নিজের লেখা নিজে সাথে সাথে পড়তে পারা। সেশন ১৫ এ consistency এর এই স্তরগুলো বিস্তারিত দেখবেন।

মূল pattern

Eventual consistency সবার জন্য সমান নয়। অন্যরা একটু পরে দেখলে চলে। কিন্তু যিনি লিখলেন, তাঁকে নিজের লেখা সাথে সাথে দেখাতে হয়।

প্রশ্ন ৩: একজন user একটা post delete করলেন। লাখো follower এর timeline থেকে post টা কীভাবে সরাবেন?
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“Fan-out এর মতোই উল্টো কাজ করবো। একটা worker সব follower এর timeline থেকে ZREM দিয়ে post_id টা মুছে দেবে।”

এভাবে কাজ হয়, কিন্তু এটা ধীর আর ব্যয়বহুল। আর মোছা শেষ হওয়ার আগ পর্যন্ত post টা অনেকের feed এ দেখা যায়।

যে জায়গাটা যোগ করা দরকার

Timeline এ শুধু id থাকে, পুরো post থাকে না। Post টা দেখাতে হলে hydration এ post cache থেকে পড়তেই হয়। তাই post টাকে post cache আর database এ “deleted” চিহ্নিত করলেই যথেষ্ট। Hydration এর সময় deleted post বাদ পড়ে যায়। মাত্র এক জায়গায় একটা write, আর সাথে সাথে সবার feed থেকে post টা অদৃশ্য।

Timeline এ পড়ে থাকা পুরোনো id গুলো পরে ধীরে ধীরে trim এর সময় এমনিই চলে যায়। আর celebrity এর post তো কারো timeline এ ছিলোই না, তাই সেখানে মোছার কিছুই নেই।

মাথায় গেঁথে নিন

Timeline এ id রাখা আর hydration এ পুরো post আনার একটা বড় লাভ এটাই। Post বদলানো বা মোছার জন্য লাখো timeline ছুঁতে হয় না। এক জায়গায় বদলালেই সবার feed এ বদলে যায়।

১১ যে টার্মগুলো শিখলাম

টার্মমানে
Functional requirementSystem কী কী কাজ করবে, অর্থাৎ user কী কী করতে পারবেন।
Non-functional requirementSystem কতটা ভালোভাবে কাজ করবে: latency, availability, scale।
Home timelineআপনি যাদের follow করেন, তাদের সবার post মিলিয়ে বানানো feed।
User timelineএকজন মানুষের নিজের post এর তালিকা, যা তাঁর profile এ দেখা যায়।
Fan-outএকজনের post তাঁর সব follower এর কাছে ছড়িয়ে দেওয়া।
Fan-out on read (pull)Post করার সময় কিছু হয় না; feed খোলার সময় সবার post খুঁজে এনে সাজানো হয়।
Fan-out on write (push)Post করার সময়েই প্রত্যেক follower এর timeline এ post_id বসিয়ে দেওয়া হয়।
Celebrity problemঅস্বাভাবিক বেশি follower এর একটা post থেকে কোটি write তৈরি হয়ে fan-out আটকে যাওয়া।
Hybrid approachসাধারণ account এর জন্য push, বিশাল account এর জন্য pull, read এর সময় দুটো মিলিয়ে feed বানানো।
Sorted setRedis এর এমন তালিকা, যেখানে প্রতিটা item একটা score অনুযায়ী সবসময় সাজানো থাকে।
Hydrationশুধু id থেকে caption, ছবি, নাম মিলিয়ে পূর্ণ post বানানোর ধাপ।
Cursor pagination“শেষ দেখা post এর চেয়ে পুরোনোগুলো চাই” বলে পরের page আনা, যাতে নতুন post এলেও duplicate না হয়।
Offset pagination“প্রথম N টা বাদ দিয়ে পরেরগুলো চাই” বলে page আনা; তালিকা বদলালে duplicate বা বাদ পড়ে।
Object storageবড় file রাখার storage, যেখানে প্রতিটা file একটা key দিয়ে পাওয়া যায় (যেমন S3)।
Presigned URLঅল্প সময়ের জন্য একটা নির্দিষ্ট file upload বা download করার অনুমতিসহ বিশেষ link।
Sharded counterএকটা counter কে কয়েকটা ছোট counter এ ভাগ করা, যাতে একটার উপর সব write না পড়ে।
Rankingপ্রতিটা post এর score বের করে feed সাজানো, শুধু সময়ের ক্রমে নয়।
Candidate generationভারী ranking এর আগে সস্তা উপায়ে অল্প কিছু সম্ভাব্য post বাছাই করা।
Graph DB“কে কার সাথে যুক্ত” ধরনের data রাখা আর খোঁজার জন্য বানানো database।

১২ শর্ট সামারি

  1. Feed এ read, write এর প্রায় ৫০ গুণ। তাই fan-out on write দিয়ে post করার সময়েই প্রত্যেক follower এর Redis timeline এ post_id বসানো হয়, আর feed খোলা হয়ে যায় একটা read।
  2. Celebrity এর একটা post থেকে কোটি write তৈরি হয়, যা fan-out কে মিনিটের পর মিনিট আটকে রাখে। Hybrid approach এ বিশাল account এর post read এর সময় pull করা হয়, আর সাধারণ account এর post আগের মতোই push হয়।
  3. Timeline এ থাকে শুধু ৮০০টা id; পুরো post আসে hydration এ, আর পরের page আসে cursor pagination এ, যাতে নতুন post এলেও duplicate না হয়।
  4. ছবি সরাসরি object storage এ upload হয় আর CDN থেকে download হয়। Like গোনা হয় Redis এর কয়েকটা ভাগ করা counter এ, আর database এ যায় কয়েক সেকেন্ড পর পর।

১৩ মনে রাখুন