
Todos nós amamos histórias. Gostamos de sentar perto do fogo e conversar sobre nossas vitórias passadas, batalhas ou apenas sobre nossa experiência de trabalho.
Hoje é um dia desses. E mesmo que você não esteja no incêndio agora, temos uma história para você. A história de como começamos a trabalhar com armazenamento em Tarantool.
Era uma vez em nossa empresa um par de "monólitos" e um "teto" para todos, ao qual esses monólitos estavam se aproximando lenta mas seguramente, limitando o vôo de nossa empresa, nosso desenvolvimento. E havia um entendimento inequívoco: um dia vamos atingir esse teto.
É agora que somos dominados pela ideologia de dividir tudo e todos, do equipamento à lógica do negócio. Como resultado, temos, por exemplo, dois DCs que são praticamente independentes no nível da rede. E então tudo ficou completamente diferente.
Hoje, existem várias ferramentas e ferramentas para fazer mudanças na forma de CI / CD, K8S, etc. Na época "monolítica", não precisávamos de tantas palavras estrangeiras. Bastava consertar o “armazenamento” do banco de dados.
Mas o tempo foi passando e o número de solicitações avançou com ele, às vezes disparando RPS além de nossas capacidades. Com a entrada no mercado dos países da CEI, a carga no processador de banco de dados do primeiro monólito não caiu abaixo de 90% e o RPS permaneceu no nível de 2.400. E esses não eram apenas pequenos seletores, mas pesquisas pesadas com um monte de verificações e JOINs que podiam ser executados quase metade dos dados no plano de fundo de um grande IO.
Quando as vendas completas da Black Friday começaram a aparecer no palco - e a Wildberries começou a considerá-los um dos primeiros na Rússia - a situação ficou completamente triste. Afinal, a carga nesses dias triplicou.
Oh, esses "tempos monolíticos"! Tenho certeza de que você também encontrou algo semelhante e ainda não consegue entender como isso pode acontecer com você.
O que você pode fazer - a moda é inerente à tecnologia. 5 anos atrás, tivemos que repensar um desses mods na forma de um site existente em .NET e MS SQL-server, que guardava cuidadosamente toda a lógica do próprio site. Ele o guardou com tanto cuidado que cortar tal monólito acabou sendo um prazer longo e bastante difícil.
Uma pequena digressão.
Em vários eventos, eu digo: "Se você não viu o monólito, então você não cresceu!" Estou interessado na sua opinião sobre o assunto, escreva, por favor, nos comentários.
Um som de trovão
Voltemos à nossa "fogueira". Para distribuir a carga de funcionalidade "monolítica", decidimos dividir o sistema em microsserviços baseados em tecnologias de código aberto. Porque, no mínimo, eles são mais baratos em escala. E o entendimento de que teríamos que escalar (e muito) era de 100%. Com efeito, já nessa altura acabou por entrar nos mercados dos países vizinhos, e o número de registos, bem como o número de encomendas, começou a crescer ainda mais.
Após analisarmos os primeiros requerentes de saída do monólito em microsserviços, percebemos que em 80% deles, 99% escrevem em sistemas de back office e lêem em sistemas front-end. Em primeiro lugar, trata-se de alguns subsistemas importantes para nós - dados do usuário e o sistema de cálculo do custo final das mercadorias com base em informações sobre descontos e cupons adicionais para clientes.
Recuar. Agora é assustador imaginar, mas além dos subsistemas acima mencionados, catálogos de produtos, uma cesta de usuário, um sistema de busca de produtos, um sistema de filtragem de catálogos de produtos e vários sistemas de recomendação também foram retirados de nosso monólito. Para a operação de cada um deles, existem classes separadas de sistemas estreitamente afiados, mas ao mesmo tempo todos viviam em uma "casinha".
Planejamos transferir dados sobre nossos clientes para um sistema fragmentado. A remoção da funcionalidade de cálculo do custo final das mercadorias exigia boa escalabilidade de leitura, pois criava a maior carga de RPS e era a mais difícil de implementar para o banco de dados (muitos dados estão envolvidos no processo de cálculo).
Como resultado, temos um esquema que funciona bem com Tarantool.
Naquela época, para a operação de microsserviços, foram escolhidos esquemas de trabalho com múltiplos data centers em máquinas virtuais e de hardware. Conforme mostrado nas figuras, as opções de replicação do Tarantool foram aplicadas nos modos mestre-mestre e mestre-escravo.

