সরাসরি পর্বে যান
পর্ব ৫৯ · ৬৬-এর মধ্যে · মডিউল ৯

Case study: video streaming — upload, transcoding আর adaptive streaming

Case Study: A Video Streaming Platform — Upload, Transcoding and Adaptive Streaming

Taught in Bangla, this lesson designs a video-on-demand platform with separate write and read paths: storage, peak egress and CDN versus origin estimates from a bitrate ladder, resumable multipart uploads via presigned URLs with checksums, transcoding as a parallel DAG of idempotent tasks, HLS and DASH packaging, two adaptive bitrate rules compared in a simulator, and CDN long tail, egress cost and view counting.

এই পর্বে যা শিখবেন

  • একটি video-on-demand প্ল্যাটফর্মের পরিধি — কী বানাবেন, কী বাদ, কোন প্রশ্ন আগে
  • Bitrate ladder ধরে storage, peak egress আর CDN বনাম origin traffic-এর হিসাব, চালিয়ে যাচাইসহ
  • Resumable আর যাচাই-করা আপলোড, আর transcoding-কে idempotent কাজের একটি সমান্তরাল DAG হিসেবে নকশা
  • Adaptive bitrate-এর দুই রকম নিয়মের তুলনা — শুরুর দেরি, থেমে যাওয়া আর মান বদলের trade-off
  • CDN-এ long tail, egress, view count, ব্যর্থতা আর ইন্টারভিউর ৬০ সেকেন্ডের সারাংশ

১ · সমস্যা আর পরিধি — “পর্দা” নামের একটি কাল্পনিক ভিডিও প্ল্যাটফর্ম

এই পর্বে আমরা একটি ভিডিও প্ল্যাটফর্ম নকশা করব; নাম ধরা যাক “পর্দা” — পুরোপুরি কাল্পনিক। নির্মাতারা (creator) ফোন বা কম্পিউটার থেকে ভিডিও আপলোড করেন; দর্শকেরা মোবাইল অ্যাপ আর ব্রাউজারে দেখেন, খোঁজেন, আর দেখেন কতবার দেখা হয়েছে। এ ধরনের সেবা video-on-demand (VOD) — ভিডিও আগে থেকে জমা, দর্শক যখন খুশি চালান। Live শুধু বিস্তার হিসেবে, অংশ ৯-এ।

আমরা পর্ব ৬-এর আট ধাপের কাঠামো ধরে এগোব: পরিধি → হিসাব → API → data model → বড় ছবি → গভীরে নামা → ভাঙন → সারাংশ। Case study-র কাজ নতুন ধারণা শেখানো নয়, আগের পর্বগুলো জোড়া দেওয়া — তাই প্রতিটি পুরোনো ধারণায় এক বাক্যের প্রয়োগ আর একটি link.

একটি বড় রেস্তোরাঁ ভাবুন। সকালে কাঁচা বাজার আসে (নির্মাতার আসল ভিডিও); রান্নাঘরে কেটে-রেঁধে ছোট-মাঝারি-বড় প্লেটে সাজানো হয় (transcoding — কয়েকটি মান); খাবার পৌঁছায় শহরের খুচরা দোকান থেকে (CDN-এর edge), যারা মাল তোলে পাইকারি আড়ত (origin) থেকে। রান্নাঘর একটু ধীর হলে চলে, কিন্তু কাঁচা মাল হারালে ফেরে না; আর দোকানে লাইন লাগলে ক্রেতা চলে যান। পুরো নকশা এই দুই ভিন্ন চাহিদার ওপর দাঁড়িয়ে।

Functional requirement — কী করতে হবে

  • আপলোড: কয়েক MB থেকে কয়েক GB; network কাটলে যেখানে থেমেছিল সেখান থেকে আবার।
  • প্রক্রিয়া আর প্রকাশ: ২৪০p থেকে ১০৮০p — কয়েকটি মান, thumbnail, তারপর “প্রকাশিত”; নির্মাতা অবস্থা দেখবেন।
  • দেখা: মোবাইল আর ওয়েবে দ্রুত শুরু, seek, আর গতি অনুযায়ী নিজে থেকে মান বদল।
  • খোঁজা: শিরোনাম, বিবরণ, tag দিয়ে search; নির্মাতার পাতা।
  • View count: একটু দেরিতে আর আনুমানিক হলেও চলবে।

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

  • দেখার availability সবচেয়ে বেশি: আপলোড থামলে বিরক্তি, দেখা থামলে দর্শক হারানো — তাই পড়ার পথে কম নির্ভরতা (পর্ব ৫)।
  • শুরুর দেরি আর থামা কম: ধরা যাক প্রায় ২ সেকেন্ডে প্রথম ছবি, আর দেখার খুব ছোট অংশে rebuffering — লক্ষ্য, p95 ধরে মাপা (পর্ব ৪)।
  • দুর্বল network সয়ে চলা: বাংলাদেশসহ অনেক দেশে দর্শকের বড় অংশ মোবাইল ডেটায়, বদলাতে থাকা গতিতে দেখেন বলে ধরে নেওয়া যুক্তিসংগত — নিজের মাপে যাচাই করবেন।
  • Durability আর প্রকাশের সময়: আসল ফাইল কখনো হারানো চলবে না (তৈরি করা মান আবার বানানো যায়); ১০ মিনিটের ভিডিও কয়েক মিনিটে দেখার যোগ্য।
  • খরচ: প্রতি ঘণ্টা দেখার খরচ — মূলত বিতরণের byte — নিয়ন্ত্রণে।

পরিধির বাইরে: recommendation, comment-like, বিজ্ঞাপন, পেমেন্ট, কপিরাইট মেলানো আর moderation-এর বিস্তারিত — প্রতিটি আলাদা বড় সিস্টেম; শুধু প্রকাশের আগে একটি “যাচাই” ধাপের জায়গা রাখব।

ইন্টারভিউয়ার যে প্রশ্নগুলো আশা করেন

প্রশ্নকেন জরুরিআমাদের অনুমান
দিনে কতজন দেখেন, কতক্ষণ?peak egress আর CDN-এর আকারধরা যাক ১ কোটি দৈনিক দর্শক, গড়ে ৩০ মিনিট
দিনে কয়টি আপলোড, কত লম্বা?storage আর transcoding-এর ক্ষমতা২০,০০০টি, গড়ে ৬ মিনিট
সর্বোচ্চ মান কত? 4K লাগবে?প্রতিটি ধাপে storage আর encode বাড়েশুরুতে ১০৮০p পর্যন্ত
দর্শক কোথায়, কোন যন্ত্রে?CDN-এর অবস্থান, codecমূলত একটি দেশ ও আশপাশ; বেশিরভাগ মোবাইল
Private বা পেইড ভিডিও আছে?signed URL আর DRM লাগবে কি নাকিছু ভিডিও শুধু সদস্যদের জন্য
View count কতটা নির্ভুল?নিখুঁত মানে লেনদেন, আনুমানিক মানে streamকয়েক মিনিট দেরি আর সামান্য ভুল চলবে

২ · হিসাব — storage, peak egress আর CDN বনাম origin

হিসাবের কৌশল পর্ব ৩-এ; সব ইনপুট অনুমান। মূল সংখ্যা bitrate — প্রতি সেকেন্ড ভিডিওতে কত bit; কয়েকটি মান মিলে bitrate ladder. আমাদের অনুমান: ২৪০p ০.৪, ৩৬০p ০.৮, ৪৮০p ১.৪, ৭২০p ২.৮, ১০৮০p ৫ Mbit/s — আসল মান codec আর বিষয়বস্তুভেদে অনেক আলাদা।

সূত্র তিনটি। (১) জমার আকার = bitrate × সময় ÷ ৮. (২) একসাথে দর্শক = দৈনিক দর্শক × মিনিট ÷ ১,৪৪০, তারপর peak-এর গুণক (peak factor). (৩) Peak egress = একসাথে দর্শক × গড় দেওয়া bitrate; origin পায় শুধু CDN-এর miss অংশ। নিচের calculator প্রতিটি সংখ্যা হিসাব করে।

// Back-of-the-envelope for a fictional video-on-demand platform. ALL inputs are ASSUMED.
const UPLOADS_PER_DAY = 20000;    // new videos per day
const AVG_MIN = 6;                // average video length, minutes
const RAW_MBPS = 8;               // creator's original phone video, Mbit/s
const LADDER = { "240p": 0.4, "360p": 0.8, "480p": 1.4, "720p": 2.8, "1080p": 5.0 }; // Mbit/s
const OVERHEAD = 1.5;             // durability overhead in the object store (e.g. erasure coding)
const DAU = 1e7, WATCH_MIN = 30;  // daily viewers; minutes watched per viewer per day
const PEAK = 3;                   // peak concurrency / average concurrency
const PLAY_MBPS = 1.5;            // average delivered bitrate (mobile-heavy mix)
const HIT = 0.95, SEG_S = 4;      // CDN byte hit ratio; segment length, seconds
const STARTS = 5;                 // video starts (play API calls) per viewer per day
const TB = (mbps, h) => mbps * 1e6 / 8 * 3600 * h / 1e12;   // decimal TB

const hDay = UPLOADS_PER_DAY * AVG_MIN / 60, hYear = hDay * 365;
console.log(`uploads: ${(UPLOADS_PER_DAY / 86400).toFixed(2)}/s avg, ${hDay} h/day, ${hYear} h/year`);
console.log(`upload ingress: ${(hDay * 3600 * RAW_MBPS / 86400 / 1000).toFixed(2)} Gbit/s avg`);
let ladderTB = 0;
for (const [rung, mbps] of Object.entries(LADDER)) {
  ladderTB += TB(mbps, hYear);
  console.log(`  ${rung.padEnd(6)} ${String(mbps).padStart(4)} Mbit/s  ${TB(mbps, hYear).toFixed(0).padStart(5)} TB/year`);
}
const rawTB = TB(RAW_MBPS, hYear), stored = (rawTB + ladderTB) * OVERHEAD;
console.log(`ladder ${ladderTB.toFixed(0)} TB + raw ${rawTB.toFixed(0)} TB, x${OVERHEAD} = ${(stored / 1000).toFixed(2)} PB/year`);

const avgConc = DAU * WATCH_MIN / 1440, peakConc = avgConc * PEAK;
const peakGbps = peakConc * PLAY_MBPS / 1000;
console.log(`viewers: ${avgConc.toFixed(0)} avg, ${peakConc.toFixed(0)} peak concurrent`);
console.log(`peak egress ${peakGbps.toFixed(1)} Gbit/s: CDN ${(peakGbps * HIT).toFixed(1)}, origin ${(peakGbps * (1 - HIT)).toFixed(1)}`);
const startsS = DAU * STARTS / 86400;
console.log(`play API ${startsS.toFixed(0)}/s avg, ${(startsS * PEAK).toFixed(0)}/s peak; segment GETs ${(peakConc / SEG_S).toFixed(0)}/s peak`);
const egressPBday = DAU * WATCH_MIN * 60 * PLAY_MBPS * 1e6 / 8 / 1e15;
console.log(`delivered ${egressPBday.toFixed(3)} PB/day; one year of new storage leaves every ${(stored / 1000 / egressPBday).toFixed(1)} days`);
console.log(`bytes out : bytes in = ${(egressPBday * 1e15 / (hDay * 3600 * RAW_MBPS * 1e6 / 8)).toFixed(0)} : 1`);

আউটপুট:

uploads: 0.23/s avg, 2000 h/day, 730000 h/year
upload ingress: 0.67 Gbit/s avg
  240p    0.4 Mbit/s    131 TB/year
  360p    0.8 Mbit/s    263 TB/year
  480p    1.4 Mbit/s    460 TB/year
  720p    2.8 Mbit/s    920 TB/year
  1080p     5 Mbit/s   1643 TB/year
ladder 3416 TB + raw 2628 TB, x1.5 = 9.07 PB/year
viewers: 208333 avg, 625000 peak concurrent
peak egress 937.5 Gbit/s: CDN 890.6, origin 46.9
play API 579/s avg, 1736/s peak; segment GETs 156250/s peak
delivered 3.375 PB/day; one year of new storage leaves every 2.7 days
bytes out : bytes in = 469 : 1

আপলোড প্রায় কিছুই না: সেকেন্ডে গড়ে ০.২৩টি, গড় ingress ০.৬৭ Gbit/s. Storage: বছরে ৭,৩০,০০০ ঘণ্টা; ladder-এ ৩,৪১৬ TB (১০৮০p একাই ১,৬৪৩), আসল ফাইল আরও ২,৬২৮ — ladder-এর প্রায় তিন-চতুর্থাংশ, তাই আসল ফাইল ঠান্ডা স্তরে (অংশ ৮); ১.৫ গুণ overhead মিলিয়ে প্রায় ৯.০৭ PB. পড়াই আসল চাপ: peak-এ ৬,২৫,০০০ জন একসাথে, ১.৫ Mbit/s-এ ৯৩৭.৫ Gbit/s; ৯৫% byte hit-এ origin পায় ৪৬.৯, বাকি ৮৯০.৬ edge থেকে। Play API peak-এ সেকেন্ডে মাত্র ১,৭৩৬টি, কিন্তু segment-এর GET ১,৫৬,২৫০টি — segment কখনো app server-এ নয়। শেষ দুই লাইন দিক ঠিক করে: দিনে ৩.৩৭৫ PB বেরোয় — এক বছরের নতুন storage প্রতি ২.৭ দিনে — আর বেরোনো byte ঢোকা byte-এর প্রায় ৪৬৯ গুণ। দাম vendor-ভেদে আলাদা, কিন্তু অনুপাতটিই বলে খরচ কমানোর প্রথম জায়গা বিতরণ।

