Design clássico · Staff
Agregação de cliques em anúncios
Projeto de entrevista clássico. Pontos centrais: semântica de processamento de streams e ledger e conciliação.
Resposta curta
Projeto de entrevista clássico. Pontos centrais: semântica de processamento de streams e ledger e conciliação. Um agregador de cliques em anúncios coleta cada clique que usuários dão em anúncios de um site ou app e entrega aos anunciantes métricas de desempenho.
01 · CONTEXTO E REQUISITOS
O que um agregador de cliques precisa fazer
Um agregador de cliques em anúncios coleta cada clique que usuários dão em anúncios de um site ou app e entrega aos anunciantes métricas de desempenho. São dois requisitos funcionais: levar o usuário ao site do anunciante e permitir que o anunciante consulte os cliques ao longo do tempo, com granularidade mínima de um minuto. É um bom exercício de arquitetura de análise em tempo real.
A escala de referência é de dez milhões de anúncios ativos e pico de dez mil cliques por segundo. Os requisitos não funcionais que mandam no desenho são: consultas do anunciante com baixa latência, coleta tolerante a falhas que não perca cliques, dados o mais próximos possível do tempo real e contagem idempotente, que não conte o mesmo clique duas vezes.
02 · O CAMINHO DO CLIQUE
Redirecionar e registrar, a versão ingênua
Há duas formas de redirecionar. No cliente, o anúncio já traz a URL de destino e o navegador navega sozinho, enquanto uma chamada paralela registra o clique; usuários sofisticados podem copiar a URL e ir direto, sem passar pelo rastreamento. No servidor, o clique chega ao nosso endpoint, que registra o evento e responde com um redirect 302 para o anunciante; é um pouco mais complexo, mas garante que todo clique seja visto.
O usuário clica no anúncio e o navegador chama o serviço de cliques, que vai registrar o evento e responder com o redirecionamento.
O serviço grava cada clique como uma linha no banco único de eventos.
Quando o anunciante pede métricas, o mesmo banco precisa agrupar todas as linhas do período, e é aí que a conta deixa de fechar.
Na escala de pico, esse desenho não aguenta: o banco vira gargalo e a consulta com agrupamento fica lenta, quebrando o requisito de baixa latência. A saída é separar quem recebe os cliques de quem responde às consultas e fazer a agregação antes que o anunciante pergunte.
03 · DO LOTE AO STREAM
Agregar em lote e por que não basta
A melhoria natural grava os eventos brutos num event store otimizado para escrita e deixa um job periódico, por exemplo em Spark, ler esses eventos e gravar contagens por anúncio e por minuto num banco analítico. Bancos OLAP usam armazenamento colunar, muito rápido para agregações. Um banco de séries temporais serviria para consultas simples, mas com milhões de anúncios e várias dimensões de análise o OLAP atende melhor.
O clique passa pelo balanceador e chega a uma instância do processador de cliques.
O processador grava o evento bruto no event store, que absorve muitas escritas.
A cada intervalo, o job de agregação lê os eventos acumulados no event store.
O job agrega por anúncio e por minuto e grava as contagens no banco analítico.
O anunciante consulta só o banco analítico, que já tem tudo agregado, e a resposta é rápida.
O custo é o atraso: o anunciante sempre enxerga dados com alguns minutos de idade. E numa rajada de cliques o próximo lote processa uma janela muito maior e pode demorar mais que o próprio intervalo, atrasando justo quando o anunciante mais quer olhar. Uma fila antes do event store ajudaria com os picos de escrita, mas não resolveria a latência.
04 · TEMPO REAL
Stream, janelas e o tempo do evento
A alternativa processa os eventos conforme chegam, em vez de esperar um job em lote: o processador de cliques escreve numa stream, como Kafka ou Kinesis, e um stream processor, como Flink, agrega em memória e grava o resultado no banco analítico. O Flink agrega pelo tempo do evento, quando o clique de fato ocorreu, e não pelo tempo em que o evento chegou, para acertar a contagem mesmo com eventos fora de ordem. Os watermarks acompanham o progresso do tempo do evento e tratam os eventos atrasados.
Ao exibir o anúncio, o serviço de posicionamento entrega ao navegador um ID de impressão assinado, único para aquela exibição.
No clique, o navegador envia o ID ao processador de cliques, que verifica a assinatura e consulta o cache de impressões.
Se o clique é novo, o processador o grava na stream primeiro e só então registra o ID no cache; depois responde com o redirecionamento.
O agregador de stream lê a stream, agrega por anúncio e por minuto e grava no banco analítico.
Em paralelo, a stream arquiva os eventos brutos no data lake, e a reconciliação em lote os reagrega e corrige o banco analítico se houver divergência.
O anunciante consulta o banco analítico e vê as métricas quase em tempo real.
A janela pode fechar no limite do minuto enquanto os resultados parciais são descarregados a cada poucos segundos, então o minuto corrente aparece incompleto, como esperado, até acabar. Também é bem mais realista reduzir a janela do Flink do que aumentar a frequência de jobs Spark, por causa do overhead de cada job.
Um consumidor Kafka comum, com contador em memória, até funciona num cenário mais simples. Mas janelas por tempo do evento, watermarks e recuperação de estado construídos à mão sobre ele são viáveis e propensos a erro, e é isso que o stream processor entrega pronto.
05 · ESCALA E PARTIÇÕES QUENTES
Escalar cada etapa e o anúncio viral
Cada etapa escala horizontalmente: o processador atrás do balanceador, a stream por partições e o agregador com uma tarefa por partição. Particionar a stream por identificador do anúncio, o AdId, é natural, pois as partições ficam independentes e todos os eventos de um anúncio caem na mesma.
O problema que sobra é a partição quente: um anúncio que acabou de viralizar manda todos os cliques para uma só partição, que se sobrecarrega, aumenta a latência e, no pior caso, perde dados. Uma saída é anexar um número aleatório ao AdId na chave de partição, só para os anúncios populares, de modo que o tráfego se espalhe. Ao gravar no OLAP, o Flink remove o sufixo e usa o AdId original, e as somas das partições se combinam corretamente.
06 · NÃO PERDER, NÃO CONTAR EM DOBRO
Retenção, reconciliação e idempotência
Perder cliques é perder dinheiro, e a stream ajuda: por padrão ela é distribuída e tolerante a falhas, e pode reter os dados por um período mesmo depois de consumidos. Se o agregador cai, ele volta e relê a stream. Como as janelas são curtas, a queda custa no máximo um minuto de agregação, que se reprocessa a partir de um ponto conhecido da stream.
O agregador de stream cai no meio do minuto, e o banco analítico para de receber contagens novas.
Os cliques continuam chegando à stream, que os retém mesmo sem ninguém consumindo.
O agregador volta, relê a stream a partir de um ponto conhecido e reagrega o que faltava, e o banco analítico se atualiza.
Mesmo assim, falhas de processamento, código com defeito ou eventos fora de ordem podem deixar pequenas imprecisões. Por isso os eventos brutos também vão para um data lake e um job periódico, por exemplo diário, em Spark, os reagrega. Se o resultado diverge do que a stream produziu, investiga-se a causa e corrige-se o banco analítico. Combinar uma camada rápida (Flink) com uma camada de lote (Spark) para correção é a arquitetura Lambda.
Para não contar o mesmo clique duas vezes, contar um clique por usuário e anúncio erra no retargeting, em que o mesmo anúncio é mostrado ao mesmo usuário várias vezes de propósito. O certo é contar um clique por instância de anúncio: o serviço de posicionamento gera um ID de impressão único a cada exibição, e o clique carrega esse ID como chave de idempotência.
A deduplicação precisa ocorrer antes da stream, porque um duplicado em lados opostos do limite do minuto contaria como dois cliques se a deduplicação ficasse dentro do agregador. Para impedir cliques forjados com IDs inventados, o ID é assinado com HMAC e chave secreta antes de ir ao navegador, e o processador verifica a assinatura.
Perguntas frequentes
O que é Agregação de cliques em anúncios?
Projeto de entrevista clássico. Pontos centrais: semântica de processamento de streams e ledger e conciliação.
O que o design de Agregação de cliques em anúncios cobre?
Ele está organizado nestas seções: O que um agregador de cliques precisa fazer, Redirecionar e registrar, a versão ingênua, Agregar em lote e por que não basta, Stream, janelas e o tempo do evento, Escalar cada etapa e o anúncio viral e Retenção, reconciliação e idempotência.
Quanto tempo leva Agregação de cliques em anúncios e qual é o nível?
Leva cerca de 17 minutos para ler e tem nível Staff.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.