Arquitetura. Opção 1. Serviço de usuário
No momento, existem 24 shards, cada um dos quais com 2 instâncias (uma para cada DC), todos no modo mestre-mestre.
No topo do banco de dados estão os aplicativos que acessam réplicas do banco de dados. Os aplicativos funcionam com o Tarantool por meio de nossa biblioteca personalizada, que implementa a interface do driver Tarantool Go. Ela vê todas as réplicas e pode trabalhar com o mestre para ler e escrever. Na verdade, ele implementa o modelo de conjunto de réplicas, que adiciona a lógica para selecionar réplicas, realizar novas tentativas, um disjuntor e limite de taxa.
Ao mesmo tempo, é possível configurar a política de escolha de uma réplica no contexto de um shard. Por exemplo, roundrobin.

Arquitetura. Opção 2. Serviço de cálculo do custo final das mercadorias
Há vários meses, a maioria dos pedidos de cálculo do custo final das mercadorias foi para um novo serviço, que, em princípio, funciona sem bases de dados, mas há algum tempo era 100% processado pelo serviço com Tarantool sob o capô.
O banco de dados de serviço é composto por 4 mestres nos quais o sincronizador coleta dados e cada um desses mestres de replicação distribui dados para réplicas somente leitura. Cada mestre tem cerca de 15 dessas linhas.
Tanto no primeiro quanto no segundo esquema, se um DC estiver indisponível, o aplicativo pode receber dados no segundo.
Deve-se notar que a replicação no Tarantool é bastante flexível e configurável em tempo de execução. Em outros sistemas, houve dificuldades. Por exemplo, alterar os parâmetros max_wal_senders e max_replication_slots no PostgreSQL requer que o assistente seja reiniciado, o que em alguns casos pode levar a uma desconexão entre o aplicativo e o DBMS.
Procura e acharás!
Por que não o fizemos "como gente normal", mas escolhemos um caminho atípico? Depende do que é considerado normal. Muitas pessoas geralmente criam um cluster do Mongo e o distribuem por três DCs distribuídos geograficamente.
Naquela época, já tínhamos dois projetos no Redis. O primeiro é um cache e o segundo é um armazenamento persistente para dados não muito críticos. Foi muito difícil com ele, em parte por nossa culpa. Às vezes, volumes muito grandes estavam na chave, e de vez em quando o site parecia ruim. Usamos este sistema na versão mestre-escravo. E houve muitos casos em que algo aconteceu ao mestre e a replicação quebrou.
Ou seja, o Redis é bom para tarefas sem estado, não para tarefas com estado. Em princípio, permitia resolver a maioria dos problemas, mas apenas se fossem soluções de valor-chave com um par de índices. Mas, na época, o Redis estava muito triste com persistência e replicação. Além disso, houve reclamações sobre o desempenho.
Pensando em MySQL e PostgreSQL. Mas o primeiro, de alguma forma, não criou raízes conosco, e o segundo em si é um produto bastante sofisticado e seria inapropriado construir serviços simples sobre ele.
Tentamos RIAK, Cassandra e até um banco de dados gráfico. Todas essas são soluções de nicho que não se encaixam no papel de uma ferramenta universal geral para a criação de serviços.
Por fim, decidimos pelo Tarantool.
Entramos em contato com ele quando ele estava na versão 1.6. Estávamos interessados na simbiose de valor-chave e na funcionalidade de um banco de dados relacional. Existem índices secundários, transações e espaços, eles são como tabelas, mas não são simples, você pode armazenar um número diferente de colunas neles. Mas os recursos matadores do Tarantool eram índices secundários combinados com valor-chave e transacional.
A comunidade responsiva de língua russa também desempenhou um papel, pronta para ajudar no chat. Usamos isso ativamente e vivemos diretamente no chat. E não se esqueça de um persistente decente, sem erros óbvios e ombreiras. Se você olhar a nossa história com o Tarantool, tivemos muitas dores e falsificações com a replicação, mas nunca perdemos dados por culpa dele!
A implementação começou difícil
Naquela época, nossa pilha de desenvolvimento principal era .NET, para a qual não havia conector para Tarantool. Imediatamente começamos a fazer algo no Go. Lua também funcionou muito bem. O principal problema na época era com a depuração: no .NET tudo fica lindo com isso, e depois disso era difícil mergulhar no mundo da Lua embarcada, quando você, além dos logs, não tinha depuração, era difícil. Além disso, a replicação, por algum motivo, falhava periodicamente, então eu tive que me aprofundar na estrutura do motor Tarantool. O chat ajudou com isso, em menor medida - a documentação, às vezes olhava para o código. Naquela época, a documentação era mais ou menos.
Então, em poucos meses, consegui preencher as doses e obter resultados decentes com o Tarantool. Formalizamos os desenvolvimentos de referência em git, que ajudaram na formação de novos microsserviços. Por exemplo, quando surgiu a tarefa: criar outro microsserviço, o desenvolvedor olhou o código-fonte da solução de referência no repositório e não demorou mais de uma semana para criar um novo.
Esses foram tempos especiais. Convencionalmente, então era possível abordar o administrador da mesa ao lado e perguntar: "Dê-me uma máquina virtual." Trinta minutos depois, você já tinha o carro. Você mesmo se conectou, instalou tudo e recebeu tráfego.
Hoje não vai funcionar assim: você precisa encerrar o monitoramento, logar no serviço, cobrir a funcionalidade com testes, pedir uma máquina virtual ou entregar ao Kuber, etc. Em geral, será melhor, embora mais longo e mais problemático.
Dividir para reinar. E quanto a Lua?
Havia um sério dilema: algumas equipes não conseguiam implementar mudanças de maneira confiável em um serviço com muita lógica Lua. Muitas vezes, isso era acompanhado pela inoperância do serviço.
Ou seja, os desenvolvedores estão preparando algum tipo de mudança. Tarantool inicia a migração, e a réplica ainda possui o código antigo; algum DDL, outra coisa, chega lá por replicação, e o código simplesmente se desfaz, porque não é levado em conta. Como resultado, o procedimento de atualização para os administradores foi agendado na folha A4: interromper a replicação, atualizá-la, habilitar a replicação, desligá-la aqui, atualizar ali. Pesadelo!
Como resultado, agora, na maioria das vezes, tentamos não fazer nada em Lua. Basta usar o iproto (um protocolo binário para comunicação com o servidor) e pronto. Talvez seja falta de conhecimento dos desenvolvedores, mas desse ponto de vista o sistema é complexo.
Nem sempre seguimos cegamente esse cenário. Hoje não temos preto e branco: ou está tudo em Lua ou está em Go. Já sabemos como você pode combiná-los para não ter problemas com a migração mais tarde.
Onde está Tarantool agora?
O Tarantool é utilizado no serviço para o cálculo do custo final da mercadoria, tendo em conta os cupões de desconto, também conhecido como “Promotor”. Como disse antes, agora está se aposentando: está sendo substituído por um novo serviço de catálogo com preços pré-calculados, mas há seis meses todos os cálculos eram feitos na Promotora. Anteriormente, metade de sua lógica era escrita em Lua. Há dois anos, foi feito um storage a partir do serviço, e a lógica foi reescrita para Go, pois a mecânica dos descontos mudou um pouco e o serviço faltou desempenho.
Um dos serviços mais críticos é o perfil do usuário. Ou seja, todos os usuários do Wildberries estão armazenados no Tarantool, e há cerca de 50 milhões deles. Um sistema fragmentado por ID do usuário, distribuído em vários DCs com uma conexão aos serviços Go.
Segundo a RPS, a "Promotora" já foi a líder, chegando a 6 mil solicitações. A certa altura, tínhamos de 50 a 60 cópias. Já o líder em RPS são os perfis de usuários, cerca de 12 mil, serviço que usa sharding personalizado com divisão por faixas de IDs de usuários. O serviço atende mais de 20 máquinas, mas é demais, planejamos reduzir os recursos alocados, pois a capacidade de 4 a 5 máquinas é suficiente para isso.
O serviço de sessão é nosso primeiro serviço em vshard e Cartridge. Configurar o vshard e atualizar o Cartridge exigiu algum trabalho de nossa parte, mas no final tudo deu certo.
O serviço de apresentação de diferentes banners no site e na aplicação mobile foi um dos primeiros a ser disponibilizado diretamente no Tarantool. Este serviço é notável pelo fato de ter de 6 a 7 anos, ainda estar em serviço e nunca ter reiniciado. A replicação foi mestre-mestre. Nunca quebrou nada.
Há um exemplo de uso do Tarantool para funcionalidade de referência rápida em um sistema de depósito para verificar rapidamente as informações em alguns casos. Tentamos usar o Redis para isso, mas os dados na memória ocuparam mais espaço do que o Tarantool.
Serviços de lista de espera, assinaturas de clientes, histórias da moda e produtos armazenados também funcionam com Tarantool. O último serviço na memória é de cerca de 120 GB. Este é o serviço mais abrangente dos itens acima.
Conclusão
Índices secundários combinados com valor-chave e propriedades transacionais tornam o Tarantool excelente para arquiteturas de microsserviço. No entanto, encontramos dificuldades ao implantar mudanças em serviços com muita lógica em Lua - os serviços frequentemente paravam de funcionar. Não poderíamos derrotar isso e, com o tempo, chegamos a diferentes combinações de Lua e Go: sabemos onde usar uma linguagem e onde usar outra.
O que mais ler sobre o assunto
- Criamos do zero um aplicativo altamente carregado em Tarantool habr.com/ru/company/mailru/blog/510440
- A escolha do líder confiável em Tarantool Cartridge habr.com/ru/company/mailru/blog/513912
- Canal Tarantool Telegram com novidades sobre produtos t.me/tarantool_news
- Discuta Tarantool no chat da comunidade t.me/tarantoolru