মূল ধারণা

ভিডিও একটি পড়া-প্রধান, byte-প্রধান সিস্টেম। অনুরোধের সংখ্যা (QPS) নয়, byte-এর প্রবাহ (Gbit/s) এখানে সীমা টানে। Metadata-র সার্ভার কয়েকটি হলেই চলে; কিন্তু segment-এর প্রতিটি byte কোথা থেকে আসবে — এই প্রশ্নই নকশার কেন্দ্র।

৩ · API আর data model — কে কী বলে, কোথায় কী থাকে

API — চারটি মূল কাজ

REST-এর নিয়ম, pagination আর idempotency key পর্ব ৮-এ। মূল কথা: ভিডিওর বাইট কখনো API server দিয়ে যায় না।

POST /v1/uploads                      Idempotency-Key: 7c1e-...-a9
{ "title": "নদীর পাড়ে বিকেল", "size": 503316480, "contentType": "video/mp4",
  "sha256": "9f2b...e41" }
→ 201 { "videoId": "v42", "uploadId": "u-88f", "partSize": 16777216, "parts": 30,
        "urls": [ { "part": 1, "url": "https://raw.store.example/...&X-Sig=..." }, ... ],
        "expiresIn": 900 }

PUT  <সই করা URL, part 1>   (সরাসরি object store-এ; উত্তরে ETag)
GET  /v1/uploads/u-88f              → { "done": [1, 2, ..., 17] }   (resume করার আগে)
POST /v1/uploads/u-88f/complete     Idempotency-Key: 7c1e-...-b0
{ "parts": [ { "part": 1, "etag": "e1" }, ... ] }
→ 202 { "videoId": "v42", "status": "processing" }
GET /v1/videos/v42/playback
→ 200 { "status": "ready", "durationS": 480,
        "manifest": "https://cdn.example/v42/r3/master.m3u8",
        "dash": "https://cdn.example/v42/r3/manifest.mpd",
        "token": "exp=1767225600~path=/v42/~sig=..." }

POST /v1/view-events                (batch, প্রতি ~৩০ সেকেন্ডে একবার)
{ "session": "s-5d1", "videoId": "v42",
  "events": [ { "t": 31, "pos": 30, "rung": "720p", "stallMs": 0 } ] }
→ 204

complete-এর উত্তর 202 Accepted — কাজ নেওয়া হয়েছে, শেষ হয়নি (দীর্ঘ কাজের API-র ধাঁচ, পর্ব ৮)। দুটি POST-ই idempotency key বহন করে, তাই দুর্বল network-এ দুবার পাঠালেও দুটি ভিডিও হয় না। view-events একটি batch — প্রতি সেকেন্ডে অনুরোধ পাঠালে ব্যাটারি আর সার্ভার দুটোরই ক্ষতি।

Data model — চার রকম ডেটা, চার রকম store

ডেটাআকার আর ধরনStore আর কারণ
ভিডিওর বাইট — আসল ফাইল, encode করা segment, manifest, thumbnailবছরে PB; একবার লেখা, বহুবার পড়া, কখনো বদলানো নয়Object storage (পর্ব ২৬), সামনে CDN
Video metadata — শিরোনাম, মালিক, অবস্থা, দৈর্ঘ্য, কোন মান তৈরি হয়েছেপ্রতি ভিডিওতে কয়েক KB; অবস্থা বদলায়, শর্তসহ লেখা লাগেRelational database, partition key video_id (পর্ব ২০, পর্ব ২৩)
Search-এর জন্য লেখাশিরোনাম, বিবরণ, tag; একটু দেরিতে হালনাগাদ চলেSearch index, metadata থেকে event দিয়ে ভরা (পর্ব ২৭)
দেখার ঘটনা আর গণনাদিনে শত কোটি ছোট ঘটনা; গণনা আনুমানিক চলেLog-ভিত্তিক stream, তারপর key-value-এ সারাংশ (অংশ ৮)
CREATE TABLE videos (
  video_id     BIGINT PRIMARY KEY,          -- Snowflake-style id, hash-sharded
  owner_id     BIGINT NOT NULL,
  title        TEXT NOT NULL,
  status       TEXT NOT NULL,               -- uploading, processing, ready, failed, removed
  duration_s   INT,
  revision     INT NOT NULL DEFAULT 0,      -- bumped on every re-encode
  visibility   TEXT NOT NULL,               -- public, unlisted, members
  created_at   TIMESTAMP NOT NULL
);
CREATE TABLE renditions (                   -- one row per quality rung
  video_id BIGINT, revision INT, rung TEXT, kbps INT, segments INT, prefix TEXT,
  PRIMARY KEY (video_id, revision, rung)
);
CREATE INDEX videos_by_owner ON videos (owner_id, created_at DESC);  -- creator page

Partition key: প্রায় সব প্রশ্ন একটি ভিডিও ধরে, তাই video_id-এর hash দিয়ে ভাগ (hash partitioning) — জনপ্রিয় নির্মাতার সব ভিডিও এক shard-এ জমে না। দাম: “এই নির্মাতার সব ভিডিও” সব shard-এ ছড়ায়, তাই owner_id-এর একটি global index, একটু দেরিতে হালনাগাদ হলেও চলে (পর্ব ২৩)। নতুন করে encode করলে নতুন revision, নতুন path — চলমান ভিডিও ভাঙে না।

Object key-এর নকশা: raw/v42/source.mp4 (আসল), media/v42/r3/720p/seg_00042.m4s (segment), media/v42/r3/master.m3u8 (manifest) ধাঁচের নাম। Key-এর বিষয়বস্তু কখনো বদলায় না, তাই CDN দীর্ঘদিন রাখে — পর্ব ১৭-এর fingerprinting-এর চিন্তা; index-এর বিস্তার পর্ব ২১-এ।

৪ · বড় ছবি — লেখার পথ আর পড়ার পথ

“পর্দা”-র high-level design — দুটি পথ, একটি object store

Creator → API → object store (raw) → transcoding DAG → object store (segments) → CDN → viewer; metadata DB, search and the view-event stream

ভিডিও প্ল্যাটফর্মের architecture: নির্মাতা, API, raw bucket, transcoding DAG, segment bucket, CDN, দর্শক, metadata DB, search আর view-event stream নির্মাতার অ্যাপ দর্শকের অ্যাপ API service CDN edge Metadata DB Segment bucket Raw bucket View stream Transcoding DAG creator player · ABR upload · play edge → shield + search index manifest · segment আসল ফাইল → counter workflow engine + worker probe → টুকরো → encode × ৫ মান → package → thumbnail → publish ১ · অনুমতি ২ · সরাসরি PUT ৩ · event ৫ · ready miss segment ৪ · segment
ছবি ৫৯.১ — সবুজ তীর বড় বাইটের লেখার পথ, নীল তীর পড়ার পথ, ধূসর তীর ছোট অনুরোধ (অনুমতি, metadata, play, দেখার ঘটনা)। API service কখনো ভিডিওর বাইট বয় না; raw আর segment bucket একই object store-এর দুটি অংশ হতে পারে।

লেখার পথ (সবুজ): API লগইন আর quota দেখে, video_id আর status = uploading লেখে, সই করা URL দেয় (১)। বাইট যায় সরাসরি raw bucket-এ (২)। শেষ হলে একটি event (৩) workflow engine-এর DAG চালু করে; DAG segment লেখে segment bucket-এ (৪), তারপর metadata-তে ready (৫), আর search index হালনাগাদ হয়।

পড়ার পথ (নীল): দর্শকের অ্যাপ API থেকে নেয় শুধু playback তথ্য আর একটি স্বল্পমেয়াদি token; তারপর manifest আর segment আসে CDN edge থেকে; edge-এ না থাকলে shield, তারপর segment bucket. পড়ার পথে নিজের কোড প্রায় নেই — তাই transcoding বা search বন্ধ থাকলেও প্রকাশিত ভিডিও চলে।

এবার একটি ভিডিওর পুরো লেখার পথ ধাপে ধাপে দেখি — ৮ মিনিটের, ৪৮০ MiB-এর একটি ফোন-ভিডিও, part ১৬ MiB, তাই ৩০টি part:

নির্মাতার অ্যাপ
API service
Object store
Workflow + worker
অবস্থার হিসাব
videos.status
পৌঁছেছে
দর্শক চালালে
  1. অনুমতি আর পরিকল্পনা। অ্যাপ জানাল আকার ৪৮০ MiB আর পুরো ফাইলের SHA-256. API v42 বানাল, status = uploading লিখল, আর ৩০টি part-এর (৪৮০ ÷ ১৬) জন্য ১৫ মিনিটের সই করা URL দিল।
  2. মাঝপথে network কাটল। Part ১–১৭ সরাসরি raw bucket-এ পৌঁছাল (১৭ × ১৬ = ২৭২ MiB), তারপর সংকেত হারাল। অ্যাপ ফোনে লিখে রেখেছে কোন part শেষ; API server দিয়ে একটি byte-ও যায়নি।
  3. যেখানে থেমেছিল সেখান থেকে, তারপর Complete. API জানাল ১–১৭ জমা; অ্যাপ বাকি ১৩টি (২০৮ MiB) পাঠাল। প্রতিটি part-এর checksum store জমা নেওয়ার সময়েই মিলিয়েছে; Complete-এর পরে API মোট আকার মেলাল, আর একই transaction-এ status = processing আর outbox-এ “VideoUploaded” (পর্ব ২৫)।
  4. যাচাই, probe আর টুকরো। Event থেকে workflow শুরু। Worker পুরো ফাইলের SHA-256 মেলাল; probe জানাল ৪৮০ সেকেন্ড, ১০৮০p — পাঁচটি মানই চলবে। ভিডিও কাটা হলো ৩২ সেকেন্ডের ১৫টি টুকরোয়, প্রতিটি keyframe থেকে।
  5. সমান্তরাল encode আর package. ১৫ × ৫ = ৭৫টি স্বাধীন কাজ, যতগুলো worker খালি। তারপর প্রতিটি মান ৪ সেকেন্ডের ১২০টি segment (৪৮০ ÷ ৪), HLS আর DASH manifest; পাশের শাখায় thumbnail.
  6. প্রকাশ। renditions-এ পাঁচটি সারি, status = ready, search index-এ নতুন নথি, নির্মাতার কাছে নোটিফিকেশন (পর্ব ৫৭)। এখন manifest আর segment আসে CDN থেকে।

৫ · গভীরে ক — আপলোড: resumable, যাচাই-করা, আর শেষে একটি event

Multipart upload আর presigned URL-এর পুরো কৌশল — part-এর আকার, সমান্তরাল stream, সই করা URL-এর নিরাপত্তা — পর্ব ২৬-এর অংশ ৩-এ। এখানে ভিডিওর তিনটি বিশেষ প্রশ্ন: দুর্বল network-এ শেষ পর্যন্ত পৌঁছানো, ফাইলটি ঠিক কি না, আর “শেষ হয়েছে” খবরটি যেন না হারায়।

দুর্বল network-এ শেষ পর্যন্ত — resumable upload

  • ছোট part: মোবাইলে ৮–১৬ MiB — ব্যর্থ হলে কম আবার পাঠাতে হয়। কিন্তু store-এর part-সংখ্যার সীমা (লেখার সময়ে S3-ধাঁচের store-এ ১০,০০০) মানে বড় ফাইলে part বড় করতেই হয়: part ≥ ফাইল ÷ সীমা (পর্ব ২৬)।
  • অবস্থা দুই জায়গায়: অ্যাপ ফোনে uploadId আর শেষ হওয়া part লিখে রাখে; কিন্তু সত্য বলে server-এর GET /v1/uploads/{id}, store-এর part-তালিকা থেকে। অ্যাপ বন্ধ হয়ে খুললেও resumable upload চলে, যতক্ষণ session-এর মেয়াদ আছে।
  • URL-এর মেয়াদ ছোট, আপলোড লম্বা: দুর্বল সংযোগে ১ GB-তে এক ঘণ্টা লাগতে পারে; তাই অ্যাপ কয়েকটি করে URL চায়, ফুরোলে নতুন — ফাঁস হলেও ক্ষতি সীমিত।

খোলা মান-রূপ tus protocol-এও একই কথা: অগ্রগতির সত্য server-পাশে, প্রতিটি টুকরো আলাদাভাবে আবার পাঠানো যায়।

