- Published on
Co nieco o System Designie - projektujemy komunikator (część 1)
- Authors

- Name
- Piotr Kołodziejczyk

System Design to jedna z tych dziedzin, gdzie liczy się nie tylko znajomość technologii, ale też zdolność myślenia o skali. Jak to jest, że WhatsApp obsługuje 100 miliardów wiadomości dziennie przy zespole liczącym zaledwie kilkadziesiąt inżynierów? Zacznijmy od podstaw - wymagań i obliczeń.
Wymagania funkcjonalne
Zanim cokolwiek narysujemy, ustalamy co system ma robić. Dla komunikatora P2P:
- Wysyłanie i odbieranie wiadomości tekstowych między dwoma użytkownikami
- Wiadomości powinny docierać w czasie rzeczywistym (sub-sekundowym)
- Obsługa multimediów: zdjęcia, filmy, dokumenty
- Statusy dostarczenia: wysłano, dostarczono, odczytano (✓, ✓✓, niebieskie ✓✓)
- Powiadomienia push gdy użytkownik jest offline
- Historia wiadomości - możliwość załadowania poprzednich konwersacji
- Wskaźnik "pisze..." (typing indicator)
Co nie wchodzi w zakres tej wersji: grupy, połączenia głosowe/wideo, enkrypcja end-to-end (to osobne, złożone tematy).
Wymagania niefunkcjonalne
Tu zaczyna się prawdziwy system design. Wymagania niefunkcjonalne definiują jak system ma działać, nie co ma robić:
- Dostępność: 99.99% uptime - maksymalnie ~52 minuty przestoju rocznie
- Latencja: wiadomość P2P powinna dotrzeć poniżej 200ms (P99) gdy obie strony są online
- Trwałość danych: wiadomości nie mogą znikać - zero utraty danych
- Spójność: użytkownik może zobaczyć wiadomości w nieco innej kolejności na różnych urządzeniach (eventual consistency), ale nie może stracić żadnej
- Skalowalność: system musi działać przy 10x wzroście bez przeprojektowania
Szacowanie skali - back-of-the-envelope
To serce pierwszego etapu system designu. Bez liczb nie wiadomo jakich komponentów potrzeba i ile ich potrzeba.
Założenia
Użytkownicy zarejestrowani: 500 milionów
DAU (Daily Active Users): 100 milionów
Wiadomości/użytkownik/dzień: 40
Średni rozmiar wiadomości: 100 bajtów (tekst)
Odsetek wiadomości z mediami: 20%
Średni rozmiar zdjęcia: 300 KB (po kompresji)
Przepustowość wiadomości (QPS)
Wiadomości/dzień = 100M użytkowników × 40 wiadomości = 4 miliardy wiadomości/dzień
Średnie QPS = 4 000 000 000 / 86 400 s ≈ 46 300 wiadomości/sekundę
Peak QPS (zakładamy 2× średniej) ≈ 92 600 wiadomości/sekundę
To jest liczba, która podbija wszystkie nasze decyzje architektoniczne. Żaden klasyczny monolityczny serwer tego nie obsłuży.
Storage - wiadomości tekstowe
Wiadomości/dzień: 4 000 000 000
Rozmiar wiadomości: ~100 bajtów
+ metadata (ID, timestamp, sender, receiver, status): ~50 bajtów
Łącznie na wiadomość: ~150 bajtów
Dane/dzień = 4B × 150 B = 600 GB/dzień
Retencja 5 lat = 600 GB × 365 × 5 ≈ 1 095 TB ≈ 1.1 PB
Storage - media (zdjęcia)
Użytkownicy wysyłający media/dzień: 100M × 20% = 20 milionów
Średni rozmiar zdjęcia: 300 KB
Dane mediów/dzień = 20M × 300 KB = 6 TB/dzień
Retencja 5 lat = 6 TB × 365 × 5 ≈ 10.95 PB ≈ 11 PB
Bandwidth
Ruch przychodzący (upload):
- Tekst: 600 GB / 86 400 s ≈ 56 Mb/s
- Media: 6 TB / 86 400 s ≈ 556 Mb/s
Łącznie: ~612 Mb/s
Ruch wychodzący (download):
- Każdą wiadomość czyta co najmniej 1 odbiorca
- Podobna skala co upload, ~612 Mb/s baseline
- Peak (pobieranie historii, CDN) ~3-5 Gb/s
Podsumowanie szacunków
| Metryka | Wartość |
|---|---|
| DAU | 100M |
| QPS (średnie) | ~46 300 msg/s |
| QPS (peak) | ~92 600 msg/s |
| Storage wiadomości/rok | ~219 TB |
| Storage mediów/rok | ~2.2 PB |
| Bandwidth łącznie | ~600 Mb/s - 5 Gb/s |
Wysokopoziomowe komponenty - przegląd AWS
Mając liczby, możemy dobierać technologie. Celujemy w AWS jako provider.
Warstwa połączeń klientów
Amazon API Gateway (WebSocket) albo Application Load Balancer (ALB) z obsługą WebSocket - to punkt wejścia dla każdego klienta mobilnego i webowego. WebSocket jest kluczowy dla real-time: utrzymuje trwałe połączenie TCP zamiast odpytywać co N sekund.
Serwery czatu
Amazon ECS (Fargate) - kontenery z logiką serwera czatu. Każdy kontener obsługuje dziesiątki tysięcy połączeń WebSocket jednocześnie. Przy ~92 600 QPS i zakładając 10 000 aktywnych połączeń na instancję potrzebujemy około 10 instancji przy peaku (z redundancją - 20+).
Alternatywa: EC2 z Auto Scaling Group dla większej kontroli nad parametrami sieci.
Warstwa przechowywania wiadomości
Amazon DynamoDB - baza NoSQL z rekordowo niską latencją. Dla wiadomości czatu DynamoDB to naturalne rozwiązanie:
- Skaluje do milionów TPS bez konfiguracji
- Single-digit millisecond latency
- Automatyczne partycjonowanie
- Point-in-time recovery (wymaganie trwałości danych)
Klucz partycji: conversation_id, klucz sortowania: message_timestamp (lub UUID z komponentem czasowym jak ULID). Przy takim schemacie załadowanie historii konkretnej rozmowy to jeden, wydajny zapytanie zakresu.
Warstwa cache
Amazon ElastiCache (Redis) - odpowiada za:
- Mapowanie
user_id → adres serwera WebSocket(kto gdzie jest podłączony) - Statusy online/offline użytkowników
- Sesje i tokeny autoryzacji
- Rate limiting (ochrona przed spamem)
- Typing indicators (wygasają po 5s - idealny use case dla TTL w Redis)
Przechowywanie mediów
Amazon S3 - obiektowy storage dla wszystkich mediów. 11 PB na 5 lat to nic dla S3. Klienci uploadują bezpośrednio przez presigned URL (bez przechodzenia przez serwery czatu), co drastycznie odciąża backend.
Amazon CloudFront - CDN przed S3. Zdjęcia wysyłane wielokrotnie (forward, share) są serwowane z edge location najbliższej użytkownika, nie z S3 za każdym razem.
Powiadomienia push
Amazon SNS jako router powiadomień:
- SNS → FCM (Firebase Cloud Messaging) dla Android
- SNS → APNs (Apple Push Notification service) dla iOS
- SNS → Web Push dla przeglądarek
Gdy odbiorca jest offline, serwer czatu zamiast próbować wysłać przez WebSocket (który nie istnieje) wrzuca zadanie powiadomienia do SNS.
Kolejkowanie asynchroniczne
Amazon SQS dla operacji które nie muszą być synchroniczne:
- Zmiana rozmiaru i kompresja zdjęć po uploadzie
- Wysyłka powiadomień push (bufor przed SNS)
- Generowanie miniatur wideo
- Archiwizacja starych wiadomości do S3 (cold storage)
Ogólny zarys architektury
┌─────────────────────────────────────────────┐
│ AWS Cloud │
│ │
Klienci │ ┌─────┐ ┌──────────────────────────┐ │
(mobile/web) ─────┼─▶│ ALB │────▶│ Chat Servers (ECS) │ │
│ │ WSS │ │ [A] [B] [C] [D] ... │ │
│ └─────┘ └──────┬───────────┬────────┘ │
│ │ │ │
│ ┌──────▼──┐ ┌────▼──────────┐ │
│ │ Redis │ │ DynamoDB │ │
│ │(ElastiC)│ │ (wiadomości) │ │
│ └─────────┘ └───────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ SQS + SNS │────▶ FCM/APNs │
│ └─────────────┘ │
│ │
│ S3 ◀── presigned URL ── klient │
│ CloudFront ──▶ klient (pobieranie mediów) │
└─────────────────────────────────────────────┘
Co dalej?
Mamy wymagania, mamy liczby, mamy listę komponentów. Ale jak one ze sobą rozmawiają? Jak wiadomość od Ani trafia do Bartka, jeśli obaj są podłączeni do różnych serwerów czatu? Co gdy Bartek jest offline? Jak upload zdjęcia wygląda krok po kroku?
Tego wszystkiego - razem z konkretnymi przepływami danych, schematem DynamoDB i tym jak Redis trzyma mapowanie połączeń - dotyczy część druga.