- ✅ Usuários podem postar tweets (140-280 caracteres)
- ✅ Usuários podem seguir/unfollow outros
- ✅ Ver feed: tweets de pessoas que você segue
- ✅ Favoritar tweets
- ✅ Procurar tweets/usuários
- ✅ Ver perfil do usuário com seus tweets
- ✅ Notificações em tempo real (novo tweet de quem você segue)
- Escala: 500M usuários, 100M ativos/dia
- Tweet Rate: 300K tweets/segundo (TPS)
- Feed Reading: 5M requisições/segundo
- Latência: Feed < 200ms, Tweet < 100ms
- Uptime: 99.99%
- Consistência: Eventual (feed pode ser levemente desatualizado)
Daily Active Users: 100M
Tweets/dia: 300K TPS × 86400s = 25.9B tweets/dia
Favoritas/dia: ~5B (assumindo 20% dos tweets)
Reads/dia: ~5T (feed reads)
Storage (1 tweet = 250 bytes):
- 25.9B tweets/dia × 250 bytes = 6.5 TB/dia
- Por ano: ~2.4 PB
- 5 anos: ~12 PB (com replicas: 36 PB)
Bandwidth:
- Write: 300K TPS × 250 bytes = 75 MB/s
- Read: 5M TPS × 1KB = 5 GB/s (feed pode ser cached)
Cache (hot tweets):
- Top 10% tweets: 2.6B tweets × 250 bytes = 650 GB
// POST: Criar tweet
POST /tweets
{
"content": "Hello Twitter!",
"media_urls": ["..."], // opcional
"reply_to_id": 123 // opcional, é reply
}
Response: {
"id": 1,
"user_id": 123,
"content": "Hello Twitter!",
"created_at": "2026-08-26T...",
"likes": 0,
"replies": 0
}
// GET: Obter tweet
GET /tweets/:tweet_id
Response: { tweet object }
// GET: Feed pessoal (tweets de quem você segue)
GET /feed
Query params: ?limit=20&offset=0
Response: { tweets: [{...}, {...}] }
// POST: Favoritar tweet
POST /tweets/:tweet_id/like
Response: { liked: true, likes_count: 1005 }
// DELETE: Remover favorita
DELETE /tweets/:tweet_id/like
Response: { liked: false, likes_count: 1004 }
// POST: Seguir usuário
POST /users/:user_id/follow
Response: { following: true }
// DELETE: Unfollow
DELETE /users/:user_id/follow
Response: { following: false }
// GET: Perfil de usuário
GET /users/:user_id
Response: {
"id": 123,
"username": "john",
"name": "John Doe",
"bio": "...",
"followers": 1000,
"following": 500,
"tweets": 1500
}
// GET: Timeline do usuário
GET /users/:user_id/tweets
Response: { tweets: [{...}] }
// GET: Notificações em tempo real
WebSocket /ws/notifications
// Recebe: { type: "new_tweet", user: {...}, tweet: {...} }
4️⃣ HIGH-LEVEL DESIGN
┌─────────────────────────────────────────────────────────────────┐
│ CLIENTS │
│ (Web, Mobile, Desktop) │
└──────────────────────────┬──────────────────────────────────────┘
│
┌──────▼──────┐
│Load Balancer│ (Nginx, HAProxy)
└──────┬──────┘
│
┌────────────────┼────────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ API │ │ API │ │ API │
│ Server 1 │ │ Server 2 │ │ Server N │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
└──────────────┼──────────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼─────┐
│Redis │ │Kafka │ │ElasticS. │
│Cache │ │Message │ │Search │
│(Feed) │ │Queue │ │(Tweets) │
└────┬────┘ └────┬────┘ └────┬─────┘
│ │ │
└────────────────┼──────────────┘
│
┌────────────────┼────────────────────┐
│ │ │
┌────▼──────┐ ┌────▼──────┐ ┌────▼──────┐
│MySQL Shard│ │MySQL Shard│ │MySQL Shard│
│ 0 │ │ 1 │ │ N │
└───────────┘ └───────────┘ └───────────┘
│ │ │
┌────▼──────┐ ┌────▼──────┐ ┌────▼──────┐
│Replica 0a │ │Replica 1a │ │Replica Na │
│Replica 0b │ │Replica 1b │ │Replica Nb │
└───────────┘ └───────────┘ └───────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Supporting Services: │
│ - Notification Service (consome Kafka) │
│ - Analytics Service │
│ - User Recommendation Engine │
└─────────────────────────────────────────────────────────────────┘
-- Users Table
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255),
bio TEXT,
profile_picture_url VARCHAR(2048),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP,
INDEX(username),
INDEX(created_at)
);
-- Tweets Table (SHARDED by user_id)
CREATE TABLE tweets (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
content VARCHAR(280) NOT NULL,
reply_to_id BIGINT, -- se for reply
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
likes_count INT DEFAULT 0,
replies_count INT DEFAULT 0,
retweets_count INT DEFAULT 0,
is_deleted BOOLEAN DEFAULT FALSE,
INDEX(user_id, created_at),
INDEX(created_at),
FOREIGN KEY(user_id) REFERENCES users(id)
);
-- Follows Table (SHARDED by follower_id)
CREATE TABLE follows (
follower_id BIGINT NOT NULL,
following_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(follower_id, following_id),
INDEX(following_id), -- para queries "who follows me"
FOREIGN KEY(follower_id) REFERENCES users(id),
FOREIGN KEY(following_id) REFERENCES users(id)
);
-- Likes/Favorites Table
CREATE TABLE likes (
user_id BIGINT NOT NULL,
tweet_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(user_id, tweet_id),
INDEX(tweet_id), -- para contar likes
FOREIGN KEY(user_id) REFERENCES users(id),
FOREIGN KEY(tweet_id) REFERENCES tweets(id)
);
-- Feed Cache (desnormalizado, armazenado em banco)
CREATE TABLE feed_cache (
user_id BIGINT NOT NULL,
tweet_id BIGINT NOT NULL,
tweet_user_id BIGINT NOT NULL,
created_at TIMESTAMP,
PRIMARY KEY(user_id, tweet_id),
INDEX(user_id, created_at DESC),
FOREIGN KEY(user_id) REFERENCES users(id)
);
Component 1: Feed Generation (mais crítico!)
O problema: Quando você abre Twitter, precisa ver tweets de 500 pessoas que segue. RÁPIDO.
Abordagem 1: Pull Model ❌
GET /feed
1. Busca lista de people_you_follow (IDs)
2. Para cada pessoa: SELECT TOP 5 tweets
3. Mescla e ordena por data
4. Retorna
❌ Problema: 500 people × 5 tweets = 500 queries!
Latência: 500-1000ms (muito lenta)
Abordagem 2: Push Model ✅
Quando usuário A posta tweet:
1. Kafka publica: { event: "new_tweet", user_id: A, tweet_id: 123 }
2. Notification Service consome
3. Para cada follower de A (ex: 1M followers):
- Adiciona tweet ao feed_cache do follower
GET /feed
1. SELECT tweets FROM feed_cache WHERE user_id = ? LIMIT 20
2. Redis cache (TTL 1 hora)
3. Retorna em <100ms
✅ Vantagem: Feed pré-computado, super rápido
Problema: Celebridades
Quando Elon Musk (100M followers) posta:
- 100M feeds precisam ser atualizados
- Impossível em real-time!
Solução: Hybrid Approach
- Seguidores "importantes" (high-traffic): push
- Seguidores "normais": lazy load (pull quando abre)
Timeline query (para feeds que não foram pre-computed):
SELECT t.*
FROM tweets t
WHERE t.user_id IN (SELECT following_id FROM follows WHERE follower_id = ?)
AND t.created_at > (NOW() - INTERVAL 24 HOUR)
ORDER BY t.created_at DESC
LIMIT 20
---
Component 2: Sharding Strategy
Shard by user_id (mais comum):
Shard 0: user_id % 4 = 0
- Contém: tweets, follows, likes de users 0, 4, 8, 12...
Shard 1: user_id % 4 = 1
- Contém: tweets, follows, likes de users 1, 5, 9, 13...
Shard 2: user_id % 4 = 2
- Contém: tweets, follows, likes de users 2, 6, 10, 14...
Shard 3: user_id % 4 = 3
- Contém: tweets, follows, likes de users 3, 7, 11, 15...
Função:
function getShardId(userId) {
return userId % 4
}
Vantagem:
- Dados de um usuário ficam no mesmo shard
- Queries por usuário são eficientes
Desafio: Feed queries atravessam múltiplos shards
- Solução: Distributed join (coordenador faz query em N shards)
---
Component 3: Caching Strategy
// 1. Redis Cache para feeds
// TTL: 1 hora
// Pattern: feed:{userId} → list de {tweet_id, timestamp, user_id}
redis.get('feed:123') // rápido!
// 2. Redis Cache para tweets populares
// TTL: 24 horas
// Tweets com > 10K likes
redis.get('tweet:999') // tweet popular
// 3. Redis Cache para usuários
// TTL: 1 dia
// Info de perfil (nome, bio, followers count)
redis.get('user:123') // perfil
// Invalidação:
// Quando usuário A segue B:
// - Invalidar feed cache de A
// - Quando A postar → invalidar feeds de seus followers
// Implementação:
async function createTweet(userId, content) {
const tweet = await db.insertTweet(userId, content)
// Publica evento
await kafka.publish('tweets', {
event: 'new_tweet',
tweet_id: tweet.id,
user_id: userId,
created_at: tweet.created_at
})
// Limpa cache (opcional, se usar cache de tweets do usuário)
await redis.invalidate(`user_tweets:${userId}`)
return tweet
}
---
Component 4: Real-time Notifications
// WebSocket para notificações
// Quando alguém que você segue posta → você é notificado
app.ws('/notifications', (ws, req) => {
const userId = req.user.id
// Subscribe ao canal Kafka
kafka.subscribe(`notifications:${userId}`, (message) => {
// New tweet do alguém que você segue
ws.send(JSON.stringify({
type: 'new_tweet',
tweet: message
}))
})
// Se favorita seu tweet
// Se comenta seu tweet
// Se te segue
})
// Notification Service (consome Kafka):
kafka.on('new_tweet', (event) => {
const { user_id, tweet_id } = event
// Busca followers deste usuário
const followers = await db.getFollowers(user_id)
followers.forEach(follower => {
// Publica para cada follower
kafka.publish(`notifications:${follower.id}`, {
type: 'new_tweet',
from: user_id,
tweet_id
})
})
})
---
🔒 7️⃣ SECURITY CONSIDERATIONS
A. Rate Limiting
// Por usuário
- Max 300 tweets/dia
- Max 100 follows/dia
- Max 500 likes/dia
// Por IP
- Max 100 requests/min
Implementação: Redis + Sliding Window
B. Input Validation
// Tweet content
- Max 280 caracteres
- Não permitir scripts maliciosos
- Sanitize HTML/SQL
// Usernames
- 4-30 caracteres alphanuméricas + underscore
- Único
C. Abuse Detection
// Detectar spam
- Mesma mensagem postada 5+ vezes → flag
- Tweets muito similares → flag
// Detectar bots
- Muitos follows/dia sem interação → flag
- Padrão de likes suspeito → flag
// Ação: Soft-ban, hard-ban, etc
D. Data Privacy
// Tweets podem ser privados (private account)
// Mensagens diretas (DMs) criptografadas
// GDPR: direito de deletar dados
---
🛡️ 8️⃣ FAILURE HANDLING
A. Redis Cache Failure
async function getFeed(userId) {
try {
// Tenta cache
const feed = await redis.get(`feed:${userId}`)
if (feed) return feed
} catch (error) {
console.log('Redis down, falling back to DB')
}
// Fallback: busca direto
const tweets = await db.getFeedTweets(userId)
return tweets
}
B. Database Failure (Replication)
Master (Writes):
shard-0-master.db
Replicas (Reads):
shard-0-replica-1
shard-0-replica-2
shard-0-replica-3
Se Master falha:
- Promove Replica-1 a Master automaticamente
- Redireciona writes para novo Master
- Clientes não veem downtime
Se Replica falha:
- Redireciona reads para outra Replica
C. Circuit Breaker
// Se DB está lento (queries > 5s)
// Abre circuito → retorna erro ao invés de travar
if (queryTime > 5000) {
circuitBreaker.open()
return { error: 'Service temporarily unavailable' }
}
D. Message Queue (Kafka) Durability
Quando novo tweet é publicado:
1. Escrito em Kafka (replicado em 3 brokers)
2. ACK enviado ao cliente
3. Notification Service consome no seu tempo
4. Se Notification Service falha → Kafka retém mensagem
5. Quando service se recupera → retoma processing
E. Health Checks
GET /health
{
"status": "healthy",
"db": "UP",
"redis": "UP",
"kafka": "UP",
"timestamp": "..."
}
Monitoramento contínuo → Alertas
---
📊 9️⃣ SHARDING & PARTITIONING
A. Estratégia Principal
Shard by user_id (MOD 4):
Shard 0 (user_id % 4 = 0):
- host: shard-0-master, shard-0-replica-1, shard-0-replica-2
- data: tweets, follows, likes de user 0, 4, 8, 12, ...
Shard 1 (user_id % 4 = 1):
- host: shard-1-master, shard-1-replica-1, shard-1-replica-2
- data: tweets, follows, likes de user 1, 5, 9, 13, ...
... (Shard 2, 3)
B. Queries por Shard
// Query simple (dados de 1 usuário) - 1 shard:
GET /users/:user_id/tweets
→ Shard = user_id % 4
→ SELECT tweets FROM tweets WHERE user_id = ?
→ Rápido!
// Query complexa (feed do usuário) - N shards:
GET /feed
→ Precisa dados de 500 pessoas que você segue
→ Cada uma pode estar em shard diferente
→ Solução:
1. Para cada person_id em follows:
- Determina shard
- Faz query paralela em N shards
2. Mescla resultados
3. Ordena por data
Implementação: Async/Await paralela
async function getFeed(userId) {
// Busca lista de people você segue (qualquer shard)
const follows = await db.getFollows(userId)
// Query paralela em múltiplos shards
const queries = follows.map(person =>
getShardConnection(person.following_id).query(
'SELECT * FROM tweets WHERE user_id = ? ORDER BY created_at DESC LIMIT 5',
[person.following_id]
)
)
const allTweets = await Promise.all(queries)
return allTweets.flat().sort((a, b) => b.created_at - a.created_at).slice(0, 20)
}
C. Hot Spot Problem
Se um usuário (Elon Musk) tem 100M followers:
- Seu shard recebe 100M feed queries por hora
- Seus dados ficam super quentes
- Replica lê não consegue acompanhar
Solução 1: Dedicated Replicas
- Musk: 10 read replicas ao invés de 3
- Distribui carga
Solução 2: Cache Agressivo
- Cache tweets de celebridades por mais tempo
- CDN global para distribuir reads
Solução 3: Separate Hot User Shard
- Usuários com >10M followers → shard especial
- Com mais replicas e cache
D. Rebalancing (adicionar novo shard)
Cenário: Passar de 4 shards para 5
Antes:
- user_id % 4 = shard
Depois:
- user_id % 5 = shard
Estratégia:
1. Criar Shard 4 novo
2. Execução gradual (em horas/dias):
- Copiar dados relevantes para Shard 4
- Sincronizar escritas (dual-write por um tempo)
3. Mudar função hash para % 5
4. Verificar consistency
5. Deletar dados antigos de shards 0-3 se necessário
Durante o processo: Sem downtime (dual-write garante)