Orivel Orivel
Abrir menu

Design de Sistema: Serviço de Notificações em Tempo Real

Compare as respostas dos modelos para esta tarefa de benchmark em Design de sistemas e reveja pontuações, comentários e exemplos relacionados.

Entre ou cadastre-se para usar curtidas e favoritos. Cadastrar

X f L

Índice

Visão geral da tarefa

Gêneros de comparação

Design de sistemas

Modelo criador da tarefa

Modelos participantes

Modelos avaliadores

Enunciado da tarefa

Você é um engenheiro de software sênior encarregado de projetar um sistema de notificações em tempo real para uma grande plataforma de mídia social.

Requisitos do Sistema:

  1. Funcionalidade: O sistema deve entregar notificações para várias interações dos usuários, incluindo novos seguidores, curtidas em publicações, comentários e mensagens diretas.
  2. Entrega em Tempo Real: As notificações devem ser entregues aos usuários online com latência muito baixa (inferior a 2 segundos).
  3. **Suporte Multipla...
Mostrar mais

Você é um engenheiro de software sênior encarregado de projetar um sistema de notificações em tempo real para uma grande plataforma de mídia social.

Requisitos do Sistema:

  1. Funcionalidade: O sistema deve entregar notificações para várias interações dos usuários, incluindo novos seguidores, curtidas em publicações, comentários e mensagens diretas.
  2. Entrega em Tempo Real: As notificações devem ser entregues aos usuários online com latência muito baixa (inferior a 2 segundos).
  3. Suporte Multiplataforma: O sistema deve suportar o envio de notificações via push móvel (iOS/Android), notificações em navegador web e e-mail.
  4. Histórico de Notificações: Os usuários devem poder visualizar um histórico de suas notificações recentes (por exemplo, as últimas 100).
  5. Escalabilidade: O sistema deve lidar com 100 milhões de usuários ativos diários (DAU), com cada usuário gerando em média 10 eventos por dia que disparam notificações. Deve também aguentar picos de carga de 5x o tráfego médio.
  6. Confiabilidade: O sistema deve ser altamente disponível (99,9% de tempo de atividade) e resiliente a falhas.

Sua Tarefa:
Forneça um plano detalhado de design do sistema. Seu plano deve abordar os seguintes aspectos:

  • Uma visão arquitetural de alto nível.
  • Componentes-chave e suas responsabilidades (por exemplo, API Gateway, Notification Service, Fan-out Service, etc.).
  • Modelo de dados e escolha dos bancos de dados (por exemplo, para armazenar preferências de usuário, histórico de notificações). Justifique suas escolhas.
  • Recomendações de stack tecnológico (por exemplo, filas de mensagens, camadas de cache, serviços de push de notificações).
  • Estratégias para garantir escalabilidade, baixa latência e alta disponibilidade.
  • Uma discussão sobre gargalos potenciais e os compromissos feitos em seu design.

Informação complementar

Nenhum contexto externo é necessário para esta tarefa.

Política de avaliação

Uma resposta de alta qualidade apresentará um design de sistema claro, coerente e tecnicamente sólido. A avaliação deve se concentrar nos seguintes critérios:

  1. Solidez Arquitetural: A arquitetura proposta deve ser lógica e bem adaptada para um sistema de notificações em tempo real em larga escala. As funções dos componentes-chave devem estar claramente definidas.
  2. Justificativa de Tecnologia: A escolha das tecnologias (bancos de dados, filas de mensagens, caches, etc.) deve ser bem justificada, explic...
Mostrar mais

Uma resposta de alta qualidade apresentará um design de sistema claro, coerente e tecnicamente sólido. A avaliação deve se concentrar nos seguintes critérios:

  1. Solidez Arquitetural: A arquitetura proposta deve ser lógica e bem adaptada para um sistema de notificações em tempo real em larga escala. As funções dos componentes-chave devem estar claramente definidas.
  2. Justificativa de Tecnologia: A escolha das tecnologias (bancos de dados, filas de mensagens, caches, etc.) deve ser bem justificada, explicando por que elas são adequadas para seu papel específico no sistema.
  3. Escalabilidade e Confiabilidade: O design deve incluir estratégias concretas para lidar com a carga especificada (100M DAU, picos de 5x) e atender ao requisito de alta disponibilidade. Isso inclui discutir conceitos como escalonamento horizontal, balanceamento de carga, particionamento de dados e redundância.
  4. Discussão de Compromissos: Uma resposta superior não apenas apresentará uma solução, mas também discutirá os compromissos envolvidos. Por exemplo, pode discutir os trade-offs entre diferentes tipos de banco de dados (SQL vs. NoSQL) ou modelos de consistência.
  5. Completude: A resposta deve abordar todas as partes do enunciado, incluindo o modelo de dados, suporte multiplataforma e gargalos potenciais.

Respostas

SERVIÇO DE NOTIFICAÇÃO EM TEMPO REAL — PLANO DE DESIGN DO SISTEMA

  1. RECAPITULAÇÃO DOS REQUISITOS E ESTIMATIVA DE CAPACIDADE

Matemática do tráfego:

  • 100M DAU x 10 eventos/dia = 1B eventos de gatilho de notificação por dia.
  • Média = 1B / 86.400 ≈ 11.600 eventos/segundo. Pico em 5x ≈ 58.000 eventos/segundo.
  • Multiplicador de fan-out: a maioria dos eventos (curtir, comentar, seguir, DM) tem como alvo exatamente um destinatário, então o fan-out é de aproximadamente 1:1. No entanto, eventos de contas com muitos seguidores (uma celebridade postando) podem ter fan-out para milhões de destinatários; trate esses como...
Mostrar resposta completa

SERVIÇO DE NOTIFICAÇÃO EM TEMPO REAL — PLANO DE DESIGN DO SISTEMA

  1. RECAPITULAÇÃO DOS REQUISITOS E ESTIMATIVA DE CAPACIDADE

Matemática do tráfego:

  • 100M DAU x 10 eventos/dia = 1B eventos de gatilho de notificação por dia.
  • Média = 1B / 86.400 ≈ 11.600 eventos/segundo. Pico em 5x ≈ 58.000 eventos/segundo.
  • Multiplicador de fan-out: a maioria dos eventos (curtir, comentar, seguir, DM) tem como alvo exatamente um destinatário, então o fan-out é de aproximadamente 1:1. No entanto, eventos de contas com muitos seguidores (uma celebridade postando) podem ter fan-out para milhões de destinatários; trate esses como um caminho "fan-out em massa" separado. Assuma entrega de pico efetiva de 80.000–100.000 notificações/segundo, incluindo campanhas em massa.
  • Armazenamento: histórico das últimas 100 notificações por usuário. Assuma ~400 bytes por linha -> 100 x 400B = 40KB/usuário -> 100M usuários ≈ 4TB de histórico quente, mais fator de replicação 3 ≈ 12TB. Facilmente particionável.
  • Conexões: assuma 20–25% do DAU online simultaneamente no pico ≈ 20–25M de conexões WebSocket ativas. Com ~100K conexões por nó de gateway (Linux ajustado, epoll), são 200–250 nós mais margem.

Metas não funcionais:

  • Latência p99 de ponta a ponta inferior a 2s para usuários online (medida desde a ingestão do evento até o ack do cliente).
  • 99,9% de disponibilidade = ~43 minutos de inatividade por mês.
  • Entrega pelo menos uma vez com desduplicação idempotente no lado do cliente/servidor.
  1. ARQUITETURA DE ALTO NÍVEL

O design é um pipeline orientado a eventos com uma divisão clara entre o caminho de ingestão (rápido, durável, otimizado para gravação) e o caminho de entrega (específico do canal, com tentativas).

Fluxo:
Serviços produtores (Serviço de Postagem, Serviço de Grafo Social, Serviço de Comentários, Serviço de Mensagens)
-> API/Gateway de Notificação (gRPC + REST, autenticação, limitação de taxa, validação de esquema, chave de idempotência)
-> Tópico Kafka notification.events (particionado por recipient_user_id quando conhecido, caso contrário por actor_id)
-> Serviço de Fan-out (resolve destinatários, expande eventos de celebridades, aplica regras de agregação)
-> Tópico Kafka notification.deliveries (uma mensagem por destinatário)
-> Serviço de Preferências e Políticas (consulta in-line via cache: opt-ins de canal, horários de silêncio, mudo/bloqueio, resumo vs. instantâneo)
-> Roteador / Despachante grava em tópicos por canal:
deliver.websocket, deliver.mobile_push, deliver.web_push, deliver.email
-> Trabalhadores de Canal:
Despachante WebSocket -> Consulta ao Registro de Conexões -> Nó do Gateway em Tempo Real -> cliente
Trabalhador de Push Móvel -> APNs (iOS) / FCM (Android)
Trabalhador de Push Web -> protocolo VAPID/Web Push via serviços de push do navegador
Trabalhador de E-mail -> SES / SendGrid, com renderização de modelo e agrupamento de resumos
-> Em paralelo, um consumidor de Histórico de Gravação persiste cada entrega no armazenamento de Histórico de Notificações e incrementa contadores de não lidos.

Transversal: Serviço de Modelos, Registro de Dispositivos, Registro de Conexões, Armazenamento de Desduplicação, Filas de Mensagens Mortas e um plano de Administração/Análise consumindo os mesmos fluxos Kafka.

  1. COMPONENTES CHAVE E RESPONSABILIDADES