ফাইলটি কি ঠিক? — দুই স্তরে checksum

  1. প্রতিটি part: অ্যাপ checksum header-এ পাঠায়, store নিজে হিসাব করে না মিললে part নেয় না। কোন algorithm (MD5, CRC32C, SHA-256) চলে তা store-ভেদে আলাদা। Multipart-এর ETag প্রায়ই পুরো ফাইলের MD5 নয় — তাই ETag দিয়ে পুরো ফাইল যাচাই ভুল।
  2. পুরো ফাইল: অ্যাপ শুরুতেই SHA-256 জানায়; worker পড়ার সময় একই hash হিসাব করে মেলায়, না মিললে failed. একই ফাইল দুবার এলে এই hash-এই ধরা যায় — তবে বহুজনের মধ্যে মেলালে “ফাইলটি আগেই কারও আছে” তথ্য ফাঁস হতে পারে।

তারপর বিষয়বস্তুর যাচাই: প্রথম কয়েক byte দেখে আসল ধরন, আকার আর দৈর্ঘ্যের সীমা, probe, malware scan — client-এর দেওয়া নাম বা contentType বিশ্বাস নয়।

“শেষ হয়েছে” খবরটি যেন না হারায়

সত্যের উৎস একটি: API-র Complete একই transaction-এ status = processing আর outbox-এ event লেখে, relay সেটি queue-তে দেয় (পর্ব ২৫) — store-এর নিজের event notification-এর ওপর আলাদাভাবে নির্ভর করলে “database বলছে processing, কেউ কাজ শুরু করেনি” অবস্থা হতে পারে। Event আসে অন্তত একবার (পর্ব ৩৪), তাই workflow-এর পরিচয় (video_id, revision): একই জোড়া আগেই থাকলে নতুনটি শুরু হয় না — idempotent consumer. আর একটি মিলিয়ে-দেখার কাজ ৩০ মিনিটের বেশি আটকে থাকা session খোঁজে; কয়েক দিন পরে অসমাপ্ত upload মুছে দেয় lifecycle নিয়ম (পর্ব ২৬)।

কী ঘটলফলপ্রতিরক্ষা
মাঝপথে network কাটলকিছু part জমা, বাকি নেইpart-তালিকা দেখে resume; ছোট part
ফাইল বিকৃত বা অন্য ধরনেরencode ব্যর্থ, বা ক্ষতিকর ফাইলchecksum, প্রথম byte দেখে ধরন, probe, malware scan
Event হারাল বা দেরিতে এলভিডিও চিরকাল processingoutbox; মিলিয়ে-দেখার কাজ; “পুরোনো processing” নিয়ে alert

৬ · গভীরে খ — Transcoding pipeline: কাজের একটি DAG, সমান্তরালে

ফোনের আসল ফাইল সরাসরি দর্শককে দেওয়া যায় না — bitrate বেশি, ফরম্যাট যেকোনো। Transcoding মানে ফাইলটি খুলে (decode), মাপে ছোট করে (scale), একটি codec-এ নির্দিষ্ট bitrate-এ আবার সংকুচিত করা (encode) — ladder-এর প্রতিটি ধাপে একবার। তারপর packaging: ছোট segment আর manifest — HLS-এর .m3u8, DASH-এর MPD, আর দুইয়ের জন্য একই fMP4 segment রাখতে CMAF (বিস্তার CN কোর্সের অংশ ৪-এ)। কাজগুলোর মধ্যে নির্ভরতা আছে, তাই এটি একটি DAG — পর্ব ৩৭-এর অংশ ৮-এর data pipeline-এর মতো, চালানোর ক্রম topological.

Transcoding DAG — কোন কাজ কোনটির পরে

probe → split → (encode × pieces × rungs, in parallel) → package → publish; thumbnails on a side branch

  1. Probe৪৮০ s · 1080pযাচাই, ladder ঠিক
  2. Split১৫ × ৩২ skeyframe-এ কাটা
  3. Encode১৫ × ৫ = ৭৫সমান্তরালে
  4. Packagem3u8 · mpd৪ s segment
  5. Publishstatus = readythumbnail-এর অপেক্ষাসহ
ছবি ৫৯.২ — Probe-এর পাশে আলাদা শাখায় audio encode আর thumbnail চলে; publish অপেক্ষা করে সব শাখা শেষ হওয়ার জন্য।

সমান্তরালতার অঙ্ক

এক worker-এ পুরো ভিডিও encode করলে সময় দৈর্ঘ্যের সমানুপাতিক — এক ঘণ্টার ভিডিওতে কয়েক ঘণ্টা। কৌশল: N টি টুকরো, প্রতিটি টুকরো প্রতিটি মানে একটি স্বাধীন কাজ। মোটামুটি:

wall-clock ≈ split + max( মোট কাজ ÷ W , সবচেয়ে লম্বা একটি কাজ ) + package
মোট কাজ = Σ (টুকরোর দৈর্ঘ্য × মানের encode-খরচ) + N × মান × প্রতি কাজের বাড়তি খরচ

তিনটি সত্য। (১) W বাড়ালে প্রথম পদ কমে, কিন্তু সবচেয়ে লম্বা কাজের নিচে নামা যায় না — ছোট টুকরো লাগে। (২) টুকরো যত ছোট, প্রতি কাজের বাড়তি খরচ (input আনা, encoder চালু, output লেখা) তত বেশিবার — বিল বাড়ে। (৩) Split আর package ভাগ হয় না — Amdahl-এর সূত্র (পর্ব ১৩)। টুকরোর সীমা বসে keyframe-এ (I-frame), নইলে টুকরোটি একা decode হয় না। নিচে ৬০ মিনিটের ভিডিও, পাঁচ মান, কাজের সময় এলোমেলো (seeded), প্রতি চেষ্টায় ৩% ব্যর্থতা — ব্যর্থ হলে কিছু কাজ নষ্ট, তারপর পরের খালি worker-এ আবার:

// Transcode a 60-min video: split into N pieces, encode every piece at 5 rungs on W workers.
// Seeded PRNG, so every run prints the same table. All costs are ASSUMED.
function mulberry32(a) { return () => { a = (a + 0x6D2B79F5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
const VIDEO_S = 3600, COST = [0.15, 0.25, 0.4, 0.8, 1.4]; // encode seconds per video second, 240p..1080p
const TASK_OVERHEAD = 3, FAIL_P = 0.03, SPLIT_S = 30, PACKAGE_S = 40;  // seconds; failure per attempt

function run(N, W) {
  const rnd = mulberry32(59), free = new Array(W).fill(SPLIT_S);   // workers start after the split
  let work = 0, fails = 0, end = 0;
  for (let p = 0; p < N; p++) for (let r = 0; r < COST.length; r++) {
    const need = TASK_OVERHEAD + (VIDEO_S / N) * COST[r] * (0.7 + 0.9 * rnd()); // noisy encode time
    let ready = SPLIT_S;
    for (;;) {                                   // attempt until success; retry on the next free worker
      let w = 0; for (let i = 1; i < W; i++) if (free[i] < free[w]) w = i;
      const start = Math.max(free[w], ready);
      if (rnd() < FAIL_P) {                      // crash part-way: time wasted, task goes back
        const lost = need * rnd(); fails++; work += lost; free[w] = start + lost; ready = free[w]; continue;
      }
      free[w] = start + need; work += need; end = Math.max(end, free[w]); break;
    }
  }
  const wall = end + PACKAGE_S, serial = SPLIT_S + work + PACKAGE_S;
  console.log(String(N).padStart(5) + String(VIDEO_S / N).padStart(7) + String(N * COST.length).padStart(7) +
    String(W).padStart(5) + String(fails).padStart(7) + (serial / 60).toFixed(1).padStart(10) +
    (wall / 60).toFixed(1).padStart(9) + (serial / wall).toFixed(1).padStart(8) + "x");
}
console.log("    N  seg_s  tasks    W  fails  serial_min  wall_min  speedup");
for (const [N, W] of [[1, 5], [12, 20], [60, 20], [60, 100], [120, 100], [120, 300], [720, 300]]) run(N, W);

আউটপুট:

    N  seg_s  tasks    W  fails  serial_min  wall_min  speedup
    1   3600      5    5      1     247.2    158.4     1.6x
   12    300     60   20      1     211.4     17.2    12.3x
   60     60    300   20     12     228.4     13.6    16.8x
   60     60    300  100     12     228.4      5.0    46.1x
  120     30    600  100     20     245.4      5.3    46.5x
  120     30    600  300     20     245.4      3.7    66.0x
  720      5   3600  300    125     395.4      2.7   144.4x

টুকরো না করলে (N = ১): মাত্র ৫টি কাজ, ১০৮০p-এর একটিই সময় ঠিক করে — wall ১৫৮.৪ মিনিট, গতি ১.৬ গুণ; একমাত্র ব্যর্থতাটি সেই দীর্ঘ কাজে পড়ায় অনেক মিনিট নষ্ট, তাই serial (২৪৭.২) N = ১২-এর (২১১.৪) চেয়েও বেশি। টুকরো আর worker দুটোই লাগে: N = ৬০, W = ২০-এ ১৩.৬ মিনিট — worker-ই সীমা (২২৮.৪ ÷ ২০ ≈ ১১.৪, সাথে split আর package); W = ১০০-এ ৫.০. শুধু N বাড়ালে লাভ নেই: N = ১২০, W = ১০০-এ ৫.৩ — কাজ দ্বিগুণ, বাড়তি খরচ আর ব্যর্থতাও (২০টি)। Latency কেনা যায়, দামে: N = ৭২০, W = ৩০০-এ ২.৭ মিনিট, ১৪৪.৪ গুণ — কিন্তু serial, মানে worker-মিনিটের বিল, ২২৮.৪ থেকে ৩৯৫.৪: প্রায় ৭৩% বেশি (৩,৬০০ কাজের প্রতিটিতে ৩ সেকেন্ড বাড়তি, ১২৫টি ব্যর্থতা)। “কয়েক মিনিটে প্রকাশ” কতটা মূল্যবান — সেটি ব্যবসার প্রশ্ন।

ব্যর্থতা, retry আর idempotent কাজ

হাজারো worker-এ রোজ কিছু ভাঙবে; নিয়মগুলো পর্ব ৩৭-এর:

  • Output-এর নাম নির্ধারিত: “v42, revision 1, ৭২০p, টুকরো ৭”-এর output সবসময় enc/v42/r1/720p/p07.mp4 — দুবার চললেও একই key-তে একই জিনিস, idempotent; PUT একবারে দৃশ্যমান, আধা-লেখা ফাইল কেউ দেখে না।
  • Lease আর heartbeat: worker কাজ নেয় একটি lease-সহ, চলার সময় নিয়মিত heartbeat দেয়; থেমে গেলে lease ফুরোয়, কাজ আবার queue-তে।
  • সীমিত চেষ্টা: backoff দিয়ে আবার; max attempts পেরোলে DLQ আর failed; বিকৃত ফাইল probe-এই ধরা ভালো (পর্ব ৩১)।
  • ধীর worker: একটি straggler পুরো ভিডিও আটকায়; তার দ্বিতীয় কপি আরেক worker-এ — speculative execution (পর্ব ৩৬), idempotent বলেই নিরাপদ।
Workflow engine
Worker pool (২০টি)
কাজ p07-720p
Object store
৭৫টি encode কাজের হিসাব
শেষ
চলছে
অপেক্ষায়
  1. Probe. Workflow (v42, revision 1) শুরু; w1 জানাল ৪৮০ সেকেন্ড, ১০৮০p, ৩০ fps, H.264 আর AAC. উৎস ১০৮০p বলে পাঁচটি মানই; উৎস ৪৮০p হলে ওপরের দুটি বাদ।
  2. Split — ৭৫টি স্বাধীন কাজ। ৩২ সেকেন্ডের ১৫টি টুকরো, প্রতিটি keyframe থেকে; ১৫ × ৫ = ৭৫টি encode কাজ queue-তে, পাশে audio আর thumbnail-এর শাখা।
  3. প্রথম ঢেউ। ২০টি worker একটি করে কাজ নিল, প্রতিটি lease-সহ; w7 নিল p07-720p. শেষ ০, চলছে ২০, অপেক্ষায় ৫৫.
  4. w7 থেমে গেল। ধরা যাক cloud যন্ত্রটি ফেরত নিল। Heartbeat থামল, lease ফুরাল, কাজটি আবার queue-তে। তখন শেষ ৪৩, চলছে ২০, অপেক্ষায় ১২ (মোট ৭৫)। Store-এ p07-720p-এর কিছুই নেই।
  5. চেষ্টা ২, তারপর join আর package. w12 একই key-তে output লিখল। ৭৫টিই শেষ, তাই package: প্রতিটি মান ১২০টি segment আর manifest. বাকি ৭৪টি কাজ আবার চালাতে হয়নি।
  6. Publish. Thumbnail-ও শেষ — renditions-এ পাঁচটি সারি, status = ready. চেষ্টা ৭৬, কাজ ৭৫: ব্যর্থতার দাম একটি টুকরোর একটি মান।

খরচ কমানোর কৌশল

কাজ ছোট আর idempotent বলে সস্তা spot বা preemptible instance চলে — ওপরের w7 ঠিক এটিই। কম দেখা ভিডিওতে শুরুতে কম মান, জনপ্রিয় হলে বাকিগুলো। আর ভিডিও-ভেদে ladder — per-title encoding: কার্টুন কম bit-এ ভালো দেখায়, খেলার দৃশ্যে বেশি লাগে; encode একটু দামি, কিন্তু প্রতিটি দেখায় byte কমে। প্রকাশিত গবেষণাতেও এই ধাঁচ: Facebook-এর প্রকৌশলীদের SOSP ২০১৭-এর “SVE” গবেষণাপত্রে আপলোড চলতে চলতেই টুকরো করে সমান্তরালে প্রক্রিয়ার বর্ণনা আছে।

৭ · গভীরে গ — Adaptive bitrate: manifest, segment আর player-এর সিদ্ধান্ত

Adaptive bitrate (ABR)-এর মূল চিন্তা, HLS-DASH-এর তুলনা আর buffer-এর গাণিতিক মডেল CN কোর্সের অংশ ৩–৪-এ। এখানে system design-এর প্রশ্ন: segment কত লম্বা, manifest কীভাবে সাজানো, আর player-এর নিয়ম সার্ভারের ভার আর দর্শকের অভিজ্ঞতা কীভাবে বদলায়।

Manifest আর segment-এর নকশা

  • দুই স্তরের manifest: master (master.m3u8) — কোন মান, bitrate, resolution, codec; আর প্রতিটি মানের নিজের segment-তালিকা। VOD-এ প্রকাশের পরে এগুলো বদলায় না, তাই path-এ revision রেখে দীর্ঘ TTL (পর্ব ১৭)।
  • সব মানে একই সীমানা: প্রতিটি মানের segment একই মুহূর্তে শুরু, শুরুতে keyframe — তাই segment ৪১ ৭২০p-তে আর ৪২ ৩৬০p-তে এলেও ছবি নিরবচ্ছিন্ন।
  • দৈর্ঘ্য — ধরা যাক ২ থেকে ৬ সেকেন্ড: ছোট segment-এ দ্রুত শুরু আর দ্রুত মান বদল, কিন্তু বেশি অনুরোধ (৪ সেকেন্ডে peak-এ সেকেন্ডে ১,৫৬,২৫০টি GET, ২ সেকেন্ডে দ্বিগুণ) আর ঘন keyframe-এ সংকোচন কিছুটা খারাপ; লম্বায় উল্টো। আমরা ৪ সেকেন্ড ধরেছি।

দুই রকম নিয়ম, আর শুরু বনাম থামার trade-off

Throughput-ভিত্তিক: সাম্প্রতিক মাপা গতির একটি নিরাপদ অংশের মধ্যে সর্বোচ্চ মান — সরল, কিন্তু গতি হঠাৎ নামলে মাপ পিছিয়ে থাকে। Buffer-ভিত্তিক: buffer-এ কত সেকেন্ড জমা, সেটিই সংকেত। বাস্তবের player সাধারণত দুটি মিলিয়ে চলে; নিচের নিয়ম দুটি ইচ্ছা করে সরল। আর একটি টানাপোড়েন সবসময়: উঁচু মানে শুরু করলে ছবি সুন্দর, কিন্তু শুরুর দেরি আর প্রথমেই থামার ঝুঁকি বাড়ে।

নিচে একটি seeded মোবাইল-ধাঁচের গতিপথ — প্রায় ২.৫ Mbit/s-এর আশপাশে ঘোরে, মাঝে মাঝে তিন segment ধরে ৮০% পতন (লিফট, ভিড়, দুর্বল টাওয়ার)। ১৫০টি ৪ সেকেন্ডের segment (১০ মিনিট); সব নিয়মে প্রথম দুটি segment সবচেয়ে নিচু মানে, ৮ সেকেন্ড জমলে চালানো শুরু:

// ABR on a seeded, fluctuating mobile bandwidth trace: throughput rule vs buffer rule.
function mulberry32(a) { return () => { a = (a + 0x6D2B79F5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
const LADDER = [400, 800, 1400, 2800, 5000], SEG = 4, SEGS = 150, START = 8, MAXBUF = 30; // kbps, s
const rnd = mulberry32(7), MU = Math.log(2500), trace = []; let lb = MU, drop = 0;
for (let i = 0; i < SEGS; i++) {                               // kbps during each segment fetch
  lb += 0.25 * (MU - lb) + 0.9 * (rnd() - 0.5);                // wanders around ~2.5 Mbit/s
  if (!drop && rnd() < 0.04) drop = 3;                         // lift, crowd, weak cell: 3 segments
  trace.push(Math.round(Math.exp(lb) * (drop ? (drop--, 0.2) : 1)));
}
const throughputRule = (hist) => {                             // 0.8 x harmonic mean of last 3
  if (!hist.length) return 0; const h = hist.slice(-3);
  const est = h.length / h.reduce((s, x) => s + 1 / x, 0);
  let q = 0; LADDER.forEach((b, i) => { if (b <= 0.8 * est) q = i; }); return q;
};
const bufferRule = (hist, buf) =>                              // 8 s reservoir, 8..24 s cushion
  Math.max(0, Math.min(4, Math.floor((buf - 8) / 16 * 5)));
function play(name, rule) {
  let t = 0, buf = 0, started = false, startup = 0, stalls = 0, stallT = 0, sw = 0, prev = -1, kb = 0;
  const hist = [];
  for (let i = 0; i < SEGS; i++) {
    const q = started ? rule(hist, buf) : 0, dl = LADDER[q] * SEG / trace[i];
    if (started) { if (dl > buf) { stalls++; stallT += dl - buf; buf = 0; } else buf -= dl; }
    t += dl; buf += SEG; hist.push(trace[i]); kb += LADDER[q] * SEG;
    if (prev >= 0 && q !== prev) sw++; prev = q;
    if (!started && buf >= START) { started = true; startup = t; }
    if (buf > MAXBUF) { t += buf - MAXBUF; buf = MAXBUF; }   // buffer full: wait, keep playing
  }
  console.log(name.padEnd(11) + (kb / (SEGS * SEG)).toFixed(0).padStart(8) + startup.toFixed(2).padStart(9) +
    String(stalls).padStart(7) + stallT.toFixed(1).padStart(9) + String(sw).padStart(9));
}
console.log(`trace kbps: min ${Math.min(...trace)}, max ${Math.max(...trace)}, mean ${(trace.reduce((a, b) => a + b) / SEGS).toFixed(0)}`);
console.log("rule       avg_kbps  start_s  stalls  stall_s  switches");
play("throughput", throughputRule); play("buffer", bufferRule);
play("fixed 720p", () => 3); play("fixed 240p", () => 0);

আউটপুট:

trace kbps: min 197, max 5545, mean 2358
rule       avg_kbps  start_s  stalls  stall_s  switches
throughput     1255     1.57      0      0.0       35
buffer         2028     1.57      5     20.5       67
fixed 720p     2768     1.57     70    559.0        1
fixed 240p      400     1.57      0      0.0        0

গতিপথের গড় ২,৩৫৮ kbps, কিন্তু নেমেছে ১৯৭ পর্যন্ত। Throughput নিয়ম একবারও থামেনি, কিন্তু গড় মান মাত্র ১,২৫৫ kbps — সাবধানী, কারণ শেষ তিনটি মাপের harmonic mean নিচু মাপকে বেশি গুরুত্ব দেয়, তার ওপর ৮০%. Buffer নিয়ম গড়ে ২,০২৮ kbps — অনেক ভালো ছবি — কিন্তু ৫ বার থেমেছে (মোট ২০.৫ সেকেন্ড) আর মান বদলেছে ৬৭ বার: buffer ভরা থাকলে সে গতি না দেখেই উঁচু মান চায়, আর ঠিক তখন গতি কয়েকশো kbps-এ নামলে সেই segment আনতে buffer-এর চেয়ে বেশি সময় লাগে। বাস্তবের player তাই ধীর segment মাঝপথে ছেড়ে নিচু মানে যায়, আর মান বদলে একটু দ্বিধা (hysteresis) রাখে। স্থির ৭২০p ৭০ বার থেমেছে, মোট ৫৫৯ সেকেন্ড — ১০ মিনিটের ভিডিওতে ৯ মিনিটের বেশি অপেক্ষা; স্থির ২৪০p কখনো থামে না, কিন্তু সারাক্ষণ ঝাপসা। শুরুর দেরি সবার ১.৫৭ সেকেন্ড, কারণ শুরুর নিয়ম একই। কোনো নিয়ম সব মাপে জেতে না — কোন মাপ বেশি জরুরি, সেটিই নকশার প্রশ্ন।

এবার সিস্টেমের দিক থেকে — player কোন সার্ভারে কী চায়, মান কখন বদলায়। নিয়ম আরও সরল: শেষ segment-এর মাপা গতি × ০.৮-এর মধ্যে সর্বোচ্চ মান; ladder ৪০০, ৮০০, ১,৪০০, ২,৮০০, ৫,০০০ kbps.

দর্শকের player
API service
CDN edge
ABR-এর হিসাব
Buffer আর সময়
এখনকার গতি
নামাতে সময়
Buffer (পরে)
সময় t
  1. Playback API. API অনুমতি আর অঞ্চল দেখে manifest-এর URL আর একটি সই করা token দিল — শুধু /v42/ path, ৬ ঘণ্টা মেয়াদ। আমাদের সার্ভারে আসে শুধু এই ছোট অনুরোধটি।
  2. Manifest — CDN থেকে। Master manifest edge-এ ছিল না, shield-এ ছিল — origin পর্যন্ত যেতে হয়নি। পাঁচটি মান; শুরুর নিয়ম সাবধানী — 360p, তার তালিকা edge-এ hit.
  3. শুরু। Segment ১, ২ — 360p (৮০০ kbps × ৪ s = ৩,২০০ kb), গতি ৪,০০০ kbps, প্রতিটি ০.৮ সেকেন্ড। t = ১.৬-এ buffer ৮ সেকেন্ড, চালানো শুরু। ১০৮০p দিয়ে শুরু করলে প্রতিটি ৫ সেকেন্ড, শুরু হতো t = ১০-এ।
  4. মান ওঠা। বাজেট ৩,২০০ — 720p (২,৮০০)। আনতে ২.৮ সেকেন্ড; buffer ৮ − ২.৮ + ৪ = ৯.২, t = ৪.৪.
  5. গতি পড়ল, মাপ পিছিয়ে। বাস ভিড়ের এলাকায়, গতি ১,০০০ kbps; player পুরোনো মাপ (৪,০০০) দেখে আবার 720p চাইল — আনতে ১১.২ সেকেন্ড, buffer ৯.২, তাই ২.০ সেকেন্ড থামা; তারপর buffer ৪.০, t = ১৫.৬. View-event-এ গেল stallMs: 2000. দোষ CDN-এর নয়, edge-এ hit ছিল।
  6. মান নামা। মাপ ১,০০০, বাজেট ৮০০ — 360p; ৩.২ সেকেন্ডে এল, buffer ৪ − ৩.২ + ৪ = ৪.৮, t = ১৮.৮. এই segment edge-এ ছিল না (কম জনপ্রিয় মান), shield থেকে এল।

দেখার অভিজ্ঞতা — QoE — মাপা হয় শুরুর দেরি (p50, p95), rebuffer ratio, গড় bitrate, মান বদল আর শুরুই না হওয়ার হার দিয়ে; আসে player-এর ঘটনা থেকে, দেশ, ISP, যন্ত্র আর CDN ধরে ভাগ করে। সার্ভারের CPU দেখে দর্শকের কষ্ট বোঝা যায় না (পর্ব ৫০)।

আরও গভীরে: Buffer-ভিত্তিক নিয়মের পরিচিত গবেষণা Huang ও সহকর্মীদের “A Buffer-Based Approach to Rate Adaptation” (SIGCOMM ২০১৪), যেখানে Netflix-এর বাস্তব ট্রাফিকে পরীক্ষা হয়েছিল। Control-theory-র MPC পদ্ধতি Yin ও সহকর্মীদের SIGCOMM ২০১৫-এর গবেষণাপত্রে; buffer-ভিত্তিক BOLA (Spiteri, Urgaonkar ও Sitaraman, INFOCOM ২০১৬) পরে dash.js-এর মতো open-source player-এ যুক্ত হয়; আর Pensieve (Mao ও সহকর্মী, SIGCOMM ২০১৭) reinforcement learning দিয়ে নিয়ম শেখায়। Per-title encoding জনপ্রিয় করে Netflix-এর ২০১৫ সালের ডিসেম্বরের একটি technology blog.

৮ · গভীরে ঘ — বিতরণ: CDN, long tail, egress, সুরক্ষা আর view count

জনপ্রিয় বনাম long tail

CDN-এর মূল অঙ্ক পর্ব ১৭-এর অংশ ৫–৬-এ: origin-এর ভার = মোট × (১ − hit ratio), origin shield বহু edge-miss এক করে, আর hit ratio আসে Zipf-ধাঁচের জনপ্রিয়তা থেকে। ভিডিওতে বিশেষ দুটি কথা: এখানে byte hit ratio-ই আসল (অংশ ২-এর ৪৬.৯ Gbit/s সেখান থেকে); আর catalogue বিশাল — ভাইরাল নাটক সব edge-এ গরম, তিন বছর আগের রান্নার ভিডিও দিনে হয়তো দুবার, প্রতিবার miss; যেন চালু জিনিস প্রতিটি খুচরা দোকানে, কম চলা জিনিস শুধু পাইকারি আড়তে।

নিচে একটি edge-এর byte-ভিত্তিক LRU cache; ১,০০,০০০টি ভিডিও, Zipf α = ০.৯ (ধরা যাক), প্রতিটির জনপ্রিয় অংশ রাখতে ৫০–৫০০ MB. শেষ দুই সারিতে pre-positioning: সবচেয়ে জনপ্রিয়গুলো আগেই, রাতে, edge-এ বসিয়ে স্থির রাখা:

// One CDN edge, byte-capacity LRU, Zipf-like video popularity. Seeded: same output every run.
function mulberry32(a) { return () => { a = (a + 0x6D2B79F5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a);
  t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
const TITLES = 100000, ALPHA = 0.9, WARM = 100000, REQS = 300000, EDGE_GBPS = 100;  // ASSUMED
const rnd = mulberry32(59), size = new Float64Array(TITLES + 1), cdf = new Float64Array(TITLES);
let sum = 0;                                     // GB an edge must hold to serve a title's views
for (let k = 1; k <= TITLES; k++) { size[k] = 0.05 + 0.45 * rnd(); sum += 1 / Math.pow(k, ALPHA); cdf[k - 1] = sum; }
const reqs = Array.from({ length: WARM + REQS }, () => {            // same request stream for all
  let lo = 0, hi = TITLES - 1; const x = rnd() * sum;
  while (lo < hi) { const m = (lo + hi) >> 1; if (cdf[m] < x) lo = m + 1; else hi = m; } return lo + 1; });
function edge(capGB, pinned) {                   // pinned: top titles pre-positioned, never evicted
  const lru = new Map(), pin = new Set(); let used = 0, hit = 0, n = 0, hitB = 0, allB = 0;
  for (let k = 1; k <= TITLES && pinned; k++) { if (used + size[k] > capGB) break; pin.add(k); used += size[k]; }
  reqs.forEach((k, i) => {
    const counted = i >= WARM, isHit = pin.has(k) || lru.has(k);
    if (counted) { n++; allB += size[k]; if (isHit) { hit++; hitB += size[k]; } }
    if (pin.has(k)) return;
    if (lru.has(k)) { lru.delete(k); lru.set(k, 1); return; }
    lru.set(k, 1); used += size[k];
    for (const [old] of lru) { if (used <= capGB) break; lru.delete(old); used -= size[old]; }
  });
  const bhr = hitB / allB;
  console.log(String(capGB / 1000).padStart(6) + (pinned ? "  pinned " : "  LRU    ") + (100 * hit / n).toFixed(1).padStart(8) +
    (100 * bhr).toFixed(1).padStart(10) + (EDGE_GBPS * (1 - bhr)).toFixed(1).padStart(12));
}
console.log(`catalogue ${TITLES} titles, ${(size.reduce((a, b) => a + b) / 1000).toFixed(1)} TB, Zipf alpha ${ALPHA}`);
console.log("cap_TB  policy   req_hit%  byte_hit%  origin_Gbps");
for (const cap of [500, 2000, 8000, 20000]) edge(cap, false);
edge(2000, true); edge(8000, true);

আউটপুট:

catalogue 100000 titles, 27.5 TB, Zipf alpha 0.9
cap_TB  policy   req_hit%  byte_hit%  origin_Gbps
   0.5  LRU        40.5      38.4        61.6
     2  LRU        56.5      55.0        45.0
     8  LRU        77.0      76.2        23.8
    20  LRU        87.8      87.4        12.6
     2  pinned     67.3      66.3        33.7
     8  pinned     83.5      83.0        17.0

পুরো catalogue ২৭.৫ TB. ০.৫ TB-এর edge-এ byte hit মাত্র ৩৮.৪% — ১০০ Gbit/s-এর ৬১.৬ যায় ওপরে। Cache ৪০ গুণ করে ২০ TB-তেও ৮৭.৪%, origin-এ তখনো ১২.৬ Gbit/s: long tail-এ প্রতিটি নতুন TB আগেরটির চেয়ে কম লাভ দেয়, আর প্রথমবার চাওয়া ভিডিও কোনো আকারেই hit নয়। Pre-positioning: ২ TB-তে LRU-র ৫৫.০%-এর জায়গায় ৬৬.৩%, ৮ TB-তে ৭৬.২%-এর জায়গায় ৮৩.০% — কারণ LRU একবার-চাওয়া ভিডিওর জন্যও জনপ্রিয় ভিডিও সরিয়ে দেয়। সীমা: এখানে জনপ্রিয়তা স্থির আর জানা, বাস্তবে নতুন ভিডিওর জনপ্রিয়তা অনুমান করতে হয়; আর বাস্তবে cache হয় segment ধরে, শুরুর segment বেশি গরম।

নকশা-সিদ্ধান্ত চারটি: (১) segment-এর দীর্ঘ TTL আর revision-সহ path — purge প্রায় লাগে না; (২) অঞ্চলপ্রতি একটি shield — long tail-এর miss একবারই origin-এ, আর নতুন ভাইরাল ভিডিওর প্রথম ভিড় request collapsing-এ এক; (৩) নতুন বা প্রচারিত ভিডিও রাতে edge-এ পাঠানো — cache warming, এক ধরনের push; (৪) খুব বড় সেবা ISP-র ভেতরেই cache বসায় (ভাবনার প্রশ্ন ৪)।

কে দেখতে পারবেন — signed URL আর DRM, ধারণার স্তরে

সদস্যদের বা অঞ্চল-সীমিত ভিডিওর সুরক্ষা কয়েক স্তরে, প্রতিটি আলাদা জিনিস রক্ষা করে:

  • Signed URL বা signed cookie (CDN-এ): playback API অনুমতি দেখে একটি সই করা token দেয় — path prefix (/v42/) আর মেয়াদ; edge প্রতিটি অনুরোধে database ছাড়াই সই আর মেয়াদ মেলায় (HMAC-ধাঁচ, পর্ব ২৬-এর presigned URL-এর চিন্তা)। ঠেকায় “লিংক পেলেই চিরকাল দেখা”, কিন্তু মেয়াদের মধ্যে ভাগ করা নয়।
  • DRM: segment একটি content key-তে এনক্রিপ্ট; key দেয় আলাদা license server, শুধু অনুমতিপ্রাপ্ত player-কে, নিয়মসহ (মেয়াদ, যন্ত্র)। ব্রাউজারে মান-রূপ W3C-র Encrypted Media Extensions (২০১৭ সালে Recommendation), আর একই segment কয়েকটি DRM-এ চালাতে Common Encryption. DRM কপি কঠিন করে, অসম্ভব নয় — পর্দা রেকর্ড ঠেকানো যায় না।
  • ভেতরের সুরক্ষা: bucket private, শুধু CDN-এর পরিচয়ে origin পড়া; key-গুলো envelope encryption-এ (পর্ব ৫২)।

View count — stream থেকে, আনুমানিক আর একটু দেরিতে

সবচেয়ে সরল নকশা — প্রতিটি দেখায় UPDATE videos SET views = views + 1 — একটি ভাইরাল ভিডিওতে ভেঙে পড়ে: মিনিটে ৫০,০০০ দেখা মানে একটি সারিতে সেকেন্ডে প্রায় ৮৩৩টি লেখা, প্রতিটি একই lock-এর জন্য লাইনে (পর্ব ২৪)। বদলে:

  1. ঘটনা, গণনা নয়: player batch-এ ঘটনা পাঠায় (অংশ ৩); ingest সেগুলো log-ভিত্তিক stream-এ লেখে, partition key video_id — এক ভিডিওর সব ঘটনা এক partition-এ, ক্রমসহ (পর্ব ৩৩)।
  2. “একটি দেখা” কী, সেটি নিয়ম: ধরা যাক একই সেশনে অন্তত ৩০ সেকেন্ড চললে একটি view (সংজ্ঞা সেবাভেদে আলাদা); পুনরাবৃত্ত ঘটনা dedup window-এ বাদ, তাই at-least-once-এও হিসাব idempotent.
  3. জানালা ধরে যোগ: প্রতি মিনিটের tumbling window-এ প্রতিটি ভিডিওর মোট counter store-এ একবার যোগ (পর্ব ৩৬) — ভাইরাল ভিডিওতেও মিনিটে একটি লেখা; খুব গরম key-তে sharded counter.
  4. আনুমানিক কাঠামো: আলাদা দর্শক — HyperLogLog, কয়েক KB-তে; এই ঘণ্টার ভাইরাল — count-min sketch আর heap দিয়ে top-k (পর্ব ৪৫)।
  5. দেখানো: গণনা cache থেকে, মিনিটখানেক পুরোনো হলেও চলে। জাল দেখা বাছাই পরে চলতে পারে, তাই গণনা পরে সংশোধিতও হয়; নির্মাতার আয়ের হিসাব হলে আলাদা, নিরীক্ষাযোগ্য batch.

Metadata, search আর ঠান্ডা ভিডিও

Search: metadata-র প্রতিটি বদল outbox বা CDC দিয়ে indexer-এ যায়, যা inverted index হালনাগাদ করে (পর্ব ২৭); index-এ শুধু ready আর public ভিডিও, আর private করা ভিডিও দ্রুত সরাতে হয় — দেরি মানে গোপনীয়তার ঝুঁকি। ঠান্ডা ভিডিও: আসল ফাইল ladder-এর প্রায় তিন-চতুর্থাংশ জায়গা নেয়, অথচ প্রক্রিয়ার পরে কদাচিৎ লাগে — lifecycle নিয়মে archive storage class-এ (পর্ব ২৬)। প্রায় না-দেখা ভিডিওর ওপরের মান cool স্তরে, বা মুছে দরকারে আবার encode — দাম: হঠাৎ জনপ্রিয় হলে প্রথম দর্শকেরা কিছুক্ষণ নিচু মান পাবেন।

৯ · ভাঙন, trade-off আর বিস্তার — কী ভাঙতে পারে, ১০ গুণে কী হবে

কী মরলকী হয়নকশার উত্তর
একটি CDN edgeসেই শহরের দর্শক দূরের edge-এ, latency বাড়েCDN-এর routing; বড় হলে দুটি CDN, playback API বাছে
Shield বা origin-এ চাপভাইরাল মুহূর্তে সব miss একসাথে ওপরেrequest collapsing, origin-এর সামনে load shedding, জনপ্রিয় ভিডিও আগেই edge-এ
Transcoding-এর queue ফুললনতুন ভিডিও দেরিতে প্রকাশ; দেখা চলেautoscaling, নিচু মান আগে প্রকাশ, queue-র বয়সে alert
Metadata DB-এর primaryনতুন আপলোড আর অবস্থা বদল বন্ধreplica থেকে failover; playback উত্তর cache-এ, তাই প্রকাশিত ভিডিও চলে (পর্ব ৫১)
View stream পিছিয়েগণনা দেরিতে বাড়েconsumer lag-এ alert; দেখা চলবেই — graceful degradation (পর্ব ১৮)
পুরো একটি regionআপলোড আর প্রক্রিয়া সেখানে বন্ধঅন্য region-এ কপি; CDN অন্য origin থেকে টানে

১০ গুণে কোথায় ভাঙবে? Peak egress প্রায় ৯.৪ Tbit/s — প্রথম সীমা CDN-এর ক্ষমতা আর বিল (বেশি edge, ISP-র ভেতরে cache, ভালো codec); transcoding বছরে ৭৩ লাখ ঘণ্টা (নতুন codec শুধু জনপ্রিয় ভিডিওতে); metadata-য় video_id ধরে আরও shard; view events-এ আরও partition আর client-এ বড় batch.

নজরদারি — কী নিয়ে alert: rebuffer ratio আর শুরুর দেরির p95 (দেশ, ISP, CDN ধরে); playback শুরু না হওয়ার হার; byte hit ratio আর origin egress; আপলোড ব্যর্থতা; “processing”-এ আটকে থাকা ভিডিওর বয়স; transcoding queue-র গভীরতা; view stream-এর consumer lag. SLO বসান দর্শকের অভিজ্ঞতায় (পর্ব ৫০)।

অপব্যবহার, প্রতিরক্ষার দিক থেকে: আপলোডে quota আর rate limit (পর্ব ৪৯); প্রতি token-এ যুক্তিসংগত segment-হার, যাতে কেউ পুরো catalogue টেনে নিতে না পারেন; প্রকাশের আগে বিষয়বস্তু যাচাই; লগে token আর সই করা URL-এর query string নয়।

Live streaming বিস্তার: পুরো ভিডিও আগে থেকে নেই, তাই টুকরো করে encode চলে না — প্রতিটি মান real time-এর চেয়ে দ্রুত encode হতে হয়, manifest প্রতি কয়েক সেকেন্ডে বদলায়; দেরি কমাতে Low-Latency HLS. বিস্তার ভাবনার প্রশ্ন ১-এ আর CN কোর্সের অংশ ৯-এ।

ইন্টারভিউর ৬০ সেকেন্ডের সারাংশ

“পর্দা একটি VOD প্ল্যাটফর্ম, ধরা যাক দিনে ২০,০০০ আপলোড আর ১ কোটি দর্শক; peak-এ প্রায় ৯৩৭.৫ Gbit/s egress, ঢোকা byte-এর প্রায় ৪৬৯ গুণ — তাই কেন্দ্র CDN আর byte. আপলোড presigned URL আর multipart-এ সরাসরি object store-এ, resumable, checksum-সহ; শেষ হলে outbox থেকে একটি event. Transcoding একটি DAG — probe, keyframe-এ টুকরো, টুকরো × মান সমান্তরালে, CMAF-এ package, publish; প্রতিটি কাজ idempotent. Player ABR দিয়ে প্রতিটি segment-এর মান বাছে; segment immutable, তাই CDN-এ দীর্ঘ TTL, shield আর জনপ্রিয় ভিডিও আগে থেকে বসানো। View count stream থেকে জানালা ধরে, আনুমানিক। Trade-off: প্রকাশের গতি বনাম encode-এর খরচ, শুরুর দেরি বনাম থেমে যাওয়া, আর ছবির মান বনাম egress-এর বিল।”

পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা system design ইন্টারভিউতে এই case-এ দুর্বল আর শক্ত উত্তরের পার্থক্য সাধারণত এখানে:

  • হিসাব: দুর্বল — “অনেক storage লাগবে”; শক্ত — ladder ধরে TB/বছর, peak একসাথে দর্শক × bitrate = Gbit/s, আর origin = মোট × (১ − hit ratio) দিয়ে হিসাব।
  • আপলোড: দুর্বল — অ্যাপ → API server → disk; শক্ত — presigned URL, multipart, resume, checksum আর outbox-এর event.
  • Transcoding: দুর্বল — “একটি worker ffmpeg চালাবে”; শক্ত — DAG, keyframe-এ টুকরো, সমান্তরালতার অঙ্ক, idempotent output আর খরচের trade-off.
  • Streaming: দুর্বল — “পুরো MP4 পাঠাব”; শক্ত — manifest, aligned segment, ABR-এর দুই নিয়ম আর QoE.
  • View count: দুর্বল — প্রতি দেখায় একটি UPDATE; শক্ত — stream, জানালা, dedup, HLL.

১০ · AI-এর যুগে — AI-এর ভিডিও-নকশা যাচাই

একটি ভিডিও প্ল্যাটফর্মের নকশা চাইলে AI কয়েক সেকেন্ডে বাক্স-তীরের খসড়া দেয় — প্রায়ই “S3 + Kafka + Redis + microservices” একসাথে। ভুল ধরা পড়ে সংখ্যায় আর ব্যর্থতার প্রশ্নে:

AI-এর দাবিকেন ভুল বা অসম্পূর্ণকীভাবে যাচাই করবেন
egress “১০ লাখ দর্শক × ৫ MB/s = ৫ TB/s”bit-byte গুলিয়ে ৮ গুণ ভুল; সবাই ১০৮০p ধরাএকক লিখে নিজে হিসাব; অংশ ২-এর calculator
“CDN সব সামলাবে, origin নিয়ে ভাবনা নেই”long tail আর shield বাদ; ৯৫% hit-এও origin-এ অনেক Gbit/sমোট × (১ − byte hit ratio); অংশ ৮-এর simulator
“আপলোডের অনুরোধের ভেতরেই transcode করে 200 দিন”দীর্ঘ কাজ request path-এ; ব্যর্থ হলে সব আবারপর্ব ৩৭-এর লক্ষণ; DAG আর 202
“VOD-এর জন্য WebRTC বা WebSocket”real-time কলের যন্ত্র; CDN-এ cache হয় না, খরচ বেশিHLS/DASH segment আর CDN-এর যুক্তি
“DRM থাকলে piracy অসম্ভব”, “Kafka-য় exactly-once, তাই view count নিখুঁত”DRM বাধা বাড়ায় মাত্র; exactly-once-এর পরিধি সীমিত, আর জাল দেখা আলাদা সমস্যাপর্ব ৩৪; DRM-এর হুমকি-মডেল
CDN-এর দাম, part-এর সীমা বা codec-এর সাশ্রয় আত্মবিশ্বাসে বলাপ্রায়ই পুরোনো বা বানানো সংখ্যাvendor-এর বর্তমান নথি; নিজের ভিডিওতে ছোট পরীক্ষা

প্রশ্নে সংখ্যা, সীমা আর ব্যর্থতার প্রশ্ন দিয়ে দিন, আর প্রতিটি সংখ্যার হিসাব চান:

তুমি একজন অভিজ্ঞ video platform আর CDN ইঞ্জিনিয়ার। একটি কাল্পনিক VOD সেবা: দিনে ২০,০০০ আপলোড, গড় ৬ মিনিট; দৈনিক দর্শক ১ কোটি, গড়ে ৩০ মিনিট; দর্শক মূলত মোবাইল ডেটায়, গতি বদলায়। ১. একটি bitrate ladder দাও, তারপর বছরের storage, peak egress (Gbit/s) আর ৯৫% byte hit ratio-তে origin traffic ধাপে ধাপে হিসাব করো — bit আর byte আলাদা লিখে। ২. Resumable আপলোড: presigned URL-এর মেয়াদ, part-এর আকার, checksum কোথায়, completion event হারালে কী হবে। ৩. Transcoding-এর DAG: কাজের সংখ্যা, সমান্তরালতার সীমা, retry, idempotent output key, খরচ বনাম প্রকাশের সময়। ৪. দুর্বল network-এ throughput বনাম buffer-ভিত্তিক ABR — শুরুর দেরি আর rebuffering ধরে। ৫. View count কীভাবে গুনবে, কতটা নির্ভুল হবে। ৬. দাম, সীমা বা codec-এর সাশ্রয়ের সংখ্যায় সূত্র দাও; নিশ্চিত না হলে "জানি না" বলো। বাড়তি component শুধু সংখ্যা দিয়ে দরকার দেখাতে পারলে।

AI-এর ভিডিও-নকশা হাতে পেলে — যাচাইয়ের চেকলিস্ট

AI দেয় দ্রুত খসড়া, যাচাইয়ের দায় আপনার।
Byte গুনুন, miss গুনুন, আর জিজ্ঞেস করুন — এই কাজটি দুবার চললে কী হবে?

ভাবনার প্রশ্ন

ইন্টারভিউয়ার মূল নকশার পরে এই ধরনের প্রশ্নে চাপ দেন। নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১“এবার live streaming যোগ করুন” — VOD-এর নকশার কোন অংশ টিকবে, কোনটি পুরো বদলাতে হবে?

টিকবে বিতরণের অর্ধেক: HTTP-র ওপর segment, manifest, CDN, ABR আর signed URL. বদলাবে প্রায় পুরো লেখার পথ, কারণ live-এ “পুরো ভিডিও” বলে কিছু নেই — ভবিষ্যতের অংশ এখনো ঘটেনি।

Ingest: নির্মাতা আর ফাইল আপলোড করেন না, একটি অবিরাম stream পাঠান। ঐতিহাসিকভাবে Flash-যুগের RTMP এই কাজে সবচেয়ে প্রচলিত ছিল, আর এখনো অনেক সম্প্রচার-সফটওয়্যার তা দিয়ে পাঠায়; নতুন বিকল্প হিসেবে SRT বা WebRTC-ভিত্তিক ingest-এর কথাও শোনা যায় (সমর্থন সেবাভেদে আলাদা)।

Transcoding: অংশ ৬-এর কৌশল — পুরো ফাইল টুকরো করে শত worker-এ — এখানে চলে না। প্রতিটি মান real time-এর চেয়ে দ্রুত encode হতেই হবে, নইলে পিছিয়ে পড়তে পড়তে stream ভেঙে যায়। সমান্তরালতা শুধু মানের মধ্যে, আর একটি encoder মরলে কয়েক সেকেন্ডেই বিকল্প লাগে — তাই প্রায়ই দুটি encoder একই stream-এ।

Packaging আর manifest: segment তৈরি হয় চলতে চলতে; manifest প্রতি segment-এ বদলায়, তাই CDN-এ তার TTL কয়েক সেকেন্ড, segment-এর TTL তবু দীর্ঘ। দর্শক সম্প্রচারের কতটা পিছিয়ে থাকবেন তার বড় অংশ segment-এর দৈর্ঘ্য × player-এর রাখা segment-সংখ্যা — CN কোর্সের হিসাবে ৬ সেকেন্ড × ৩টি = ১৮ সেকেন্ড। Apple ২০১৯ সালে Low-Latency HLS ঘোষণা করে, যেখানে segment-এর ছোট অংশ তৈরি হতে হতেই পাঠানো যায়; দেরি কয়েক সেকেন্ডে নামতে পারে, তবে CDN আর player-এর সমর্থন লাগে।

ভিড়: খেলার ফাইনালে লাখো দর্শক একই মুহূর্তে একই নতুন segment চান — পর্ব ১৭-এর request collapsing আর shield এখানে বাঁচায়, নইলে origin ভেঙে পড়ে। View count আর chat আলাদা real-time সিস্টেম (পর্ব ৫৫)।

মূল উপলব্ধি: VOD-এ সময় আমাদের পক্ষে — আগে থেকে সব বানিয়ে রাখা যায়; live-এ সময় বিপক্ষে — প্রতিটি ধাপকে real time মেনে চলতে হয়, আর latency, স্থিতি ও খরচের নতুন trade-off আসে।

প্র ০২H.264 থেকে AV1 — নতুন codec কম bit-এ একই ছবি দেয়, তবু কেন সব ভিডিও সবচেয়ে নতুন codec-এ রাখা হয় না?

Codec বাছাই একটি তিন-দিকের টানাপোড়েন: সংকোচন (কত কম bit), encode-এর খরচ, আর দর্শকের যন্ত্রে চলবে কি না।

ইতিহাস সংক্ষেপে: H.264 (AVC) মান হিসেবে আসে ২০০৩ সালে, আর আজ প্রায় সব ফোন, ব্রাউজার আর টিভিতে hardware-এ decode হয় — নিরাপদ ভিত্তি। H.265 (HEVC) আসে ২০১৩ সালে, ভালো সংকোচনসহ, কিন্তু তার patent-লাইসেন্সের জটিলতা নিয়ে বহু আলোচনা হয়েছে। Google-এর VP9 (২০১৩) royalty-free হিসেবে প্রচারিত। আর একদল প্রতিষ্ঠানের জোট Alliance for Open Media ২০১৮ সালে AV1 প্রকাশ করে, আবারও royalty-free লক্ষ্য নিয়ে। প্রকাশিত তুলনায় নতুন codec সাধারণত একই মানে উল্লেখযোগ্যভাবে কম bit দেখায়, তবে পরিমাণ বিষয়বস্তু, encoder আর মাপার পদ্ধতিভেদে অনেক আলাদা।

কেন সবকিছু নতুন codec-এ নয়: (১) নতুন codec-এর encode প্রায়ই অনেক গুণ বেশি CPU খায় — অংশ ৬-এর worker-মিনিট কয়েক গুণ হয়ে যায়। (২) পুরোনো বা সস্তা ফোনে hardware decode নেই; software decode-এ ব্যাটারি দ্রুত ফুরোয়, ফোন গরম হয়। তাই H.264 সংস্করণ সবসময় রাখতেই হয়, নতুনটি হয় বাড়তি জায়গা।

অঙ্কটি কোথায় মেলে: encode একবার, কিন্তু সাশ্রয় প্রতিটি দেখায়। ধরুন নতুন codec ভিডিওপ্রতি encode-এ বাড়তি C টাকা লাগায় আর প্রতি দেখায় s টাকার egress বাঁচায়; তাহলে দেখা V > C ÷ s হলে লাভ। Zipf-ধাঁচের জনপ্রিয়তায় অল্প কিছু ভিডিওই বেশিরভাগ দেখা পায় — তাই প্রচলিত কৌশল: সব ভিডিও H.264-এ, আর যেগুলো জনপ্রিয় হয়ে উঠছে সেগুলো পরে নতুন codec-এ, অগ্রাধিকারের queue-তে। Player manifest-এ যন্ত্রের সমর্থন দেখে বেছে নেয়। Netflix-এর ২০১৫ সালের per-title encoding blog একই যুক্তির আরেক রূপ: প্রতিটি ভিডিওর জন্য আলাদা ladder.

মূল উপলব্ধি: সবচেয়ে ভালো codec সবচেয়ে নতুনটি নয়; সেটি হলো যার বাড়তি খরচ দেখার সংখ্যা দিয়ে ফেরত আসে — তাই সিদ্ধান্ত ভিডিওপ্রতি, জনপ্রিয়তা ধরে।

প্র ০৩ভিডিওর জন্য বিশেষ UDP protocol না বানিয়ে সাধারণ HTTP-র ওপর ফাইল-টুকরো (segment) কীভাবে VOD-এর মানদণ্ড হলো?

শুনতে উল্টো লাগে: ভিডিও তো সময়-সংবেদনশীল, আর TCP-ভিত্তিক HTTP হারানো packet-এর জন্য অপেক্ষা করে। তবু ২০০০-এর দশকের শেষ থেকে VOD প্রায় পুরোপুরি HTTP-তে চলে গেছে।

আগের যুগ: RTSP (RFC 2326, ১৯৯৮) আর RTP দিয়ে streaming server প্রতিটি দর্শকের জন্য একটি অবস্থা রাখত — কোথায় আছেন, কত গতিতে পাঠাতে হবে। Flash-এর RTMP-ও ছিল এ ধাঁচের, স্থায়ী সংযোগের। সমস্যা: প্রতিটি দর্শক সার্ভারে একটি অবস্থা, তাই বড় করতে বিশেষ সার্ভার; অনেক অফিস আর বাসার firewall বা NAT অপরিচিত port আটকায়; আর সাধারণ web cache এগুলো রাখতে পারত না।

HTTP-র উত্থান: Microsoft-এর Smooth Streaming (২০০৮ নাগাদ), Apple-এর HLS (২০০৯), Adobe-র HDS — সবাই একই চিন্তায় পৌঁছাল: ভিডিও ছোট ফাইলে কাটা থাকবে, সাথে একটি তালিকা, আর client নিজে চেয়ে নেবে। তারপর ২০১২ সালে MPEG-DASH আন্তর্জাতিক মান হলো, আর CMAF (২০১৮ নাগাদ) segment-এর ফরম্যাট এক করল। জেতার কারণ system design-এর ভাষায়: (১) server stateless — যেকোনো web server বা object store কাজ করে; (২) CDN-এর বিদ্যমান বিশাল cache-জাল সরাসরি ব্যবহার করা যায় — অংশ ৮-এর পুরো অঙ্ক এর ওপর দাঁড়িয়ে; (৩) port 443, তাই firewall-এর সমস্যা নেই; (৪) বুদ্ধি client-এ, তাই নতুন ABR নিয়ম চালু করতে সার্ভার বদলাতে হয় না।

TCP-র দুর্বলতা কোথায় গেল? VOD-এ player কয়েক থেকে কয়েক দশ সেকেন্ড আগে থেকে জমিয়ে রাখে, তাই একটি packet হারিয়ে কয়েকশো ms দেরি buffer শুষে নেয়। Real-time কলে এই বিলাসিতা নেই — সেখানে এখনো RTP আর WebRTC (CN কোর্সের অংশ ৬–৮)। আর HTTP/3 (QUIC, UDP-র ওপর) head-of-line blocking কমিয়ে দুই জগতের মাঝে নতুন পথ খুলছে।

মূল উপলব্ধি: “সবচেয়ে উপযুক্ত” protocol নয়, যে protocol বিদ্যমান অবকাঠামো — CDN, firewall, web server — বিনা খরচে ব্যবহার করতে দেয়, বড় আকারে সেটিই জেতে।

প্র ০৪Egress-এর বিল কমাতে বললে কোন কোন হাতল আছে? ISP-র নেটওয়ার্কের ভেতরে cache বসানো কি সবার জন্য সমাধান?

অংশ ২-এর হিসাবে দিনে ৩.৩৭৫ PB বেরোয়, আর এক বছরের নতুন storage প্রতি ২.৭ দিনে একবার বেরিয়ে যায় — তাই খরচের আলোচনা শুরু হয় এখান থেকে। হাতলগুলো গুরুত্বের মোটামুটি ক্রমে:

১ · কম byte পাঠানো: per-title encoding আর জনপ্রিয় ভিডিওতে নতুন codec (প্রশ্ন ২)। ছোট পর্দায় ১০৮০p-এর দরকার কম — player পর্দার মাপের চেয়ে বড় মান না চাইলে বড় সাশ্রয়, আর “data saver” বিকল্প মোবাইল ডেটার দর্শকদের কাজে লাগে।

২ · Origin-এ কম যাওয়া: byte hit ratio বাড়ানো — shield, pre-positioning, বড় edge cache — অংশ ৮-এর simulator দেখুন। Origin-এর egress প্রায়ই সবচেয়ে দামি, কারণ সেটি cloud থেকে বাইরে যায়।

৩ · প্রতি GB-এর দাম কমানো: একাধিক CDN-এর মধ্যে প্রতিযোগিতা, আর দেশের ভেতরের internet exchange-এ peering, যাতে byte বিদেশের দামি link পার না হয়। বাংলাদেশের মতো দেশে আন্তর্জাতিক bandwidth তুলনামূলক দামি বলে ধারণা করা হয়, তাই দেশের ভেতরের cache সবার জন্য লাভজনক হতে পারে — নিজের হিসাবে যাচাই করবেন।

৪ · ISP-র ভেতরে cache: খুব বড় ভিডিও সেবা ISP-র data center-এ নিজেদের cache-যন্ত্র বসায়। প্রকাশিত তথ্য অনুযায়ী Netflix ২০১২ নাগাদ Open Connect চালু করে, যেখানে ISP বিনা মূল্যে যন্ত্র পায় আর রাতের কম ব্যস্ত সময়ে পরদিনের জনপ্রিয় catalogue যন্ত্রে ভরা হয় — অংশ ৮-এর pre-positioning-এর বড় রূপ। Google-ও তার Google Global Cache প্রকল্প প্রকাশ্যে বর্ণনা করেছে।

সবার জন্য নয় কেন? যন্ত্র, পরিচালনা আর প্রতিটি ISP-র সাথে চুক্তি তখনই লাভজনক, যখন একটি ISP-র ভেতরে আপনার traffic এত বেশি যে ISP-ও লাভ দেখে; ছোট সেবার পথ বাণিজ্যিক CDN আর ভালো hit ratio.

মূল উপলব্ধি: egress কমানোর ক্রম — আগে কম byte, তারপর কম miss, তারপর সস্তা byte; বিশেষ অবকাঠামো আসে শেষে, আকার যখন তাকে যুক্তিসংগত করে।

প্র ০৫View count কেন কখনো আটকে থাকে, বা পরে কমে যায়? নিখুঁত গণনা কি আদৌ সম্ভব, আর দরকার?

২০১০-এর দশকের প্রথমার্ধে অনেকে লক্ষ করেছিলেন, YouTube-এ নতুন ভিডিওর গণনা প্রায় ৩০০-র কাছে কিছুক্ষণ আটকে থাকে; প্রকাশ্য আলোচনা অনুযায়ী সেই সময়ে জাল দেখা বাছাইয়ের যাচাই চলত, আর পরে ব্যবস্থাটি বদলানো হয়। ভেতরের নকশা আমরা জানি না, কিন্তু শিক্ষাটি সাধারণ: গণনা দুটি আলাদা প্রশ্ন — “কয়টি ঘটনা এল” আর “কয়টি সত্যিকারের দেখা”।

প্রথম প্রশ্ন — ঘটনা না হারানো, দুবার না গোনা: network কাটলে অ্যাপ আবার পাঠায়, তাই ঘটনা at-least-once; প্রতিটি ঘটনায় (session, ক্রমিক নম্বর), আর stream processor একটি dedup window-এ পুনরাবৃত্তি বাদ দেয় (পর্ব ৩৪)। Kafka-ধাঁচের সিস্টেমের “exactly-once” প্রতিশ্রুতি নির্দিষ্ট পরিধিতে — log থেকে পড়ে log-এ লেখা — কাজ করে; ফোন থেকে আসা দুটি একই ঘটনা চিনতে তবু নিজের key লাগে।

দ্বিতীয় প্রশ্ন — সত্যিকারের দেখা: script-চালিত player বা একই যন্ত্র থেকে অস্বাভাবিক হার বাদ দিতে নিয়ম লাগে, আর অনেক নিয়ম কয়েক ঘণ্টার আচরণ দেখে তবেই সিদ্ধান্ত নিতে পারে। তাই গণনা “আগে আনুমানিক, পরে সংশোধিত” — stream-এ দ্রুত সংখ্যা আর batch-এ পরে নিরীক্ষিত সংখ্যা, পর্ব ৩৬-এর lambda ধাঁচ।

নিখুঁত কি দরকার? পাতায় “১২ লাখ বার দেখা” লেখায় কয়েক হাজারের পার্থক্য কেউ টের পান না, তাই সেখানে আনুমানিক কাঠামো আর মিনিটখানেকের দেরি যুক্তিসংগত — HyperLogLog-এ আলাদা দর্শক কয়েক KB-তে, প্রায় ১% ভুলে (পর্ব ৪৫)। কিন্তু নির্মাতাকে দেখার ভিত্তিতে টাকা দিলে সংখ্যাটি একটি হিসাবের খাতা — সেখানে নিরীক্ষাযোগ্য batch, পুনরায় চালানো যায় এমন pipeline আর ব্যাখ্যাযোগ্য নিয়ম লাগে।

মূল উপলব্ধি: “নির্ভুল” কতটা লাগবে, সেটি ব্যবহার ঠিক করে; একই ঘটনার ধারা থেকে দ্রুত-আনুমানিক আর ধীর-নিরীক্ষিত দুটি সংখ্যা বানানোই সাধারণ নকশা।

অনুশীলন

কাগজ-কলমে হিসাব করুন, নকশা আঁকুন, তারপর উত্তর মেলান। শেষ প্রশ্নে আগে অনুমান, তারপর কোড চালিয়ে যাচাই।

  1. বড় সেবার হিসাব: একটি কাল্পনিক ভিডিও সেবায় দৈনিক দর্শক ২ কোটি, প্রত্যেকে গড়ে ৪০ মিনিট; peak-এর গুণক ৩; 4G বাড়ায় গড় দেওয়া bitrate ২.৫ Mbit/s; CDN-এর byte hit ratio ৯৮%; segment ৪ সেকেন্ড। (ক) গড় আর peak একসাথে দর্শক কত? (খ) Peak egress, CDN আর origin-এর অংশ? (গ) Peak-এ সেকেন্ডে কয়টি segment GET? (ঘ) দিনে মোট কত PB বেরোয়? (ঙ) Hit ratio ৯৫% থেকে ৯৮% হলো, তবু origin-এর traffic অংশ ২-এর চেয়ে বাড়ল কেন?
    • (ক) গড় = ২,০০,০০,০০০ × ৪০ ÷ ১,৪৪০ ≈ ৫,৫৫,৫৫৬; peak = × ৩ ≈ ১৬,৬৬,৬৬৭ জন।
    • (খ) ১৬,৬৬,৬৬৭ × ২.৫ Mbit/s ≈ ৪১,৬৬,৬৬৭ Mbit/s = ৪,১৬৬.৭ Gbit/s, প্রায় ৪.১৭ Tbit/s. CDN ৯৮% = ৪,০৮৩.৩, origin ২% = ৮৩.৩ Gbit/s.
    • (গ) প্রতিটি দর্শক ৪ সেকেন্ডে একটি segment: ১৬,৬৬,৬৬৭ ÷ ৪ ≈ ৪,১৬,৬৬৭ GET প্রতি সেকেন্ডে।
    • (ঘ) ২ কোটি × ৪০ × ৬০ সেকেন্ড × ২.৫ × ১০৬ bit ÷ ৮ = ১.৫ × ১০১৬ byte = ১৫ PB দিনে।
    • (ঙ) মোট ৯৩৭.৫ থেকে ৪,১৬৬.৭ Gbit/s — প্রায় ৪.৪ গুণ, অথচ miss ৫% থেকে ২% — মাত্র ২.৫ গুণ কম; তাই origin ৪৬.৯ থেকে ৮৩.৩ Gbit/s. Hit ratio-র উন্নতি মোট বৃদ্ধির সাথে পাল্লা না দিলে origin-এর ভার বাড়েই।

    যাচাই: অংশ ২-এর calculator-এ DAU = 2e7, WATCH_MIN = 40, PLAY_MBPS = 2.5, HIT = 0.98 বসালে আসে:

    viewers: 555556 avg, 1666667 peak concurrent
    peak egress 4166.7 Gbit/s: CDN 4083.3, origin 83.3
    play API 1157/s avg, 3472/s peak; segment GETs 416667/s peak
    delivered 15.000 PB/day; one year of new storage leaves every 0.6 days
  2. Requirement বদলাল, নতুন নকশা: নির্মাতারা এখন চান — ৬০ মিনিটের একটি ভিডিও আপলোড শেষের ২ মিনিটের মধ্যে অন্তত কোনো মানে দেখা যাক, আর সব মান ১৫ মিনিটের মধ্যে। অংশ ৬-এর নকশা আর simulator-এর ফল ধরে কী কী বদলাবেন? কোন দামে?

    লক্ষ্য দুই ভাগে — progressive publish: নিচের তিন মান (২৪০p, ৩৬০p, ৪৮০p) আমাদের অনুমানে মিলিয়েও ১০৮০p-এর একার চেয়ে সস্তা, তাই সেগুলোর কাজ আগে, অগ্রাধিকারের queue-তে। তিনটি তৈরি হলেই manifest প্রকাশ আর status = ready (সীমিত মান); ওপরের মান তৈরি হলে নতুন revision-এর manifest, playback API নতুন দর্শককে সেটি দেয় — CDN-এ purge লাগে না।

    সমান্তরালতা: simulator-এ N = ১২০, W = ৩০০-এ পুরো ladder ৩.৭ মিনিট, তাই ১৫ মিনিট সহজ; শুধু নিচের তিন মানে আরও কম। টুকরো ৩০ সেকেন্ড বা কম, কিন্তু N = ৭২০-এর মতো অতি ছোট নয় — সেখানে বিল প্রায় ৭৩% বেশি।

    Worker তৈরি রাখা: কিছু উষ্ণ worker সবসময় চালু, তাড়ার কাজে স্থির যন্ত্র, পেছনের কাজে spot — দাম অলস যন্ত্রের খরচ।

    আপলোডের সাথেই শুরু: অ্যাপ এমন ফরম্যাটে পাঠালে যা আংশিক খোলা যায় (যেমন fragmented MP4), প্রথম part-গুলো পৌঁছালেই প্রথম টুকরোর কাজ শুরু। দাম: জটিলতা, আর ফাইল শেষে বিকৃত হলে নষ্ট কাজ। মাপবেন “আপলোড শেষ থেকে প্রথম প্রকাশ” আর “সব মান”-এর p95.

  3. একটি ত্রুটিপূর্ণ নকশা সমালোচনা করুন: একটি কাল্পনিক ভিডিও অ্যাপের প্রথম সংস্করণ: অ্যাপ পুরো ভিডিও এক অনুরোধে API server-এ পাঠায়; server ফাইলটি নিজের disk-এ রেখে একই অনুরোধের ভেতরে ffmpeg দিয়ে শুধু একটি ১০৮০p MP4 বানায়, তারপর 200; দর্শক সেই MP4 সরাসরি server থেকে পান; প্রতিটি দেখায় UPDATE videos SET views = views + 1. সমস্যাগুলো বলুন আর প্রতিটির সমাধান দিন।
    • এক অনুরোধে পুরো আপলোড: কাটলে শূন্য থেকে, বড় বাইট API server আটকায় → presigned URL, multipart, resume (অংশ ৫)।
    • Local disk: অন্য server-এ ফাইল নেই, server মরলে আসল ফাইল হারায় → object storage আর archive.
    • Request-এর ভেতরে transcode: ঘণ্টাখানেক অনুরোধ খোলা, timeout, ব্যর্থ হলে সব আবার → 202 আর DAG, টুকরো করে সমান্তরালে, idempotent কাজ (অংশ ৬)।
    • শুধু ১০৮০p: ২.৫ Mbit/s-এর দর্শকের কাছে ৫ Mbit/s বারবার থামবে (অংশ ৭-এ স্থির ৭২০p-ই ৫৫৯ সেকেন্ড থেমেছিল) → ladder, segment আর ABR.
    • সরাসরি server থেকে MP4: CDN নেই, অংশ ২-এর ৯৩৭.৫ Gbit/s-এর পুরোটা origin-এ → segment, CDN, shield, দীর্ঘ TTL.
    • প্রতি দেখায় UPDATE: ভাইরাল ভিডিওর একটি সারিতে সেকেন্ডে শত শত লেখা → stream, জানালা ধরে যোগ, cache থেকে দেখানো (অংশ ৮)।
    • যাচাই নেই: ধরন, আকার, checksum, malware → দুই স্তরের checksum, probe আর বিষয়বস্তুর যাচাই।
  4. আউটপুট অনুমান করুন: অংশ ৬-এর transcode_parallel.js-এ শেষ লাইনের তালিকা বদলে [[60, 300], [60, 1000], [12, 300]] করলে wall_min আর serial_min কত হবে? চালানোর আগে লিখুন। ইঙ্গিত: কাজের সময় আর ব্যর্থতা W-এর ওপর নির্ভর করে না।

    অনুমানের যুক্তি: PRNG-এর ক্রম শুধু কাজের ক্রম আর চেষ্টার ওপর নির্ভর করে, W-এর ওপর নয় — তাই একই N-এ serial_min আর fails বদলায় না: N = ৬০-এ ২২৮.৪ আর ১২, N = ১২-এ ২১১.৪ আর ১. N = ৬০-এ কাজ ৩০০টি; W = ৩০০ হলে সবাই একসাথে শুরু করে, wall = split + সবচেয়ে লম্বা কাজের শিকল (একটি ব্যর্থ চেষ্টা থাকলে তার নষ্ট সময়সহ) + package. W = ১,০০০ হলে বাড়তি ৭০০টি worker বসে থাকে — wall একই। N = ১২-এ ৬০টি কাজ, প্রতিটি ৩০০ সেকেন্ডের টুকরো; ১০৮০p-এর একটি কাজই কয়েক মিনিটের, তাই W যত বড়ই হোক, wall দশ মিনিটের ঘরে থাকবে।

    চালানোর আসল ফল:

        N  seg_s  tasks    W  fails  serial_min  wall_min  speedup
       60     60    300  300     12     228.4      3.8    59.4x
       60     60    300 1000     12     228.4      3.8    59.4x
       12    300     60  300      1     211.4     14.3    14.8x

    W = ৩০০ আর ১,০০০ দুটোতেই ৩.৮ মিনিট — split (০.৫) আর package (প্রায় ০.৬৭) বাদে প্রায় ২.৬ মিনিট সবচেয়ে লম্বা কাজের শিকলের, যার নিচে নামা যায় না। N = ১২-এ ১৪.৩ — W = ২০-এর ১৭.২ থেকে সামান্য কম, কারণ সীমা এখন টুকরোর আকার। শিক্ষা: worker বাড়ানোর আগে দেখুন সবচেয়ে লম্বা কাজটি কত লম্বা।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি ভিডিও আপলোড থেকে দর্শকের কাছে চালানোর যোগ্য হওয়া পর্যন্ত ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 🔐 অ্যাপ API-র কাছে অনুমতি চায়; API video_id বানায় আর part-এর সই করা URL দেয়
  2. 📤 অ্যাপ part-গুলো সরাসরি object store-এ পাঠায়, কাটলে বাকি থেকে আবার
  3. ✅ Complete: আকার মেলে, একই transaction-এ status আর outbox-এ event
  4. 🔍 Worker পুরো ফাইলের checksum মেলায়, probe করে, keyframe-এ টুকরো করে
  5. ⚙️ প্রতিটি টুকরো প্রতিটি মানে সমান্তরালে encode, তারপর HLS/DASH-এ package
  6. 🌍 Publish: status = ready, search index হালনাগাদ, দর্শক CDN থেকে দেখেন

একটি ভিডিও সেবায় peak-এ একসাথে ৪ লাখ দর্শক, গড় দেওয়া bitrate ২.৫ Mbit/s, আর CDN-এর byte hit ratio ৯৬%. Peak-এ origin থেকে প্রায় কত traffic যায়?

  • প্রায় ৪০ Gbit/s
  • প্রায় ৯৬০ Gbit/s
  • প্রায় ১০০ Gbit/s
  • প্রায় ৫ Gbit/s

Adaptive streaming-এর জন্য একটি ভিডিওর পাঁচটি মান encode করার সময় কেন সব মানে keyframe আর segment-এর সীমানা ঠিক একই সময়ে রাখা হয়?

  • যাতে সব মানের segment মিলিয়ে storage কম লাগে
  • যাতে CDN পাঁচটি মানকে একটি object হিসেবে cache করে
  • যাতে player যেকোনো segment-এর সীমানায় মান বদলালেও ছবি নিরবচ্ছিন্ন আর decode-যোগ্য থাকে
  • যাতে encode দ্রুত হয়

একটি player-এর buffer-ভিত্তিক নিয়ম: buffer ৮ সেকেন্ড বা কম হলে সবচেয়ে নিচু মান, ২৪ সেকেন্ড বা বেশি হলে সবচেয়ে উঁচু, মাঝে সমানুপাতিক। এই মুহূর্তে buffer ৬ সেকেন্ড, আর শেষ segment এসেছে ৯ Mbit/s গতিতে। পরের segment কোন মানে চাওয়া হবে?

  • সবচেয়ে উঁচু মান, কারণ গতি ৯ Mbit/s
  • সবচেয়ে নিচু মান, কারণ buffer ৮ সেকেন্ডের নিচে
  • মাঝের একটি মান
  • কোনোটিই নয় — player থেমে buffer ভরার অপেক্ষা করবে

একটি ভিডিও সেবা প্রতিটি দেখায় একটি relational database-এ UPDATE videos SET views = views + 1 WHERE video_id = ? চালায়। একটি ভিডিও ভাইরাল হয়ে মিনিটে ৫০,০০০ বার দেখা হচ্ছে। সবচেয়ে উপযুক্ত পরিবর্তন কোনটি?

  • Database-কে আরও বড় যন্ত্রে নেওয়া
  • Read replica যোগ করা
  • Isolation level কমিয়ে read uncommitted করা
  • দেখার ঘটনা একটি partition-করা stream-এ পাঠিয়ে প্রতি মিনিটের জানালায় যোগ করে একবার লেখা

৪০ মিনিটের একটি ভিডিও ৩০ সেকেন্ডের ৮০টি টুকরোয় কাটা, প্রতিটি টুকরো ৫টি মানে encode, প্রতিটি কাজ প্রায় ৬০ সেকেন্ড, worker ১০০টি। Split, package আর ব্যর্থতা বাদে encode-এর wall-clock প্রায় কত?

  • প্রায় ১ মিনিট
  • প্রায় ৪ মিনিট
  • প্রায় ৪০ মিনিট
  • প্রায় ৪০০ মিনিট

একটি ভিডিও সেবা শুধু সদস্যদের জন্য একটি ভিডিওর segment CDN-এ রাখে, আর playback API একটি সই করা token দেয় — path prefix /v42/, মেয়াদ ৬ ঘণ্টা; edge প্রতিটি অনুরোধে সই আর মেয়াদ মেলায়। এই ব্যবস্থা কী নিশ্চিত করে?

  • কেউ ভিডিও কপি বা রেকর্ড করতে পারবেন না
  • Token শুধু সেই একটি ফোনেই কাজ করবে
  • বৈধ, মেয়াদ-না-ফুরোনো token ছাড়া সেই path-এর segment CDN দেবে না; তবে মেয়াদের মধ্যে ভাগ করা ঠেকায় না
  • Segment-গুলো এনক্রিপ্ট হয়ে যায়

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. ভিডিও পড়া-প্রধান আর byte-প্রধান: ladder ধরে storage, একসাথে দর্শক × bitrate = peak egress, আর origin = মোট × (১ − byte hit ratio) দিয়ে শুরু।
  2. আপলোড সরাসরি object store-এ — presigned URL, multipart, resume, part আর পুরো ফাইলের checksum, আর outbox থেকে একটি completion event; API server বাইট বয় না।
  3. Transcoding একটি DAG: probe → keyframe-এ টুকরো → টুকরো × মান সমান্তরালে → package → publish; wall-clock-এর সীমা সবচেয়ে লম্বা কাজ, আর ছোট টুকরো মানে বেশি বিল। কাজ idempotent, lease আর সীমিত retry-সহ।
  4. ABR: সব মানে একই সীমানার segment; throughput নিয়ম সাবধানী, buffer নিয়ম বেশি মান দেয় কিন্তু হঠাৎ পতনে থামে — শুরুর দেরি, থামা আর মানের trade-off, মাপা হয় QoE দিয়ে।
  5. বিতরণ: immutable segment, দীর্ঘ TTL, shield, জনপ্রিয় ভিডিও আগে থেকে edge-এ; long tail-এ cache-এর লাভ কমে। View count stream আর জানালা থেকে, আনুমানিক; নিরাপত্তা signed URL আর দরকারে DRM.

পরের পর্বে: ভিডিওতে কয়েক মিনিটের দেরি আর সামান্য ভুল গণনা চলে; টিকিট আর টাকায় চলে না। ঈদের আগে বাসের টিকিটের ভিড়ের মতো চাপে একই আসন দুজনকে না দেওয়া আর টাকার হিসাব মেলানো — পর্ব ৬০-এর case study: ticket booking আর payment — concurrency, idempotency আর ledger.

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান