At a glance#
| Number | Value | What it drives |
|---|---|---|
| Uploads | 20M+ / day ≈ 231 / s | design peak 1,000 / s (3× evening peak, assumed) |
| Video arriving | 500+ h / min = 30,000 s of video every second | average upload ~2.2 min; long videos carry the bytes |
| Originals | ~2.6 PB / day · ~720 Gbps peak | 8 Mbps average source (assumed); kept forever |
| Open upload sessions | ~600,000 | front ends keep no file in memory; state in a session table |
| Transcoding | ~450,000 cores in software · ~18,000 with chips | one codec; YouTube reports 20–33× from its VCU |
| Views | 200B+ Shorts views / day · ~6M starts / s peak | ~18M events / s; one viral video at 50,000 / s |
| Recommendations | ~1.2M requests / s · 100 ms | 20B+ videos → hundreds of candidates → 20 shown |
| Largest upload | 256 GB or 12 hours | resumable chunks, multiples of 256 KB |
Architecture#
DiagramUpload, processing, views and recommendations on one page
%% title: Upload, processing, views and recommendations on one page
flowchart TD
CR@{ icon: "sd:phone", label: "Creators<br/>phone · browser · studio apps" } ==>|"chunks, resumable"| UP@{ icon: "sd:gateway", label: "Upload front ends<br/>session table · 308 + Range" }
UP ==> ORIG@{ icon: "sd:object-storage", label: "Originals<br/>kept forever" }
ORIG -.->|"upload complete"| ORCH["Processing orchestrator<br/>one task graph per video<br/>tiers · caps · retries"]:::hot
ORCH ==> TR@{ icon: "sd:cpu", label: "Transcode pool<br/>closed-GOP chunks in parallel" }
ORCH --> META@{ icon: "tech:mysql", label: "Video metadata · sharded MySQL<br/>renditions ready · policy result" }
TR ==> REND@{ icon: "sd:cdn", label: "Renditions + CDN<br/>segments · manifests · rebuildable" }
VW@{ icon: "sd:users", label: "Viewers<br/>web · apps · TVs" } ==> API["Watch and feed API<br/>manifest lists only ready renditions"]:::core
API --> REND
API --> REC["Recommendations<br/>candidates → ranking → rules"]:::core
VW -.->|"view_start · heartbeats"| EV@{ icon: "tech:kafka", label: "Kafka · view events<br/>partitioned by view_id" }
EV --> CNT["View counter<br/>dedupe · quality filter · sharded"]:::core
EV -.-> REC
class CR,VW client
class UP edge
class ORIG,META store
class TR service
class REND edge
class EV queue- The original is the only thing you can't rebuild. Renditions, counts and recommendations are derived, so they can be retried, redone and corrected.
- Processing is a task graph of chunks on a shared pool with priorities; the video goes live tier by tier.
- Views and recommendations are event pipelines off the same stream, never row updates on the watch path.
DiagramOne video's processing task graph
%% title: One video's processing task graph flowchart TD IN(["upload.complete<br/>v_7Hq2 · 12 min · 1080p"]):::client --> PROBE["Probe<br/>codec · length · GOPs"]:::service PROBE --> SPLIT["Split at closed GOPs<br/>180 chunks of ~4 s"]:::core PROBE --> CHK["Policy · copyright · thumbnails<br/>must pass before public"]:::service SPLIT --> T1["Tier 1 · high priority<br/>decode once → 144p–480p"]:::hot SPLIT --> T2["Tier 2 · normal<br/>720p · 1080p"]:::core SPLIT -.-> T3[["Tier 3 · low, by popularity<br/>4K · newer codec"]]:::queue T1 --> ASM["Assemble per rendition<br/>duration must match the original"]:::service T2 --> ASM T3 -.-> ASM ASM --> PUB(["Manifest updated tier by tier"]):::ok CHK --> PUB
Key flows#
DiagramA dropped upload resumes from the stored offset
%% title: A dropped upload resumes from the stored offset
sequenceDiagram
autonumber
participant A as Asha's app
participant U as Upload front end
participant S as Originals store
participant O as Orchestrator
A->>U: Start session · 1.8 GB · title · privacy
U-->>A: Session URI
A->>U: 8 MB chunks
U->>S: Write part, then advance the stored offset
Note over A,U: connection drops at 1.1 GB
A->>U: Status? bytes */1,800,000,000
U-->>A: 308 · Range bytes=0-1,099,999,999
A->>U: The rest from byte 1,100,000,000
alt checksum matches
U->>S: Seal the original
S-)O: upload.complete · tier 1 first
else checksum mismatch
U-->>A: 400 · upload failed, start again
endDiagramCounting a viral video's views
%% title: Counting a viral video's views
sequenceDiagram
autonumber
participant P as Player
participant K as View events
participant W as Stream worker
participant C as Sharded counter
participant Q as Quality filter
P->>K: view_start · view_id v1 · video X
K->>W: Partition by view_id, not video
alt v1 already in the dedupe window
W->>W: Drop · a player retry
else new playback
W->>W: Add 1 to X's one-second total
end
W->>C: Every second · X += 412
Note over C: above 1,000 views/s · 64 shards
W->>Q: Score · address · device · tabs · progress
Q->>C: Cleared views → verified public count
Q-->>C: Invalid later · subtractData model#
DiagramCore entities
%% title: Core entities
classDiagram
class Video {
+videoId : string
+channelId : string
+state : VideoState
+durationS : int
+originalKey : string · kept forever
+popularity : hot / warm / long-tail
}
class UploadSession {
+sessionId : string
+size : bytes
+stored : bytes · only durable parts
+parts : part number to etag
+state : string
}
class Rendition {
+tier : 1 / 2 / 3
+resolution : string
+encoder : version
+outputKey : video · chunk · tier · encoder
}
class ViewEvent {
+viewId : UUID · dedupe key
+videoId : string
+type : start / heartbeat / end
+at : timestamp
}
class ViewCount {
+videoId : string
+provisional : int64
+verified : int64 · public
+shards : 1 or 64
}
UploadSession "1" --> "1" Video : creates
Video "1" --> "many" Rendition : derived from original
Video "1" --> "many" ViewEvent : played as
ViewEvent "many" --> "1" ViewCount : aggregated into- Every rendition's key contains the encoder version, so a bad encoder's outputs are a query, and a retried task overwrites the same object.
- View streams partition by
view_id; counters shard only for videos above ~1,000 views a second. Counts are 64-bit.
Lifecycle#
DiagramA video from upload to fully processed
%% title: A video from upload to fully processed stateDiagram-v2 [*] --> UPLOADING UPLOADING --> UPLOADING: chunk stored, offset advanced UPLOADING --> EXPIRED: session idle too long UPLOADING --> PROCESSING: last byte, checksum ok UPLOADING --> FAILED: checksum mismatch PROCESSING --> BLOCKED: policy or copyright check fails PROCESSING --> LIVE_LOW: tier 1 ready LIVE_LOW --> LIVE_HD: tier 2 ready LIVE_HD --> LIVE_FULL: tier 3, once watched enough LIVE_HD --> TAKEN_DOWN: deleted or ordered down LIVE_FULL --> TAKEN_DOWN: deleted or ordered down LIVE_FULL --> [*] TAKEN_DOWN --> [*] EXPIRED --> [*] FAILED --> [*] BLOCKED --> [*] class LIVE_FULL ok class FAILED,EXPIRED,BLOCKED,TAKEN_DOWN bad class PROCESSING queue
Decisions#
| Decision | Chose | Over | Because |
|---|---|---|---|
| Upload protocol | Resumable session, offset after durable write | One long POST | phone uplinks drop; nothing stored is sent twice |
| Transcoding unit | ~4 s closed-GOP chunks, decode once, encode a tier | One job per video | minutes not hours; a crash costs one chunk |
| What gets rendered | Basic ladder now, tier 3 when watched | Every rendition for every upload | long tail is most videos; compute follows watch time |
| Publishing | Manifest lists ready tiers only | Wait for every rendition | live in low quality within minutes |
| View counting | Events by view_id, 1 s totals, sharded counters | views = views + 1 |
hot key, retries and fraud |
| Public count | Verified count, may pause or correct | Raw count | bots can't inflate it; eventually right |
| Ranking target | Expected watch time + satisfaction | Clicks | clicks reward misleading thumbnails |
When it breaks#
| Failure | What users see | Contain it with |
|---|---|---|
| Connection drops mid-upload | upload pauses, then resumes | 308 + Range from the stored offset |
| Corrupt chunk crashes workers | one rendition marked failed | dead-letter after a few retries; others publish |
| Encoder bug ships for a day | a flash in some videos | encoder version in output keys; re-encode from originals |
| Transcode pool at 85 % at peak | 4K arrives later | tier 3 waits for the trough; tier 1 never does |
| Viral video, 50,000 views / s | count keeps up | partition by view_id, 64 counter shards |
| View stream lags minutes | counts freeze, then jump | async counting; playback never waits |
Say it in the interview#
- "Keep the original, rebuild everything else." Renditions, counts and recommendations are derived data you can redo.
- The offset only moves after a durable write: that one rule makes resumable uploads correct.
- One video becomes thousands of jobs: closed-GOP chunks, decode once per tier, idempotent output keys.
- Low quality first: tier 1 at high priority puts a video live in minutes; tier 3 waits for quiet hours.
- Compute follows watch time: the long tail gets a basic ladder until it earns more.
- A view count is a pipeline: dedupe by view_id, filter low-quality playbacks, shard hot counters, publish a verified number.
- Two stages for recommendations: cheap sources narrow 20B+ videos to hundreds; the ranker scores expected watch time, not clicks.