Gateway de API de Notificação

  • Ponto de entrada único para produtores internos (gRPC, esquemas protobuf em um registro) e para APIs de leitura do cliente (REST/GraphQL para histórico, marcar como lido, preferências).
  • Responsabilidades: autenticação (mTLS serviço a serviço, JWT para clientes), limitação de taxa/quotas por locatário e por produtor, validação de solicitação, aceitação de chave de idempotência e anexo durável imediato ao Kafka. Retorna 202 Accepted rapidamente; não realiza trabalho de entrega in-line.

Ingestão de Eventos / Kafka

  • Log durável e reproduzível. notification.events com ~256 partições, fator de replicação 3, min.insync.replicas=2, acks=all, retenção de 7 dias para recuperação de reprodução e incidentes.
  • Fornece back-pressure e buffer natural para o pico de 5x, desacoplando produtores de provedores terceirizados lentos.

Serviço de Fan-out

  • Resolve um evento em uma lista de destinatários. Para eventos 1:1, é um pass-through. Para eventos 1:N (nova postagem para seguidores, atividade de thread de grupo), ele consulta o Serviço de Grafo Social e emite mensagens de destinatário em lotes.
  • Estratégia de fan-out híbrida: baseada em push (gravação por destinatário) para contas normais; baseada em pull/preguiçosa para contas acima de um limite de seguidores (por exemplo, 1M+), onde um único "marcador de feed" é gravado e os destinatários se materializam na leitura. Isso evita que um evento de celebridade crie uma tempestade de gravação de 50M mensagens no caminho quente.
  • Limita a taxa de expansões em massa para um tópico Kafka separado de baixa prioridade para que nunca sufoquem as notificações interativas.
  • Aplica agregação/coalescência: "Alice e outras 24 pessoas curtiram sua postagem" em vez de 25 linhas, usando uma janela deslizante curta (por exemplo, 30–60s) com chave (destinatário, post_id, tipo) no Redis.

Serviço de Preferências e Políticas

  • Armazena e serve preferências de canal por usuário, opt-ins em nível de categoria, horários de silêncio com fuso horário, configurações de nível de dispositivo, listas de mudo/bloqueio e sinalizadores de consentimento legal (GDPR/CAN-SPAM).
  • O caminho de leitura é armazenado em cache no Redis com invalidação write-through; meta de consulta p99 inferior a 5ms. Fonte da verdade em um armazenamento relacional.
  • Aplica limitação de frequência (por exemplo, no máximo N pushes/hora por usuário) para proteger a experiência do usuário e as quotas do provedor.

Roteador / Despachante

  • Mapeia uma entrega aprovada em itens de trabalho específicos do canal. Decide "apenas no aplicativo + WebSocket se online; caso contrário, push móvel" usando o Registro de Conexões e agenda resumos de e-mail em vez de envios instantâneos quando as preferências indicam isso.

Gateway em Tempo Real (camada WebSocket)

  • Camada com estado mantendo conexões WebSocket persistentes (fallback: Server-Sent Events, depois long polling).
  • Na conexão: autenticar, registrar (user_id, device_id) -> gateway_node_id no Registro de Conexões (Cluster Redis, TTL + atualização de heartbeat), em seguida, enviar qualquer backlog não lido.
  • Na entrega: o despachante consulta o nó, envia via um fluxo gRPC interno ou um canal Kafka/Redis Pub/Sub por nó; o gateway grava no socket e aguarda um ack do cliente.
  • Heartbeats a cada 30s; heartbeats perdidos removem a entrada do registro e futuras notificações caem automaticamente para o push móvel.

Trabalhadores de Canal

  • Trabalhador de Push Móvel: agrupa para APNs (HTTP/2 multiplexado, autenticação baseada em token) e FCM. Lida com limites de concorrência por provedor, backoff exponencial com jitter e remove tokens de dispositivo inválidos/não registrados do Registro de Dispositivos.
  • Trabalhador de Push Web: protocolo Web Push com chaves VAPID e criptografia de payload.
  • Trabalhador de E-mail: renderiza modelos, suporta resumos instantâneos, horários e diários; lida com bounces/reclamações via webhooks do provedor e mantém uma lista de supressão.
  • Todos os trabalhadores são consumidores idempotentes com chave por notification_id e gravam o status terminal de volta em um tópico delivery.status.

Histórico de Gravação e Caminho de Leitura

  • Consumidor que persiste notificações e mantém contadores de não lidos no Redis (contador autoritativo periodicamente reconciliado do armazenamento).
  • A API de leitura atende às últimas 100 notificações do armazenamento de histórico, com um cache Redis da primeira página por usuário para leituras típicas de sub-10ms.

Serviços de Suporte

  • Serviço de Modelos: modelos localizados e versionados com interpolação de variáveis; desacopla alterações de cópia de implantações de código.
  • Registro de Dispositivos: token_dispositivo, plataforma, versao_app, local, fuso_horario, ultimo_visto.
  • Armazenamento de Desduplicação: Redis com TTL de 24h em (producer_id, idempotency_key) para fazer com que pelo menos uma vez se comporte como efetivamente uma vez.
  • Agendador: para notificações atrasadas/agendadas e janelas de resumo (filas agrupadas por tempo em conjuntos ordenados do Redis ou um agendador dedicado como uma escada de tópicos de atraso do Kafka).
  • DLQ + Replayer: mensagens venenosas estacionadas e reproduzíveis após correções.
  1. MODELO DE DADOS E ESCOLHAS DE BANCO DE DADOS

a) Histórico de Notificações — Cassandra (ou ScyllaDB / DynamoDB)
Tabela: notifications_by_user

  • Chave de partição: user_id
  • Chave de clusterização: created_at DESC, notification_id
  • Colunas: type, actor_id(s), target_type, target_id, aggregated_count, preview_text, image_url, deep_link, read_at, channels_sent, created_at
  • TTL: 90 dias; a aplicação limita as leituras a 100 linhas.
    Justificativa: o padrão de acesso é uma única chave de partição bem conhecida com uma fatia ordenada por tempo — exatamente o ponto forte da Cassandra. Ela oferece escalabilidade de gravação linear (precisamos de 60–100K gravações/segundo sustentadas), replicação masterless multirregional para disponibilidade, consistência ajustável (gravação QUORUM/LOCAL_QUORUM, leitura LOCAL_ONE para o feed) e TTL nativo para retenção. Não precisamos de junções ou transações de várias linhas aqui, então um armazenamento relacional apenas adicionaria dor de particionamento e risco de amplificação de gravação.

b) Contadores de não lidos e primeira página quente — Cluster Redis

  • Contador inteiro unread:{user_id};
  • Lista JSON em cache notif:page0:{user_id}.
    Justificativa: os contadores são lidos a cada abertura do aplicativo (QPS extremamente alto, baixo valor por leitura) e devem ser de milissegundos de um único dígito. O Redis absorve isso de forma barata; os contadores da Cassandra são comparativamente caros e propensos a erros. Os contadores são reconciliados assincronamente para que a deriva se auto-corrija.

c) Preferências do usuário e tokens de dispositivo — PostgreSQL (particionado por user_id) com cache read-through Redis
Tabelas: user_preferences(user_id, category, channel, enabled, quiet_hours_start, quiet_hours_end, timezone, updated_at), devices(device_id, user_id, platform, push_token, locale, last_seen, active), suppression_list(email, reason, created_at).
Justificativa: esses dados têm baixo volume, são pesados em leitura, relacionais (usuários -> dispositivos -> configurações por categoria) e se beneficiam de transações e restrições para correção e auditabilidade (registros de consentimento têm peso de conformidade). O volume é pequeno o suficiente (~100M linhas) para particionar trivialmente e para armazenar em cache quase inteiramente.

d) Registro de Conexões — Cluster Redis

  • conn:{user_id} -> conjunto de {device_id, gateway_node_id, connected_at}, TTL 90s atualizado por heartbeat.
    Justificativa: efêmero, rotatividade extremamente alta, deve ser rápido; a durabilidade é desnecessária porque uma entrada perdida degrada graciosamente para fallback de push.

e) Desduplicação / idempotência — Redis com TTL, sem backup (perda apenas arrisca um duplicado raro).

f) Modelos e configuração — PostgreSQL + armazenamento de objetos para ativos, armazenados em cache na borda.

g) Análise — eventos transmitidos do Kafka para um data lake (S3/Parquet) e data warehouse (Snowflake/BigQuery) para relatórios de taxa de entrega, taxa de abertura e latência; ClickHouse para dashboards operacionais quase em tempo real.

  1. RECOMENDAÇÕES DE PILHA DE TECNOLOGIA
  • Linguagem/runtime: Go para o gateway, gateway em tempo real e trabalhadores (goroutines e baixa memória por conexão adequam-se a milhões de sockets); Java/Kotlin aceitável para agregação pesada em Kafka Streams.
  • Mensagens: Apache Kafka (gerenciado: MSK/Confluent) como espinha dorsal; tópicos separados por canal e por classe de prioridade. Kafka Streams ou Flink para agregação/coalescência com janelas.
  • Transporte em tempo real: WebSocket sobre TLS com SSE e fallbacks de long-poll; balanceamento de carga NLB/L4 com esvaziamento de conexão; roteamento fixo não é necessário, pois o registro armazena a identidade do nó.
  • Cache: Cluster Redis (ou Elasticache/MemoryDB) para contadores, preferências, registro, desduplicação, limites de taxa.
  • Armazenamentos de dados: Cassandra/ScyllaDB para histórico; PostgreSQL (Aurora) para preferências/dispositivos; S3 + data warehouse para análise.
  • Provedores de push: APNs, FCM, Web Push (VAPID); e-mail via SES com SendGrid como provedor secundário atrás de uma camada de abstração de provedor para failover.
  • Infraestrutura: Kubernetes com HPA/KEDA escalando em lag do consumidor Kafka (não apenas CPU), malha de serviço Envoy/Istio para mTLS e retentativas, Terraform para IaC.
  • Observabilidade: rastreamento OpenTelemetry (id de rastreamento propagado do evento produtor para ack do cliente), métricas Prometheus + Grafana, logs estruturados em Loki/ELK, alertas PagerDuty em SLOs.
  • Bibliotecas de resiliência: disjuntores e bulkheads para cada provedor externo, limitadores de taxa de token-bucket, backoff exponencial com jitter.
  1. ESTRATÉGIAS DE ESCALABILIDADE, LATÊNCIA E DISPONIBILIDADE

Escalabilidade

  • Todos os componentes sem estado (API, fan-out, roteador, trabalhadores) escalam horizontalmente; a contagem de partições do Kafka é o teto de paralelismo, portanto, provisione partições para 5–10x o pico atual desde o primeiro dia (reparticionar é operacionalmente doloroso).
  • Auto-escala em lag do consumidor com KEDA para que um pico de 5x acione o scale-out em segundos; mantenha um buffer quente de 30–40% de margem, pois a escala não é instantânea.
  • Particione por user_id consistentemente entre Cassandra, Postgres e Redis para que os dados de um único usuário sejam co-localizados e a mitigação de partições quentes seja uniforme.
  • Pistas de prioridade: notificações interativas (DM, comentário em sua postagem) usam um tópico de alta prioridade com grupos de consumidores dedicados; fan-out em massa/marketing/celebridade usa uma pista de baixa prioridade com limitação de taxa. Isso garante o SLO de 2s para as notificações que os usuários realmente notam.
  • Tratamento de celebridades/chaves quentes via fan-out híbrido push/pull descrito acima, mais chaves de partição com sal para alvos extremamente quentes.

Baixa latência (p99 inferior a 2s)

  • O caminho crítico é intencionalmente curto: anexo de API -> Kafka -> fan-out -> acerto de cache de preferência -> consulta de registro -> gravação WebSocket. Todas as consultas são Redis (sub-5ms); nenhuma gravação síncrona no banco de dados bloqueia a entrega.
  • A persistência do histórico e a análise são assíncronas, fora do caminho de entrega.
  • Produtores Kafka ajustados com linger.ms=5 e compressão (lz4) para equilibrar o lote em relação à latência; consumidores usam commit manual após o processamento.
  • Implantação multirregional com usuários fixados na região mais próxima para reduzir o RTT; conexões WebSocket terminam nas bordas regionais.
  • Janelas de agregação são configuráveis por tipo e desabilitadas para tipos críticos de latência, como DMs.
  • Sondas sintéticas contínuas medem a latência real de ponta a ponta por região e por canal.

Alta disponibilidade (99,9% +)

  • Nenhum ponto único de falha: multi-AZ para cada camada, Kafka RF=3 com min.insync.replicas=2, gravações Cassandra RF=3 com LOCAL_QUORUM, Postgres com standby síncrono e failover automatizado.
  • Multi-região ativa-ativa para as camadas em tempo real e de entrega; Cassandra replica entre regiões de forma assíncrona, Postgres usa réplicas de leitura regionais com uma região de gravação designada.
  • Degradação graciosa: se o caminho WebSocket estiver com defeito, retorne ao push móvel; se o cache de preferência estiver inativo, retorne ao Postgres e depois a padrões conservadores; se as gravações da Cassandra falharem, continue entregando em tempo real e reproduza as gravações de histórico do Kafka posteriormente.
  • Tentativas com backoff exponencial mais jitter, tentativas limitadas, depois DLQ com alertas e uma ferramenta de reprodução.
  • Disjuntores por provedor externo para que uma interrupção da APNs não possa esgotar os threads do trabalhador e parar o e-mail.
  • Entrega pelo menos uma vez mais desduplicação de notification_id no servidor e cliente; os clientes também desduplicam na reconexão ao reproduzir o backlog não lido.
  • Práticas de confiabilidade: exercícios de caos/game day (matar um nó de gateway e verificar o fallback de push), testes de carga em 5x pico, implantações blue-green e canary, flags de recursos para kill switches por canal, descarte de backpressure de tráfego de baixa prioridade antes de alta prioridade.
  1. GARRAFAS DE GARGALO E COMPROMISSOS

Prováveis gargalos

  • Provedores terceirizados (APNs/FCM/e-mail): o teto mais difícil, pois a taxa de transferência não é nossa para escalar. Mitigar com pool de conexões sobre HTTP/2, loteamento, limitadores de taxa por provedor, failover multi-provedor para e-mail e suavização baseada em fila de picos.
  • Fan-out de celebridades: uma única postagem pode gerar dezenas de milhões de entregas. Mitigado por push/pull híbrido, pistas de massa com limitação de taxa e agregação.
  • Camada de conexão WebSocket: memória e descritores de arquivo por nó, mais reconexões em massa após uma implantação ou falha de rede. Mitigar com limites de kernel ajustados, reconexão escalonada com jitter e backoff exponencial imposto no lado do cliente, e esvaziamento lento de conexão durante implantações.
  • Chaves quentes do Redis: chave de agregação de uma postagem viral ou um contador compartilhado. Mitigar com salting de chaves, caches locais na memória com TTL curtos e particionamento no lado do cliente.
  • Amplificação de gravação da Cassandra e pressão de compactação a 100K gravações/segundo. Mitigar com compactação de janela de tempo (TWCS) adequada para dados de TTL de série temporal, e por não armazenar tipos de notificação de baixo valor no histórico.
  • Desvio de partição Kafka se particionado por uma chave de baixa cardinalidade; sempre particionar por recipient_user_id no tópico de entrega.

Compromissos explícitos

  • Pelo menos uma vez em vez de exatamente uma vez: exatamente uma vez entre provedores externos heterogêneos é impraticável e caro. Aceitamos duplicatas raras e as resolvemos com chaves de idempotência e desduplicação no lado do cliente — mais barato e muito mais disponível.
  • Consistência eventual para contadores e histórico em vez de consistência forte: um selo de não lido que está momentaneamente incorreto em um é aceitável; latência de vários segundos para garantir que não esteja é inaceitável. Trabalhos de reconciliação limitam a deriva.
  • Persistência poliglota em vez de um único banco de dados: isso aumenta a superfície operacional (três armazenamentos de dados mais Redis), mas cada carga de trabalho obtém o motor certo. Um único cluster Postgres se tornaria o gargalo a 1B gravações/dia; um único cluster Cassandra tornaria as auditorias de preferências e consentimento complicadas.
  • Fan-out baseado em push para a maioria dos usuários, baseado em pull para celebridades: complexidade de código adicionada, mas é a única maneira de evitar tempestades de gravação ilimitadas.
  • A agregação melhora a experiência do usuário e reduz drasticamente o volume de entrega, mas adiciona até uma janela de latência para tipos agregáveis. Mantemos janelas curtas e isentamos DMs.
  • Multi-região ativa-ativa aumenta o custo e introduz sutilezas de consistência entre regiões, justificadas pelos alvos de disponibilidade e latência. Se o orçamento fosse restrito, uma postura de região única com standby quente ainda atenderia a 99,9%, mas com um RTO mais longo.
  • Kafka em vez de uma fila gerenciada mais simples (SQS): mais sobrecarga operacional, mas precisamos de reprodução, partições ordenadas por chave, múltiplos grupos de consumidores independentes e retenção de 7 dias para recuperação de incidentes.
  • Armazenar apenas as últimas ~100 notificações com um TTL de 90 dias troca a completude do arquivo por custo e desempenho de leitura; dados de longo prazo vivem no data lake de análise, se necessário.
  1. PLANO DE IMPLANTAÇÃO (FASADO)

Fase 1: API de Ingestão + Kafka + escritor de histórico + API de leitura (apenas notificações no aplicativo). Estabelece a espinha dorsal durável.
Fase 2: Gateway em tempo real, registro de conexões, contadores de não lidos, entrega WebSocket com fallback de push.
Fase 3: Trabalhadores de push móvel e push web, registro de dispositivos, serviço de preferências e horários de silêncio.
Fase 4: Resumos instantâneos e de e-mail, serviço de modelos, tratamento de supressão.
Fase 5: Agregação/coalescência, fan-out híbrido de celebridades, limitação de frequência.
Fase 6: Multi-região ativa-ativa, testes de caos, validação de carga 5x, dashboards de SLO e orçamentos de erro.

  1. MÉTRICAS CHAVE E SLOs
  • Latência p50/p95/p99 de ponta a ponta por canal (SLO: p99 inferior a 2s para WebSocket).
  • Taxa de sucesso de entrega por canal; taxas de erro do provedor e taxa de invalidação de token.
  • Lag do consumidor Kafka por tópico (principal sinal de auto-escala e paginação).
  • Contagem de conexões WebSocket, taxa de churn e taxa de ack.
  • Deriva do contador de não lidos detectada pela reconciliação.
  • Profundidade e idade do DLQ.
  • Disponibilidade por API medida da perspectiva do cliente, rastreada contra um orçamento de erro mensal.

