O efeito negligenciado do tamanho dos dados

Agradecimentos especiais a Özgür Karakaya com quem co-escrevi esta postagem no blog.
PDP (Product Detail Page) na Trendyol é uma equipe responsável por fornecer informações sobre produtos específicos. Essas informações incluem preço, tamanho, cor ou outras informações relevantes de um produto que os clientes desejam saber antes de fazer uma compra. As informações podem ser apresentadas de duas maneiras diferentes, a primeira é na forma de uma única página de detalhes do produto, principalmente quando você clica em um produto e é navegado para uma página onde exibe as informações específicas do produto. A equipe também é responsável por fornecer dados detalhados do produto para outras visualizações no site, ou seja, sua lista de favoritos, produtos recomendados e finalização da compra. Ambas as formas foram tratadas por um serviço chamado Product Detail API apoiado pelo banco de dados NoSQL couchbase que atende solicitações únicas (página de detalhes de produto único), bem como solicitações em massa (lista de favoritos, etc.).
Antes do evento Black Friday de 2021, a equipe havia definido uma meta de lidar com 8 milhões de solicitações por minuto para solicitações em massa. Um problema era que o serviço funcionava mal na forma de aumento do uso de memória e um número considerável de erros quando recebia alta taxa de transferência. O desafio era descobrir uma maneira de otimizar o serviço.
Então aqui está a jornada.
Brainstorming & Análise
Durante a fase de análise, a equipe PDP descobriu que — em termos de domínio — os clientes que enviam solicitações em massa não usam todos os campos do documento do banco de dados. O primeiro ponto de otimização foi introduzir um novo modelo de documento sem aqueles campos desnecessários. As vantagens desta reestruturação são:
- otimização de tamanho de dados
- foco de domínio de conteúdo em massa
Abaixo está um diagrama da arquitetura existente e nova na época:

As caixas externas pontilhadas definem os limites de um serviço. Observe que nesta primeira ilustração, um serviço era responsável por lidar com todo o pipeline de operações. A otimização em si era dividir o pipeline de conversão em dois processos separados, conforme visto na segunda arquitetura: o processo de conversão de tempo de índice e o serviço de conteúdo em massa. O último é um serviço de leitura que lida com solicitações HTTP. O primeiro acabou sendo um processo orientado a eventos que lê dados do ProductContent CB (Source Of Truth), processa parcialmente e os armazena em um Counchbase Bucket para o último usar. Mas mais sobre isso mais tarde.
Sob a nova arquitetura proposta, o principal ganho é que o processo de execução será otimizado, o que por sua vez reduz o tempo de resposta do serviço.
Neste blog, vamos nos concentrar no processo de indexação. Para obter mais informações sobre o “Bulk Content Service”, consulte esta postagem de Enes Turgut
PS Estou usando o termo tempo de execução aqui de forma bastante vaga. No contexto deste blog, estamos definindo tempo de execução como a sequência de operações acionadas por uma solicitação HTTP recebida.
Soluções existentes
Até agora, a equipe havia se decidido pela nova arquitetura. Agora é hora de implementá-lo. O que estamos tentando alcançar é uma solução de conversão de couchbase para couchbase quase em tempo real. O Couchbase já oferece replicação em todo o cluster, no entanto, a forma dos dados precisa ser alterada, então a replicação normal obviamente não é o caminho a seguir. Não seria incrível se houvesse uma maneira de manipular os dados durante o processo de replicação? enquanto vasculhávamos a documentação do Couchbase, nos deparamos com o Couchbase Eventing Service. Acabamos de ganhar o jackpot?
Couchbase Eventos:
O Couchbase Eventing Service é uma estrutura para operar em mudanças de dados em tempo real. Eventos são mudanças nos dados no cluster Couchbase
O Couchbase Eventing pareceu ser uma boa opção para nós porque você pode :
- Propagar alterações para outros sistemas
- Enriqueça um documento em tempo real
- Não requer manutenção
Sem jackpot.
Nossa solução (conversor de dados de conteúdo em massa)
Inspirado pelo CB Elasticsearch Connector .
O Couchbase Elasticsearch Connector replica seus documentos do Couchbase Server para o Elasticsearch quase em tempo real. O conector usa o Database Change Protocol (DCP) de alto desempenho para receber notificações quando os documentos são alterados no Couchbase.
Substitua o Elasticsearch pelo Couchbase e você terá exatamente o que precisamos. Tudo o que precisamos fazer é aceitar as mutações do documento (atualizações) da fila do DCP, processá-las e gravá-las em um novo depósito.
Tenha em mente que a solução precisa ser capaz de:
- Escale conforme o número de documentos e mutações cresce.
- Execute o processo de ponta a ponta (aceitando mensagens da fila, convertendo o modelo de dados e gravando-o em um novo bucket) quase em tempo real.
- Manipule os documentos existentes e não apenas os novos quando novos campos forem adicionados ou removidos do modelo de documento.
- Forneça um mecanismo para regenerar documentos desde o início em caso de corrupção grave de dados.
Bem, há muito o que descompactar aqui, vamos resumir primeiro:

A arquitetura acima mostra a solução final. Por fim, o Product Detail Service lidará com solicitações únicas, enquanto o Bulk Content Service lidará com solicitações em massa. Os serviços são apoiados por diferentes fontes couchbase. Enquanto ProductContent CB representa a Fonte da Verdade, ProductSummary CB representa uma versão pré-processada resumida dela. A conversão será processada no Bulk Content Data Converter quase em tempo real, aceitando mutações de documento da fila DCP. Cada mutação de documento representa o estado do documento após a atualização. Esta é outra maneira de dizer que o conversor obterá automaticamente todo o documento atualizado do ProductContent CBem cada atualização.
Para entender como esta solução escala exatamente, deve-se dar uma olhada no arquivo de configuração do conversor:
{
"group": {
"name": "v20220909",
"membership": {
"memberNumber": 1,
"totalMembers": n
},
"limit": {
"bytes": "5m",
"actions": 100,
"timeout": "1m",
"concurrency": 2
},
"version": 1,
"ignoreDelete": false
},
"dcp": {
"sourceConfig": {
"hosts": ["source_couchbase_host"],
"network": "auto",
"bucket": "bucketName",
"metadataBucket": "metadataBucketName",
"scope": "",
"collections": [],
"compression": true,
"flowControlBuffer": "16m",
"persistencePollingInterval": "100ms"
},
"targetConfig": {
"hosts": ["taget_couchbase_host"],
"bucket": "targetBucketName"
}
}
}
Outra parte importante da configuração é group.name , que representa o grupo de consumidores onde um deslocamento é armazenado, pense como os grupos de consumidores Kafka. A alteração dessa configuração redefine o índice de deslocamento, o que significa que todo o estado do banco de dados será enviado por meio da fila DCP. Isso é especialmente prático quando é necessária uma atualização no modelo de documento, adicionar ou remover novos campos do bloco de destino requer uma atualização completa em todos os documentos armazenados, isso inclui os documentos que de outra forma não seriam normalmente atualizados no bloco de destino simplesmente porque nenhuma mutação teria ocorrido nos documentos de origem. Ele também pode funcionar como um mecanismo completo de regeneração do banco de dados em caso de corrupção grave de dados.
Para obter mais informações sobre a configuração, consulte o link da documentação .
Outro ponto de otimização foi reduzir o tamanho do documento de destino com abreviações ou caracteres únicos como nós JSON. Especialistas de domínio e PMs não devem entender este documento e é por isso que podemos nos safar com esses hacks malignos. Segue abaixo um exemplo do documento:

E a partir de 31 de outubro, o resultado é…

Este hack simples nos permitiu reduzir o tamanho do Bucket de 2,92 TB para 595 GB! Uma redução de tamanho impressionante de aproximadamente 80%!
Deve-se ter notado também que a contagem de documentos no banco de dados de destino é reduzida em aproximadamente 12 milhões. O motivo é que excluímos produtos fora de estoque que não foram atualizados por 12 meses. No balde de origem, ainda podemos precisar desses documentos, mas não faria sentido tê-los aqui, daí a diferença de contagem.
Monitoramento de desempenho
Até agora, temos a solução instalada e funcionando. Mas como exatamente podemos saber se o desempenho é suficiente? e se ele estiver, hipoteticamente, fazendo a conversão, mas atrasado, digamos, 2 segundos? Isso seria um desastre total! essa consistência eventual vazará até a interface do usuário do site e afetará gravemente a experiência do usuário. Acionar manualmente uma alteração no banco de dados de produção não é possível e não deve ser a maneira de testar em primeiro lugar. Uma abordagem sistemática é necessária para identificar a origem do atraso, caso ocorra. O atraso está acontecendo devido ao congestionamento na fila do DCP? Ou é porque o conversor tem algum tipo de bug?
Para responder a essas perguntas, introduzimos três métricas, conforme mostrado na figura abaixo:

O documento de origem contém o timestamp da última atualização, chamamos isso de LMD (Last Modified Date). Depois que uma mutação é recebida pelo conversor, o tempo “ Wing in DCP queue” pode ser calculado facilmente subtraindo LMD de time.now() . As métricas são expostas ao Prometheus, conforme mostrado nos gráficos abaixo:

Limitações:
Lembra quando eu disse anteriormente que escalar é apenas uma questão de aumentar o número total de conversores? Bem, isso vem com uma pequena ressalva: a configuração da solução é um pouco conectada à infraestrutura com a qual ela se comunica; O rebalanceamento automático do consumidor na fila do DCP e a distribuição dos consumidores nos vBuckets são conectados à configuração estática que o consumidor tem na inicialização. Para alterar isso, é necessário alterar o próprio arquivo de configuração, o que requer uma nova implantação. Além disso, cada consumidor precisa de seu próprio arquivo de configuração estática, o que significa que cada consumidor precisa — na terminologia do Kubernetes — ter seus próprios arquivos de recursos de implantação. Na Trendyol, usamos amplamente o ArgoCD com o Kustomize para gerenciar nossas implantações, dimensionar a solução requer adicionar - na terminologia ArgoCD - um novo ApplicationSet.
No entanto, até agora, nossa escala de 8 consumidores está lidando com a grande carga com eficiência quase em tempo real. Mas, à medida que o balde de origem aumenta e o número de mutações cresce, é necessário reconfigurar manualmente os consumidores.
Uso interno de outras equipes
Sentindo-se confiante com o conector CB-to-CB desenvolvido, a PDP Team decidiu apresentá-lo a outras equipes da Trendyol. A equipe recebeu muitos comentários positivos e, antes que percebêssemos, a equipe de pesquisa decidiu bifurcar o projeto e usá-lo em seu domínio, onde armazena eventos de domínio em um banco de dados couchbase e usa o conector CB-to-CB para gerar outro modelo de documento para uso posterior. Essa adoção de soluções desenvolvidas internamente inspira a todos nós a seguir em frente e continuar melhorando e refinando nosso uso da tecnologia.
Candidate-se às nossas vagas abertas aqui !