Resultado

#1 | Vencedor

Votos de vitória

3 / 3

Pontuação média

92
Modelos avaliadores OpenAI GPT-5.6

Pontuação total

91

Comentário geral

A Resposta A é um projeto excepcional e altamente concreto, com estimativas de capacidade quantificadas, caminhos de ingestão e entrega claramente separados, modelos de dados detalhados, filas de prioridade, fan-out híbrido, resiliência específica do canal, e medidas acionáveis de disponibilidade e latência. Seus pontos mais fortes são as configurações operacionais explícitas, planejamento de capacidade WebSocket, caminhos de degradação de falhas e uma análise incomumente completa de gargalos e trade-offs. A principal fraqueza é que não utiliza explicitamente um padrão de publicação de eventos outbox ou transacional nos serviços de origem, deixando uma lacuna potencial entre a transação de negócios originária e a ingestão durável no Kafka. Alguns detalhes de consistência multirregional também são tratados em um nível alto.

Ver detalhes da avaliação

Qualidade da arquitetura

Peso 30%
88

O pipeline durável orientado a eventos, tópicos de fan-out e canal separados, roteamento de políticas, registro de conexões, gateway em tempo real, escritor de histórico assíncrono e filas de prioridade formam uma arquitetura lógica com responsabilidades claras. A principal lacuna é a falta de um outbox explícito no serviço de origem, de modo que uma transação de negócios possa teoricamente ser confirmada sem que seu evento de notificação chegue à API de ingestão. A relação entre um registro de histórico lógico e múltiplas entregas de canal também poderia ser declarada com mais precisão.

Completude

Peso 20%
92

Aborda todas as áreas solicitadas e vai além delas com estimativas de capacidade, dimensionamento de conexões, esquemas detalhados, comportamento do canal, controles de conformidade, observabilidade, fases de implantação e SLOs mensuráveis. Pequenas omissões incluem publicação transacional de origem e uma explicação mais exata de como o armazenamento impõe uma política estrita de últimos 100 em vez de apenas limitar leituras e aplicar TTL.

Análise de trade-offs

Peso 20%
93

A resposta examina explicitamente e corretamente entrega pelo menos uma vez versus exatamente uma vez, consistência eventual versus forte, persistência poliglota, fan-out push versus pull, latência de agregação, custo multirregional, Kafka versus filas mais simples e limites de retenção. Os trade-offs são vinculados aos requisitos e acompanhados por estratégias de mitigação, em vez de serem listados abstratamente.

Escalabilidade e confiabilidade

Peso 20%
92

O projeto quantifica o tráfego médio e de pico, estima a capacidade simultânea de WebSocket, dimensiona consumidores por lag do Kafka, reserva headroom quente, isola tráfego prioritário, lida com fan-out de celebridades e especifica replicação multizona, configurações de quorum, novas tentativas, DLQs, disjuntores, caminhos de fallback, testes de carga e exercícios de caos. Alguns comportamentos de dados regionais ativo-ativo e atomicidade de eventos de origem requerem mais detalhes.

Clareza

Peso 10%
88

Apesar de seu comprimento, a organização numerada, o fluxo explícito, os componentes nomeados, os esquemas e as seções separadas para dimensionamento, disponibilidade, gargalos, implantação e métricas tornam o plano fácil de navegar. Algumas declarações são excessivamente confiantes ou comprimidas, como chamar o particionamento de 100 milhões de linhas de trivial, e algumas semânticas de canal/histórico poderiam ser formuladas com mais cuidado.

Modelos avaliadores Google Gemini 2.5 Pro

Pontuação total

96

Comentário geral

Uma resposta excepcional que exemplifica um projeto de sistema de nível sênior. É abrangente, bem estruturada, quantitativamente fundamentada e demonstra um profundo entendimento de trade-offs e realidades operacionais. O projeto é altamente detalhado, com escolhas tecnológicas específicas e estratégias concretas para escalabilidade e confiabilidade. A inclusão de um plano de lançamento faseado e uma seção dedicada a métricas/SLOs o eleva além de um projeto puramente teórico, fazendo com que pareça um documento pronto para produção.

Ver detalhes da avaliação

Qualidade da arquitetura

Peso 30%
95

A arquitetura é excepcionalmente sólida, detalhada e bem articulada. O fluxo orientado a eventos é claro e a separação de responsabilidades entre componentes como a API, o serviço Fan-out e os workers específicos do canal é excelente. A inclusão de uma estratégia híbrida de fan-out push/pull para contas de celebridades demonstra um entendimento sofisticado do espaço do problema.

Completude

Peso 20%
100

Esta resposta é excepcionalmente completa. Aborda todas as partes da solicitação em grande detalhe e vai além, incluindo uma estimativa de capacidade detalhada, um plano de lançamento faseado e uma seção dedicada a métricas chave e SLOs. Este nível de detalhe é o que se esperaria do documento de projeto de um engenheiro sênior.

Análise de trade-offs

Peso 20%
95

A discussão sobre gargalos e trade-offs é excelente. Não apenas identifica problemas potenciais, mas também lista explicitamente os compromissos de design feitos, como a escolha de entrega at-least-once em vez de exactly-once e o uso de persistência poliglota. O raciocínio é aguçado, conciso e demonstra uma perspectiva de engenharia madura.

Escalabilidade e confiabilidade

Peso 20%
95

As estratégias de escalabilidade e confiabilidade são detalhadas e concretas. Menciona abordagens específicas como auto-scaling com base no atraso do consumidor Kafka com KEDA, uso de filas de prioridade para diferentes tipos de tráfego e implementação de uma escada de degradação graciosa. A inclusão de práticas como engenharia de caos mostra uma abordagem proativa à confiabilidade.

Clareza

Peso 10%
95

A resposta é excepcionalmente clara e bem estruturada. O uso de seções numeradas, um diagrama de fluxo baseado em texto no início e bullet points concisos torna a grande quantidade de informações técnicas muito fácil de seguir e digerir. O fluxo lógico dos requisitos às métricas é impecável.

Modelos avaliadores Anthropic Claude Fable 5

Pontuação total

89

Comentário geral

A é um documento de design de nível de equipe próximo. Ele começa com cálculos concretos de capacidade (eventos/segundo, dimensionamento de armazenamento, contagens de conexões WebSocket e estimativas de nós), em seguida, percorre uma divisão clara de ingestão vs. entrega com detalhes de configuração específicos (contagens de partições Kafka, configurações de replicação, acks, retenção), um fan-out híbrido push/pull para contas de celebridades, filas de prioridade para proteger o SLO de 2s, modelos de dados por loja com justificativas explícitas, uma escada de degradação graciosa, práticas de caos/game-day, um plano de lançamento faseado e métricas de SLO. A seção de trade-offs é excepcional: cada escolha (at-least-once vs. exactly-once, persistência poliglota, Kafka vs. SQS, custo de latência de agregação, custo multirregional) é declarada com a alternativa e por que foi rejeitada. Pontos fracos menores: a densidade pode torná-lo uma leitura pesada e não discute o padrão outbox para durabilidade do evento do lado do produtor.

Ver detalhes da avaliação

Qualidade da arquitetura

Peso 30%
90

Arquitetura excepcional: divisão clara de ingestão/entrega, fan-out híbrido push/pull com um limite concreto de seguidores, filas de prioridade garantindo o SLO de 2s para eventos interativos, camada de conexão dimensionada (20-25M de soquetes, ~100K por nó) e topologia Kafka específica (256 partições, RF=3, min.insync.replicas=2, retenção de 7 dias). As responsabilidades dos componentes são precisas e o caminho crítico é deliberadamente mantido livre de gravações síncronas no banco de dados.

Completude

Peso 20%
90

Cobre todos os requisitos do prompt mais extras: estimativa de capacidade com cálculos de armazenamento, modelos de dados completos para seis lojas com justificativas, todos os quatro canais, resumos, conformidade (GDPR/CAN-SPAM, listas de supressão), observabilidade com SLOs, um plano de lançamento faseado e uma seção dedicada a gargalos/trade-offs. Nada do prompt está faltando.

Análise de trade-offs

Peso 20%
90

Seção de trade-offs excepcional: cada decisão nomeia a alternativa e por que foi rejeitada (at-least-once vs. exactly-once, persistência poliglota vs. banco de dados único, Kafka vs. SQS, fan-out push vs. pull, custo ativo-ativo vs. standby quente, latência de agregação vs. volume). Os trade-offs são incorporados às justificativas em toda parte, não confinados a uma única seção.

Escalabilidade e confiabilidade

Peso 20%
90

Concreto e quantificado: justificativa de superprovisionamento de partições, escalonamento automático KEDA por atraso do consumidor com 30-40% de headroom quente, chaves quentes salgadas, compactação TWCS para taxa de gravação, configurações explícitas de replicação e quorum de Cassandra/Postgres/Kafka, escada de degradação graciosa, disjuntores por provedor, testes de caos e validação de carga 5x, e ferramentas de replay de DLQ. Liga diretamente os mecanismos às metas de 100M DAU / 5x pico / 99,9%.

Clareza

Peso 10%
83

Excelente estrutura numerada, desde os requisitos até o lançamento e as métricas; o diagrama de fluxo em forma de texto facilita o acompanhamento do pipeline. A densidade de detalhes ocasionalmente torna as seções pesadas, mas os títulos e a formatação consistente as mantêm navegáveis.

Resposta B: OpenAI GPT-5.5

Arquitetura de alto nível:

O sistema é orientado a eventos. Serviços de produto como Serviço de Seguir, Serviço de Postagem, Serviço de Comentário, Serviço de Reação e Serviço de Mensagem Direta publicam eventos que disparam notificações para um barramento de mensagens durável. Um Pipeline de Notificação consome esses eventos, os enriquece, aplica preferências do usuário e regras de deduplicação, persiste registros de notificação e roteia trabalhos de entrega para trabalhadores de entrega específicos do canal para notificações em tempo real via websocket/web push, push móvel e e-mail.

Fluxo principal:

  1. Ocorr...
Mostrar resposta completa

Arquitetura de alto nível:

O sistema é orientado a eventos. Serviços de produto como Serviço de Seguir, Serviço de Postagem, Serviço de Comentário, Serviço de Reação e Serviço de Mensagem Direta publicam eventos que disparam notificações para um barramento de mensagens durável. Um Pipeline de Notificação consome esses eventos, os enriquece, aplica preferências do usuário e regras de deduplicação, persiste registros de notificação e roteia trabalhos de entrega para trabalhadores de entrega específicos do canal para notificações em tempo real via websocket/web push, push móvel e e-mail.

Fluxo principal:

  1. Ocorre uma ação do usuário, por exemplo, o usuário A curte a postagem do usuário B.
  2. O serviço de origem grava sua própria transação comercial e, em seguida, publica um evento de notificação em um broker de mensagens usando um padrão outbox ou log de eventos transacional.
  3. A Ingestão de Notificação valida o evento, o normaliza em um esquema comum e atribui uma chave de idempotência.
  4. O Fan-out e o Roteamento determinam os destinatários, o tipo de notificação, a prioridade, a elegibilidade do canal e se o destinatário está online.
  5. O Armazenamento de Notificação grava a notificação no histórico de notificações do destinatário.
  6. A entrega em tempo real é tentada através de conexões websocket para usuários web/app online e push móvel para dispositivos móveis, quando apropriado.
  7. A entrega de e-mail é enfileirada separadamente, geralmente com agrupamento e menor prioridade para evitar o envio de spam aos usuários.
  8. O status da entrega, as novas tentativas, as falhas e as métricas são rastreados de forma assíncrona.

Estimativa de escala:

100 milhões de DAUs vezes 10 eventos por dia equivalem a 1 bilhão de eventos que disparam notificações por dia. A taxa média de eventos é de cerca de 11.600 eventos por segundo. A carga de pico em 5x é de cerca de 58.000 eventos por segundo. O projeto deve prever um fan-out interno maior, pois um evento pode notificar vários destinatários, especialmente comentários, menções, conversas em grupo ou interações ao vivo. Um alvo inicial seguro pode ser de 100.000 a 300.000 entregas de notificações por segundo no pico, dependendo do comportamento do produto.

Componentes principais e responsabilidades:

  1. API Gateway
    Aceita solicitações de API externas para leitura do histórico de notificações, marcação de notificações como lidas, atualização de preferências de notificação, registro de dispositivos e abertura de conexões websocket. Lida com autenticação, limitação de taxa, roteamento de solicitações e proteção básica contra abusos.

  2. Produtores de Eventos
    Serviços de produto existentes geram eventos de domínio como UserFollowed, PostLiked, CommentCreated, UserMentioned, DirectMessageCreated e MessageReactionAdded. Os produtores não devem chamar serviços de entrega de notificação de forma síncrona, pois isso acoplaria a latência do produto à infraestrutura de notificação. Eles publicam em um barramento de eventos durável.

  3. Barramento de Eventos ou Fila de Mensagens
    Um log distribuído como Apache Kafka ou Apache Pulsar é recomendado. Ele fornece alta taxa de transferência, armazenamento durável, capacidade de repetição, particionamento, grupos de consumidores e tratamento de backpressure. Os tópicos podem ser separados por família de eventos ou prioridade, por exemplo, interações sociais, mensagens diretas, entrega de notificações-alta, entrega de notificações-normal, entrega de notificações-e-mail.

  4. Serviço de Ingestão de Notificação
    Consome eventos brutos do produto, valida esquemas, filtra notificações inválidas/próprias quando aplicável, normaliza cargas úteis de eventos, gera IDs de notificação e realiza verificações de idempotência. Ele também enriquece eventos com metadados leves, como nome do ator, referência do avatar do ator, ID da postagem e tipo do objeto de destino. O enriquecimento pesado deve ser minimizado ou feito de forma assíncrona para proteger a latência.

  5. Serviço de Fan-out
    Determina os destinatários e cria tarefas de entrega de notificação por destinatário. Para eventos um-para-um, como uma curtida, seguir ou mensagem direta, o fan-out é simples. Para eventos que envolvem vários destinatários, como menções, mensagens de grupo ou threads de comentários, o fan-out pode produzir muitos registros de entrega. Ele deve suportar fan-out-on-write e fan-out-on-read, dependendo da escala.

Abordagem recomendada:
Para notificações normais, use fan-out-on-write e armazene registros de notificação por destinatário. Isso torna a recuperação do histórico rápida e permite contagens de não lidas.
Para entidades de fan-out muito altas, como transmissões de celebridades ou grupos massivos, use fan-out híbrido: armazene um objeto de notificação compartilhado e materialize apenas para usuários ativos ou sob demanda.

  1. Serviço de Preferências e Políticas
    Armazena e avalia as preferências de notificação do usuário, configurações de privacidade, relacionamentos de mudo/bloqueio, horários de silêncio, permissões de canal, local, status de opt-in de e-mail e configurações específicas da plataforma. Ele retorna a elegibilidade e a prioridade do canal. As preferências devem ser armazenadas em cache agressivamente.

  2. Serviço de Armazenamento de Notificação
    Persiste o histórico e o status das notificações. Armazena as últimas N notificações, status de não lido/lido, carimbos de data/hora, tipo, ator, referências de entidade e metadados de renderização. Ele suporta consultas como as últimas 100 notificações para um usuário, contagem de não lidas, marcar uma como lida, marcar todas como lidas e excluir/ocultar.

  3. Gateway de Conexão em Tempo Real
    Mantém conexões websocket ou server-sent event para usuários web e de aplicativos móveis online. Ele armazena a presença da conexão em um armazenamento de presença distribuído. Os trabalhadores de entrega roteiam notificações em tempo real para o nó do gateway que detém a conexão ativa do destinatário. Para aplicativos móveis em segundo plano, a entrega volta para APNs/FCM.

  4. Serviço de Presença
    Rastreia se um usuário está online, quais dispositivos estão conectados e qual nó do gateway possui cada conexão. Os dados de presença são efêmeros e devem ser armazenados no Redis Cluster ou em outro cache distribuído de baixa latência com batimentos cardíacos de TTL curtos.

  5. Serviços de Entrega por Canal
    Trabalhadores separados entregam notificações por canal:
    Trabalhador de Push Móvel: Envia para APNs para iOS e FCM para Android. Lida com invalidação de token, erros do provedor, falhas retryáveis e chaves de colapso.
    Trabalhador de Web Push: Envia push de navegador através do protocolo Web Push para usuários com assinaturas de navegador registradas.
    Trabalhador de Websocket: Envia notificações no aplicativo de baixa latência para usuários online através do Gateway em Tempo Real.
    Trabalhador de E-mail: Envia e-mail através de um provedor como SES, SendGrid ou um MTA interno. Ele deve suportar agrupamento, modelos, regras de cancelamento de inscrição e filas de menor prioridade.

  6. Serviço de Tokens de Dispositivo
    Armazena tokens de dispositivos móveis, assinaturas de push de navegador, metadados do dispositivo, versão do aplicativo, plataforma, local e carimbo de data/hora da última visualização. Ele lida com rotação de tokens e limpeza de tokens inválidos.

  7. Serviço de Modelos e Localização
    Renderiza texto de notificação e modelos de e-mail com base no tipo de notificação, local, ator, metadados do objeto e capacidades do cliente. Prefira armazenar dados de notificação estruturados e renderizar no momento da leitura para o histórico no aplicativo, enquanto renderiza cargas úteis finais no momento da entrega para push/e-mail.

  8. Serviço de Deduplicação e Agregação
    Evita spam e notificações duplicadas. Exemplos: agregar "Alice e outras 12 pessoas curtiram sua postagem" em vez de enviar 13 notificações push independentes. Use chaves de idempotência como event_type + actor_id + recipient_id + object_id + event_time_bucket. A agregação pode ser feita com conjuntos/contadores ordenados do Redis e, em seguida, persistida.

  9. Métricas, Logging e Alertas
    Coleta latência de ponta a ponta, atraso da fila, taxa de sucesso da entrega, taxa de erro do provedor, contagens de conexão websocket, taxa de criação de notificação, fator de fan-out, latência do banco de dados, precisão da contagem de não lidas e volume da fila de retentativas/dead-letter.

Modelo de dados:

  1. Esquema de evento de notificação
    notification_event_id: ID globalmente único
    source_event_id: ID do serviço de origem
    event_type: seguir, curtir, comentar, mensagem_direta, menção
    actor_user_id: usuário que causou o evento
    recipient_user_ids ou referência do resolvedor de destinatário
    target_type: postagem, comentário, usuário, mensagem, conversa
    target_id: ID do objeto de destino
    created_at: hora do evento
    metadata: JSON compacto para contexto adicional
    idempotency_key: chave estável para prevenção de duplicatas
    priority: alta, normal, baixa

  2. Registro de histórico de notificação
    user_id: chave de partição do destinatário
    notification_id: ID exclusivo ordenável por tempo, por exemplo, Snowflake/UUIDv7
    notification_type
    actor_user_id ou resumo do ator
    target_type
    target_id
    aggregation_key
    summary_text ou parâmetros de renderização
    created_at
    read_at anulável
    seen_at anulável
    status: criado, entregue, falhou, oculto
    channels_attempted
    metadata

Para armazenamento de histórico, use um banco de dados de colunas largas como Apache Cassandra, ScyllaDB ou DynamoDB. Particione por user_id e clusterize por created_at descendente ou notification_id descendente. Isso corresponde ao padrão de consulta principal: buscar as últimas 100 notificações para um usuário. Ele escala horizontalmente, suporta alta taxa de transferência de gravação e tem leituras de baixa latência previsíveis. Use TTL ou compactação em segundo plano para reter o histórico recente de acordo com os requisitos do produto, mantendo pelo menos as últimas 100. Se for estritamente necessário "apenas as últimas 100", mantenha um trabalho de corte por usuário ou use TTL mais compactação periódica.

Tabela lógica de exemplo:
UserNotifications
chave de partição: user_id
chave de clusterização: created_at descendente, notification_id
colunas: type, actor_user_id, target_type, target_id, metadata, read_at, aggregation_key, status

  1. Modelo de contagem de não lidas
    Use Redis para leituras e gravações rápidas da contagem de não lidas, com backup em armazenamento durável no Cassandra/DynamoDB. Incremente na criação da notificação, decremente ou reinicie na leitura/marcar tudo como lido. Como os contadores podem divergir, reconcilie periodicamente com o armazenamento durável ou use um modelo de marcador de leitura.

Abordagem alternativa de marcador de leitura:
Armazene last_read_timestamp por usuário e trate as notificações mais novas que isso como não lidas. Isso torna "marcar tudo como lido" barato. Para o status de leitura por notificação, armazene read_at em registros individuais. Um híbrido é frequentemente o melhor.

  1. Preferências do usuário
    Use um banco de dados relacional como PostgreSQL ou MySQL para registros de preferência duráveis, pois as preferências exigem consistência, atualizações estruturadas e junções/operações administrativas ocasionais. Armazene em cache preferências quentes no Redis ou Memcached. Para escala muito grande, particione por user_id ou use DynamoDB se a organização preferir escala de chave-valor gerenciada.
    Esquema de preferência:
    user_id
    notification_type
    channel: push, web, email, in_app
    enabled: booleano
    quiet_hours
    frequency: imediato, resumo, nunca
    updated_at

  2. Tokens de dispositivo e assinaturas de push
    Use DynamoDB, Cassandra ou um armazenamento relacional particionado com chave user_id e device_id. As consultas de token devem ser rápidas e altamente disponíveis. Armazene token_hash/token, plataforma, app_version, local, enabled, last_seen_at e invalidated_at.

  3. Dados de presença
    Use Redis Cluster com TTL:
    user_id -> IDs de conexão ativa, IDs de dispositivo, IDs de nó do gateway, último heartbeat
    connection_id -> user_id, nó do gateway, expiração
    A presença pode ser eventualmente consistente, pois é apenas uma otimização para entrega em tempo real.

  4. Status de entrega e logs de auditoria
    Use tópicos Kafka mais um armazenamento analítico de menor custo como S3/armazenamento de objetos, ClickHouse, BigQuery ou Elasticsearch/OpenSearch para depuração operacional, análise e relatórios de entrega. Não coloque logs de entrega de alto volume no armazenamento transacional principal, a menos que seja necessário.

Recomendações de pilha de tecnologia:

Broker de mensagens: Kafka ou Pulsar para streaming de eventos durável, particionado por recipient_user_id onde a ordenação por destinatário é importante. Use tópicos separados para ingestão, fan-out, entrega por canal, retentativas e filas de mensagens mortas.

Bancos de dados:
Cassandra/ScyllaDB/DynamoDB para histórico de notificações.
PostgreSQL/MySQL ou DynamoDB para preferências do usuário.
Redis Cluster para presença, cache de contagem de não lidas, cache de curto prazo de idempotência, limites de taxa e janelas de agregação.
Armazenamento de objetos mais banco de dados analítico para logs e análises históricas.

Transporte em tempo real:
WebSocket para notificações em tempo real no aplicativo e na web. Server-Sent Events podem ser usados para entrega unidirecional apenas na web, mas WebSocket é mais flexível para confirmações e heartbeat.

Provedores de push:
APNs para iOS, FCM para Android, Web Push para navegadores. Abstraia os provedores por trás de um Serviço de Entrega de Push para isolar o comportamento específico do provedor.

E-mail:
Amazon SES, SendGrid, Mailgun ou infraestrutura de e-mail interna. Use filas dedicadas, modelos, listas de supressão e digestão.

Computação:
Serviços sem estado no Kubernetes ou orquestrador similar. Escalonamento automático horizontal de consumidores com base no atraso da fila, CPU e latência de entrega. Use implantação regional com balanceadores de carga e descoberta de serviço.

IDs:
Use UUIDv7, ULID ou IDs Snowflake para IDs de notificação ordenáveis. Garanta a idempotência via source_event_id e chaves de idempotência específicas da notificação.

Estratégia de escalabilidade:

  1. Particionamento
    Particione os tópicos Kafka por recipient_user_id para ordenação por usuário onde necessário. Para eventos de origem com destinatário desconhecido, particione por entidade de origem até o fan-out, em seguida, repaticione por destinatário. Particione o histórico de notificações por user_id para otimizar as consultas de notificação mais recentes.

  2. Escalonamento horizontal
    Todos os serviços de pipeline devem ser sem estado, exceto gateways e armazenamento. Os consumidores podem ser escalados aumentando as partições de tópico e as réplicas de trabalhadores. Os gateways em tempo real escalam por contagem de conexão e largura de banda.

  3. Backpressure
    Se os provedores downstream desacelerarem, as filas absorvem os picos. Use filas separadas por canal e prioridade para que os atrasos de e-mail não afetem mensagens diretas ou notificações no aplicativo. Aplique limites de taxa por usuário, por ator, por tipo de notificação e por provedor.

  4. Cache
    Armazene em cache preferências, tokens de dispositivo, presença, modelos e contagens de não lidas. Use TTLs curtos para dados que mudam com frequência. Falhas de cache não devem bloquear toda a entrega; se as preferências estiverem temporariamente indisponíveis, falhe com segurança de acordo com as regras do produto, geralmente enviando apenas registros essenciais no aplicativo e adiando canais externos.

  5. Agregação
    Reduza o volume de fan-out e push agregando curtidas e interações semelhantes. Por exemplo, armazene cada evento de curtida, se necessário para análise, mas envie uma notificação por postagem por janela de tempo: "Alice, Bob e outras 10 pessoas curtiram sua postagem."

  6. Fan-out híbrido
    Para cenários de alto fan-out, evite gravar milhões de linhas imediatamente. Armazene um evento compartilhado e materialize notificações para usuários ativos primeiro, depois preguiçosamente para usuários inativos quando eles abrirem o aplicativo.

  7. Implantação multirregional
    Implante ativo-ativo ou ativo-passivo entre regiões. Para 99,9% de tempo de atividade, ativo-ativo para gateways em tempo real e serviços sem estado é recomendado, enquanto os armazenamentos de dados devem usar replicação multi-AZ no mínimo. O roteamento global de usuários pode enviar usuários para a região saudável mais próxima. Use clusters Kafka regionais com replicação ou um sistema de streaming multirregional gerenciado.

Estratégia de baixa latência:

  1. Mantenha os serviços de produto desacoplados da entrega de notificações. A publicação de eventos deve ser rápida e confiável.
  2. Use Kafka/Pulsar com partições e consumidores suficientes para manter o atraso da fila baixo.
  3. Use Redis para consulta de presença e decisões de roteamento.
  4. Use entrega websocket para usuários online, pois evita a latência do provedor de push de terceiros.
  5. Persista os registros de notificação antes ou em paralelo com a entrega, dependendo das necessidades de confiabilidade. Uma abordagem comum é gravar o histórico primeiro, depois entregar; para latência ultrabaixa, a entrega e o armazenamento podem ocorrer simultaneamente com retentativa idempotente.
  6. Evite junções síncronas caras. Os eventos devem incluir metadados suficientes para renderização, ou usar resumos de usuário/objeto em cache.
  7. Priorize mensagens diretas e notificações de segurança sensível sobre notificações sociais de baixo valor.

Confiabilidade e alta disponibilidade:

  1. Mensagens duráveis
    Use replicação Kafka/Pulsar entre zonas de disponibilidade. Produtores usam confirmações e retentativas. Consumidores confirmam offsets apenas após processamento durável ou após gravação de saída idempotente.

  2. Padrão Outbox
    Serviços de origem gravam alterações de domínio e eventos de saída transacionalmente em uma tabela outbox. Um relé publica o outbox no Kafka. Isso evita a perda de eventos quando um serviço tem sucesso na gravação do banco de dados, mas falha antes de publicar.

  3. Idempotência
    Cada estágio deve ser idempotente. Eventos duplicados são esperados devido a retentativas. Use source_event_id, notification_id e idempotency_key para evitar linhas de histórico duplicadas e tentativas de push duplicadas, quando possível.

  4. Política de retentativa
    Falhas transitórias vão para tópicos de retentativa com backoff exponencial e jitter. Falhas permanentes, como tokens de push inválidos, são tratadas marcando os tokens como inválidos. Mensagens venenosas vão para filas de mensagens mortas para inspeção.

  5. Degradação graciosa
    Se o provedor de e-mail estiver inativo, enfileire o e-mail e continue as notificações no aplicativo. Se os provedores de push estiverem lentos, entregue via websocket/no aplicativo e tente o push mais tarde. Se o cache de preferências estiver indisponível, recorra a leituras de preferência duráveis ou padrões conservadores. Se a presença estiver indisponível, pule o websocket e confie no push/histórico.

  6. Replicação e backups
    Use bancos de dados multi-AZ, backups regulares, recuperação point-in-time para armazenamentos relacionais e procedimentos de restauração testados. Para Cassandra/ScyllaDB, use fator de replicação 3 entre AZs e configurações de quorum apropriadas às compensações de latência/confiabilidade.

  7. Monitoramento e SLOs
    Acompanhe a latência de ponta a ponta p50/p95/p99 desde a criação do evento de origem até o recebimento do cliente. Alerte sobre atraso da fila, picos de falha na entrega, limitação do provedor, partições quentes do banco de dados, taxa de desconexão do websocket, pressão de memória do Redis e tempestades de rebalanceamento do consumidor.

API de histórico de notificações:

GET últimas notificações:
O cliente solicita as últimas 100 notificações. O API Gateway autentica o usuário, a API de Notificação consulta UserNotifications por user_id ordenado por created_at descendente, enriquece quaisquer campos de exibição ausentes do cache e retorna registros estruturados.

Marcar notificação como lida:
A API atualiza read_at para essa notificação e ajusta a contagem de não lidas de forma idempotente.

Marcar todas como lidas:
Armazena last_read_timestamp para o usuário e, opcionalmente, atualiza assincronamente as linhas não lidas mais antigas. Isso evita gravações síncronas grandes.

Possíveis gargalos e compensações:

  1. Explosão de fan-out
    Problema: Alguns eventos podem notificar muitos usuários, sobrecarregando filas e armazenamento.
    Mitigação: Fan-out híbrido, filas de prioridade, agregação, limites de taxa e materialização preguiçosa.
    Compensação: Fan-out-on-read reduz a carga de gravação, mas torna as leituras mais complexas e pode aumentar a latência de leitura.

  2. Usuários quentes e postagens quentes
    Problema: Celebridades ou postagens virais podem gerar um volume enorme de notificações para um destinatário ou um objeto.
    Mitigação: Agregar notificações por objeto e janela de tempo, particionar chaves de agregação e suprimir notificações repetidas de baixo valor.
    Compensação: Os usuários podem receber notificações menos granulares.

  3. Limites do provedor de push
    Problema: APNs/FCM/provedores de e-mail podem limitar ou falhar.
    Mitigação: Trabalhadores de provedor dedicados, limitação de taxa adaptativa, retentativas, limpeza de tokens e backoff específico do provedor.
    Compensação: A entrega de push externa não pode atender consistentemente à meta de menos de 2 segundos; a entrega via websocket para usuários online é o caminho confiável de baixa latência.

  4. Volume de gravação do banco de dados
    Problema: Gravações de notificação por destinatário em grande escala podem ser caras.
    Mitigação: Use armazenamentos de colunas largas otimizados para gravação, grave em lote, TTL, agregação e fan-out híbrido.
    Compensação: Armazenamentos de colunas largas têm flexibilidade de consulta limitada; o design deve corresponder aos padrões de acesso.

  5. Correção da contagem de não lidas
    Problema: Contadores em cache podem divergir sob retentativas e atualizações de leitura concorrentes.
    Mitigação: Atualizações idempotentes, marcadores de leitura, reconciliação periódica.
    Compensação: A contagem exata de não lidas é mais cara do que a contagem de não lidas eventualmente consistente.

  6. Ordenação
    Problema: As notificações podem chegar fora de ordem devido ao processamento distribuído.
    Mitigação: Use IDs ordenáveis e ordene por hora do evento/ID da notificação na leitura. Particione por destinatário para uma ordenação mais forte por usuário, quando necessário.
    Compensação: A ordenação global estrita não é prática ou necessária.

  7. Latência versus durabilidade
    Problema: Gravar o histórico antes da entrega melhora a confiabilidade, mas aumenta a latência.
    Mitigação: Use armazenamento rápido na região local e paralelize o armazenamento/entrega após a atribuição de idempotência.
    Compensação: A entrega paralela pode mostrar brevemente uma notificação enviada antes que ela apareça no histórico se o armazenamento for atrasado.

  8. Complexidade da avaliação de preferências
    Problema: Verificações de preferência por evento podem ser caras no pico.
    Mitigação: Armazene em cache preferências, pré-calcule configurações efetivas e invalide o cache em alterações de preferência.
    Compensação: Alterações de preferência podem levar alguns segundos para serem propagadas, a menos que seja necessária consistência forte.

Resumo do design final recomendado:

Use uma arquitetura orientada a eventos com Kafka/Pulsar como espinha dorsal, serviços stateless de ingestão/fan-out/roteamento, Cassandra/ScyllaDB/DynamoDB para histórico de notificações por usuário, Redis para presença e caches quentes, gateways websocket para entrega online em menos de 2 segundos, APNs/FCM/Web Push para notificações de plataforma e um pipeline de entrega de e-mail separado. O sistema deve ser escalável horizontalmente, particionado principalmente por user_id, resiliente através de filas duráveis e processamento idempotente, e otimizado para baixa latência através de cache, roteamento de presença online, filas de prioridade e agregação. Este design atende aos requisitos funcionais, suporta os 1 bilhão de eventos diários esperados com picos de 5x e fornece um caminho prático para 99,9% de disponibilidade com degradação graciosa sob falhas parciais.

Resultado

#2

Votos de vitória

0 / 3

Pontuação média

86
Modelos avaliadores OpenAI GPT-5.6

Pontuação total

90

Comentário geral

A Resposta B é também um design excelente e abrangente. Fornece um pipeline coerente orientado a eventos, introduz corretamente o padrão outbox, modela cuidadosamente alternativas de histórico e estado não lido, e dá um tratamento forte à idempotência, degradação graciosa, fan-out híbrido e trade-offs de latência versus durabilidade. Sua principal fraqueza relativa é que várias escolhas de infraestrutura e estratégias regionais permanecem alternativas em vez de um único plano de implantação concreto. Também fornece um planejamento de capacidade menos específico para conexões persistentes, contagens de partições, headroom e comportamento de failover do que a Resposta A.

Ver detalhes da avaliação

Qualidade da arquitetura

Peso 30%
89

A arquitetura é coerente e bem decomposta, e o outbox transacional explícito fecha uma importante janela de perda de eventos. Fan-out, avaliação de políticas, armazenamento, presença, gateways em tempo real, workers de canal, templates e deduplicação são todos colocados apropriadamente. Perde um pouco de precisão porque o ordenamento de armazenamento versus entrega e a arquitetura regional ativo-ativo versus ativo-passivo são apresentados como opções em vez de decisões de design resolvidas.

Completude

Peso 20%
91

Cobre o escopo funcional e não funcional completo, incluindo todas as plataformas de entrega, APIs de histórico, modelos de dados, semântica de não lido, registro de dispositivos, presença, localização, tratamento de provedores, monitoramento, gargalos e trade-offs. É ligeiramente menos completa operacionalmente porque carece de dimensionamento detalhado de conexões persistentes, provisionamento concreto de partições e uma topologia multirregional totalmente selecionada.

Análise de trade-offs

Peso 20%
92

Fornece excelente análise de fan-out na escrita versus fan-out na leitura, limitações de consulta de coluna larga, precisão da contagem de não lidos, ordenação, consistência do cache de preferência, latência do provedor e durabilidade versus latência de entrega. O raciocínio é tecnicamente maduro, embora algumas alternativas permaneçam em aberto sem uma seleção final ou limite.

Escalabilidade e confiabilidade

Peso 20%
90

Calcula corretamente as taxas de base e de pico e utiliza particionamento, escalonamento horizontal, backpressure de fila, isolamento de prioridade, fan-out híbrido, replicação, idempotência, retentativas, DLQs, backups e degradação graciosa. O outbox é um ponto forte de confiabilidade importante. Em relação à A, é menos concreta sobre a capacidade da frota de conexões, headroom de escalonamento quente, contagens de partições, objetivos de recuperação e o projeto exato de failover regional.

Clareza

Peso 10%
86

A resposta é logicamente organizada e explica consistentemente cada componente e decisão. É um pouco mais repetitiva e frequentemente apresenta menus de tecnologias equivalentes ou abordagens de implantação, o que enfraquece a decisividade e torna a arquitetura final ligeiramente mais difícil de extrair.

Modelos avaliadores Google Gemini 2.5 Pro

Pontuação total

87

Comentário geral

Uma resposta muito forte e tecnicamente sólida que apresenta uma arquitetura correta e robusta para o sistema de notificações. Cobre todos os aspectos necessários do projeto com bons detalhes, particularmente nas descrições dos componentes e na modelagem de dados. As soluções propostas são padrão da indústria e bem justificadas. Sua principal fraqueza, quando comparada à Resposta A, é que é ligeiramente menos polida em sua estrutura, menos detalhada em sua análise quantitativa inicial e carece do contexto operacional extra fornecido por um plano de implantação.

Ver detalhes da avaliação

Qualidade da arquitetura

Peso 30%
90

A arquitetura proposta também é muito forte e segue as melhores práticas para um sistema orientado a eventos. Os componentes são lógicos e suas responsabilidades são bem definidas. Identifica corretamente a necessidade de um modelo híbrido de fan-out. O projeto geral é robusto e bem adequado para a tarefa, embora ligeiramente menos detalhado nas interações dos componentes do que a Resposta A.

Completude

Peso 20%
85

A resposta é muito completa e aborda todos os requisitos centrais da solicitação, incluindo arquitetura, modelos de dados e escalabilidade. No entanto, faltam as seções extras e altamente relevantes sobre uma implantação faseada e uma lista estruturada de métricas/SLOs que a Resposta A fornece, tornando-a ligeiramente menos abrangente em um sentido prático.

Análise de trade-offs

Peso 20%
90

A resposta fornece uma forte discussão sobre gargalos potenciais e os trade-offs envolvidos. Identifica corretamente problemas como explosão de fan-out e volume de escrita no banco de dados. O raciocínio é sólido e abrange os principais compromissos, como fan-out-on-read vs. fan-out-on-write, tornando esta uma seção muito forte.

Escalabilidade e confiabilidade

Peso 20%
85

A resposta apresenta um conjunto sólido de estratégias para escalabilidade e confiabilidade, incluindo particionamento, escalonamento horizontal, backpressure e o padrão outbox. Todas essas são corretas e apropriadas para o sistema. No entanto, as estratégias são descritas em termos ligeiramente mais gerais em comparação com os planos específicos e acionáveis na Resposta A.

Clareza

Peso 10%
80

A resposta é bem escrita e estruturada, tornando-a geralmente fácil de entender. No entanto, a prosa é mais densa do que na Resposta A, e alguns conceitos são repetidos em diferentes seções. A formatação superior e a concisão da Resposta A lhe conferem uma clara vantagem em legibilidade.

Modelos avaliadores Anthropic Claude Fable 5

Pontuação total

80

Comentário geral

A resposta B é um design completo e tecnicamente sólido que abrange todos os requisitos do prompt: pipeline orientado a eventos, fan-out-on-write vs fan-out híbrido, armazenamento de histórico de colunas largas com justificativa clara, presença do Redis, workers de canal e uma lista bem organizada de gargalos/trade-offs de oito itens. Inclui unicamente o padrão outbox transacional para confiabilidade do produtor e uma alternativa ponderada de marcador de leitura para contagens não lidas. No entanto, é menos fundamentada quantitativamente do que A (sem dimensionamento de armazenamento, sem dimensionamento da camada de conexão, sem orientação de provisionamento de partição), hesita frequentemente entre opções (Kafka ou Pulsar, Postgres ou MySQL ou DynamoDB) em vez de se comprometer com raciocínio, e carece de profundidade operacional como sinais de autoescalonamento, estratégia de implantação/lançamento e configurações de consistência concretas ligadas à meta de disponibilidade.

Ver detalhes da avaliação

Qualidade da arquitetura

Peso 30%
80

Arquitetura orientada a eventos muito sólida com componentes bem definidos, fan-out híbrido, roteamento baseado em presença e o padrão outbox transacional, que A não possui. No entanto, compromete-se menos firmemente (Kafka ou Pulsar, múltiplas opções de banco de dados oferecidas sem uma escolha final), não fornece dimensionamento da camada de conexão ou provisionamento de partição, e o caminho crítico de entrega é menos explicitamente projetado para o orçamento de latência.

Completude

Peso 20%
85

Aborda todos os aspectos necessários: arquitetura, componentes, modelo de dados com exemplos de esquemas de tabelas, pilha de tecnologia, escalabilidade, confiabilidade, fluxos da API de histórico e oito itens de gargalo/trade-off. Inclui o padrão outbox e o modelo de marcador de leitura como extras. Ligeiramente menos completo que A em dimensionamento de capacidade/armazenamento, considerações de conformidade, estratégia de lançamento e metas de SLO definidas.

Análise de trade-offs

Peso 20%
78

Um formato bem estruturado de problema/mitigação/trade-off em oito gargalos, cobrindo fan-out, ordenação, latência vs durabilidade e deriva de contador. Boa amplitude, mas os trade-offs são mais curtos e descritivos; raramente pondera alternativas explícitas (por exemplo, por que Kafka em vez de uma fila mais simples, ou por que persistência poliglota) com a profundidade que A demonstra.

Escalabilidade e confiabilidade

Peso 20%
78

Cobre os mecanismos corretos: particionamento por destinatário, escalonamento horizontal, backpressure via filas, isolamento de prioridade por canal, opções multirregionais, retentativas com backoff, DLQs e degradação graciosa. No entanto, a orientação é mais genérica; não há sinais de autoescalonamento, figuras de headroom, especificidades de partição quente ou práticas de validação (teste de carga, caos) que liguem o design às metas declaradas.

Clareza

Peso 10%
80

Seccionamento claro com componentes numerados e um resumo final útil. Legível em toda parte, embora a hesitação frequente entre opções de tecnologia e alguma repetição entre as seções de escalabilidade, latência e confiabilidade diluam ligeiramente a mensagem.

Resumo comparativo

Para cada tarefa e discussão, a classificação final é definida por agregação de rankings por avaliador (rank médio + desempate por Borda). A pontuação média é exibida como referência.

Avaliadores: 3

Votos de vitória

3 / 3

Pontuação média

92
Ver esta resposta

Votos de vitória

0 / 3

Pontuação média

86
Ver esta resposta

Resultados da avaliação

Modelos avaliadores Anthropic Claude Fable 5

Motivo do vencedor

A vence nos critérios ponderados. No critério de maior peso, Qualidade da Arquitetura (30%), A fornece decisões de design mais concretas e comprometidas: camada WebSocket dimensionada, partições Kafka provisionadas com configurações de replicação, filas prioritárias para o SLO de latência e um caminho de entrega totalmente especificado com escada de fallback. Em Raciocínio de Trade-off (20%), as alternativas e rejeições explícitas de A (Kafka vs SQS, exatamente uma vez vs pelo menos uma vez, poliglota vs loja única, região única vs ativo-ativo) excedem a lista de trade-offs no estilo de mitigação de B. Em Escalabilidade/Confiabilidade (20%), A fornece mecanismos acionáveis (escalonamento KEDA por atraso do consumidor, compactação TWCS, chaves quentes salgadas, testes de caos, validação de carga 5x) onde B permanece mais genérico. O padrão outbox e o modelo de marcador de leitura de B são vantagens genuínas, mas não compensam a superioridade de A nos critérios de maior peso. O resultado ponderado favorece claramente A.

Modelos avaliadores Google Gemini 2.5 Pro

Motivo do vencedor

A resposta A vence por fornecer um design mais detalhado, polido e abrangente. Embora ambas as respostas proponham uma arquitetura semelhante e sólida, a Resposta A se destaca em várias áreas-chave. Ela começa com uma estimativa quantitativa completa da capacidade, que fundamenta todo o design em números concretos. Sua discussão sobre escalabilidade e confiabilidade é mais específica, mencionando ferramentas e práticas avançadas como KEDA para autoescalonamento e engenharia do caos. É excepcionalmente clara e bem estruturada, tornando a informação complexa fácil de digerir. Finalmente, ela vai além dos requisitos da solicitação, incluindo um plano de lançamento faseado e uma seção dedicada a métricas/SLO, demonstrando uma mentalidade mais completa e pronta para produção.

Modelos avaliadores OpenAI GPT-5.6

Motivo do vencedor

A Resposta A vence por uma margem ponderada estreita. Embora a Resposta B tenha uma história de confiabilidade de eventos de origem mais forte através do seu padrão explícito de outbox, a Resposta A é mais completa e operacionalmente concreta em termos de planeamento de capacidade, isolamento de prioridade, escalabilidade de WebSocket, configuração de datastore, controlos de latência, mecanismos de alta disponibilidade, mitigação de gargalos, rollout e SLOs. Essas vantagens são especialmente significativas em termos de completude e escalabilidade/confiabilidade, enquanto sua arquitetura permanece altamente sólida, apesar da falta de detalhes de publicação transacional.

X f L