Como combinar duas plataformas em uma e não ofender os usuários. Experiência dos desenvolvedores do Yandex.Kew



No ano passado, o serviço TheQuestion ingressou na Yandex. Naquela época, já havia um serviço semelhante de perguntas e respostas - Yandex.Znatoki. Os conhecedores tinham um grande público e muitas perguntas interessantes, mas não havia especialistas suficientes que pudessem dar respostas de alta qualidade a essas perguntas. A pergunta, pelo contrário, tinha uma forte comunidade de especialistas, mas faltava perguntas interessantes. O passo lógico foi combinar os dois serviços para obter o melhor de cada um deles. Mas como fazer isso se cada serviço tiver sua própria base tecnológica, conteúdo e usuários?

Hoje vou falar sobre como nossa equipe resolveu esse problema do ponto de vista tecnológico. Você descobrirá quais opções de combinação nós consideramos e quais no final foram escolhidas. Vou falar sobre a "API de troca", migração de banco de dados, perfis de pool e teste de back-end. E ainda - sobre a noite da mudança sem o direito de cometer um erro. Você verá que não precisamos ficar entediados.

A tarefa de mesclar dois serviços em um não é nova, mas isso não facilita. A história conhece muitos exemplos bem-sucedidos (e nem tanto) de integração, mas, infelizmente, não existe uma “bala de prata” e uma instrução clara “faça isso e tudo dará certo”. Tudo depende muito das especificidades dos serviços que estão sendo combinados e do resultado desejado.

No nosso caso, o objetivo era o seguinte: que todo o conteúdo já escrito em cada um dos sites estivesse disponível em um serviço unificado e que seus autores pudessem gerenciá-lo.

Então, como você combina os dois serviços de perguntas e respostas, que parecem tão semelhantes, mas muito diferentes em essência? A transferência de conteúdo e usuários de um serviço para outro é muito semelhante à mudança de um apartamento antigo para outro.

Somente no nosso caso, o usuário pode viver simultaneamente em dois apartamentos (especialistas e TheQuestion), e você precisa transportá-lo cuidadosamente para o terceiro. Você precisa mover todos os móveis, plantas, um gato e até papel de parede para um novo apartamento (ou seja, perguntas, respostas, comentários, curtidas) e depois convidá-lo a se mudar.

Como fazer isso? Várias opções vêm imediatamente à mente.

Opção 1. Muito ruim.
Vamos pegar um dos serviços, transferir todo o conteúdo para outro (embora até isso não seja mais fácil) e fechar o serviço original.

Esta opção é muito ruim do ponto de vista do usuário do primeiro serviço. Acabamos de demolir sua casa antiga e o forçamos a mudar para uma nova. Qualquer pessoa não vai gostar dessa atitude e, em vez de se mexer, pode simplesmente entrar no pôr do sol. Para nós, o principal valor é a comunidade de usuários, portanto, não planejamos ofender ninguém. E corajosamente mudou para outras opções.

Opção 2. Ruim
Não vamos alterar nenhum dos serviços, em vez disso, inicie um novo e integrado, e adicionaremos periodicamente conteúdo dos outros dois (por exemplo, uma vez por dia).

Nesse caso, não parecemos piorar o usuário, mas também não melhoramos. Seu antigo apartamento permanece inalterado, mas não faz sentido mudar para um novo. Todos os vizinhos também moram em uma casa antiga; uma flor recém-comprada será transportada para um novo apartamento somente após um dia. Esse serviço unificado não tem chance de se tornar um novo lar.

Opção 3. Bom, mas difícil
: não fechemos nenhum dos serviços, duplicaremos instantaneamente o conteúdo e os perfis no serviço integrado e as pessoas se moverão com o tempo.

Todos os vizinhos do usuário (e até o gato) vivem simultaneamente na casa antiga e na nova. Uma flor que você acabou de comprar aparece instantaneamente em um apartamento novo. Exatamente o que é necessário! Esta opção é a mais confortável do ponto de vista do usuário. Portanto, nós escolhemos.

Comece a se mover


O que fizemos eventualmente pode ser descrito em várias frases. Repetimos completamente todo o back-end da API TheQuestion com base no back-end dos conhecedores, obtendo assim um único back-end que pode funcionar com dois (e até três) sites ao mesmo tempo. Ao mesmo tempo, o front end do TheQuestion permaneceu praticamente inalterado, o que significa que, do ponto de vista dos usuários, o próprio site praticamente não mudou. Este projeto recebeu o nome interno "API de troca". Mas as primeiras coisas primeiro.

O que tínhamos na entrada: dois locais completamente independentes. Os especialistas vivem nas nuvens internas de Yandex. O back-end dos conhecedores é escrito em Python. O TheQuestion vive nas nuvens do Microsoft Azure, o back-end do TheQuestion está escrito em Go. Os serviços têm um esquema de armazenamento de dados completamente diferente nos bancos de dados. Além disso, o TheQuestion possui dois aplicativos móveis (para Android e iOS), que também precisam ser suportados. Em geral, o inimigo não quer unir esse zoológico.



Etapa 0. Dirija-se à nuvem Yandex


Estritamente falando, essa etapa não é necessária para a "API do plug-in", mas simplifica significativamente as próximas etapas. Nesse estágio, abandonamos completamente o armazenamento e as instalações externas. A Pergunta começou a usar servidores DNS Yandex. Os serviços rentme foram transferidos para o Yandex.Cloud. O banco de dados foi transferido para os bancos de dados gerenciados Yandex. Durante a mudança, também conseguimos encontrar e corrigir vários erros no TheQuestion, por exemplo, conexões não fechadas com o Redis no código de 2015. Como bônus, também recebemos energia adicional para o TheQuestion.

Etapa 1. Migração de Dados


Independentemente de qual opção você deseja combinar serviços, teríamos que combinar os dados em qualquer caso. Para um único banco de dados, eles decidiram usar o PostgreSQL - esse DBMS já foi usado no Experts e no TheQuestion. Para não complicar o projeto, eles não começaram a criar uma terceira base para o serviço integrado, mas simplesmente pegaram o banco de dados Znatokov e o expandiram para poder aceitar todos os dados do TheQuestion. Este foi o primeiro grande desafio tecnológico.

Cada entrada em cada tabela do banco de dados TheQuestion precisava ser convertida e colocada no banco de dados do Expert. Então - correlacione cada coluna de uma e de outra base. Muitos campos tiveram que ser convertidos de maneira não trivial de um formato para outro. Portanto, uma grande subtarefa separada foi a conversão do formato de armazenamento de texto (o formato real da pergunta ou resposta) de QML (TheQuestion) para Markdown (Experts).

Configuramos um processo regular (várias vezes ao dia) de transferência de novos dados de um banco de dados para outro, mas, ao mesmo tempo, garantimos que os dados do TheQuestion não fossem exibidos em nenhum lugar até a conclusão do próximo estágio. Como "várias vezes ao dia" está longe de ser prometido "instantaneamente", e os dados podem estar em um estado inconsistente com os mesmos dados no TheQuestion, o que enganaria os usuários. Então, por que começamos com a migração de dados se o back-end ainda não estava pronto?

Primeiro, dessa maneira, estabilizamos o processo. Em segundo lugar, eles reduziram a quantidade de novos dados que precisarão ser transferidos no futuro, e isso é importante, pois todo o conteúdo importado teve que ser direcionado pela marcação para qualidade, conteúdo inapropriado, spam, fraude.



Etapa 2. "Trocando API"


Então, resolvemos o primeiro dos problemas - aprendemos a pegar o conteúdo do banco de dados TheQuestion e até exibi-lo, se desejar. Agora era necessário fazer com que esse conteúdo chegasse ao banco de dados integrado instantaneamente, e não várias vezes ao dia.

Para fazer isso, foi necessário reescrever todo o back-end do TheQuestion com toda a lógica necessária. O nome do projeto, "Swap API", estritamente falando, não reflete completamente a essência. Seria mais correto chamá-lo de "Trocar back-end". O fato é que, além da implementação direta de todas as "canetas" necessárias para o funcionamento do front end TheQuestion, outras possibilidades precisavam ser realizadas. Enfrentamos várias tarefas importantes.

AutorizaçãoO Yandex possui um sistema centralizado de autorização de usuário - Yandex.Passport. E os conhecedores, é claro, usaram o passaporte. Para fazer login, você deve ter uma conta no Yandex. Esse foi o problema. Nem todos os usuários do TheQuestion efetuaram login no site através do Yandex (embora houvesse essa oportunidade). Muitos usuários não tinham um login no Yandex e passaram pelas redes sociais (VKontakte, Facebook ...). Naturalmente, tivemos que manter essa funcionalidade ao nos mover. Portanto, implementamos a autorização "sem passaporte".

Pesquisa no site.TheQuestion implementou uma busca por perguntas, respostas, usuários e tópicos. Para a pesquisa, foi usada uma solução Sphinx de terceiros. Obviamente, se estamos falando de um único serviço, a pesquisa deve ser a mesma, ou seja, não pode funcionar em dois sistemas ao mesmo tempo. Assim, o Sphinx foi abandonado em favor de um mecanismo de pesquisa interno com suporte para a funcionalidade e indexação necessárias de todo o conteúdo do TheQuestion.

Envio de páginas em Zen e Turbo . No momento da adesão, o TheQuestion já usava as tecnologias Yandex. Páginas Turbo foram suportadas, conteúdo interessante caiu no feed Zen. Tudo isso também teve que ser suportado na "API de substituição".

Notificações no serviço e aplicativos, listas de discussão.Tudo relacionado à notificação de usuários: assinaturas, boletins com conteúdo interessante, informações sobre curtidas e comentários, muito mais. Tudo isso teve que ser cuidadosamente transferido e não esquecido.

Sistema de Administração do Site . Este parágrafo refere-se a tudo relacionado ao gerenciamento interno do serviço: moderação, análise e assim por diante.

Sistema de classificação de usuário unificado.Essa tarefa não era técnica, mas lógica. Formalmente, não é necessário desenvolver um sistema de classificação unificado para uma "API de troca", mas esse sistema ainda é necessário para o futuro serviço integrado. Nos dois sites, os usuários foram classificados pela quantidade e qualidade do conteúdo criado. Os detalhes da classificação não foram divulgados, mas quanto mais frequentemente e melhor você responder às perguntas, maior será sua classificação. Os princípios de classificação eram os mesmos nos dois serviços, mas a fórmula em si e os fatores eram muito diferentes. Era necessário não apenas comparar correta e honestamente os usuários de Znatokov e TheQuestion entre si, mas também aprender a considerar uma classificação única para os especialistas que escreveram em dois serviços ao mesmo tempo.

E reescreva todas as APIs.Goste ou não, essa tarefa foi a mais importante e difícil. Muitos processos nos serviços foram semelhantes, então os tiramos dos especialistas e não escrevemos do zero. Mas havia também muitas coisas novas, por exemplo, linhas diretas de usuário ou respostas preliminares. Como resultado, reescrevemos mais de 100 "canetas" na "API de troca" e implementamos mais de 50 recursos REST.

Depois de implementarmos toda a funcionalidade descrita acima, foi possível começar a mover. Mas antes de fazermos um truque.

É claro que antes de alternar e lançar a "API de troca" na produção, ela precisava ser testada muito bem. Antes de tudo, era necessário testá-lo funcionalmente, ou seja, verificar diretamente o desempenho de todo o site na nova API. Em segundo lugar, é estressante. Nós queríamos ter 100% de certeza de que nosso design não ficaria "sob carga". Naturalmente, realizamos regularmente “queima de carga”, o que mostra que temos um bom suprimento de desempenho. Mas em questões de desempenho de serviço, é sempre melhor jogar com segurança. Qualquer teste de carga sintético, mesmo o melhor, é de alguma forma diferente da carga de produção. Portanto, decidimos, antes de mudar a API, preencher a carga de produção em nosso estande.

Para fazer isso, no front end do TheQuestion, implementamos a duplicação de todas as solicitações GET (ou seja, solicitações de dados, não modificações) em duas APIs ao mesmo tempo: a "antiga API" TheQuestion, que na época era a principal, e a "API de troca" secundária. Ao mesmo tempo, o front-end não esperou por uma resposta menor da API e não tratou de erros, mas dessa maneira conseguimos testar o back-end em usuários reais.



Etapa 3. E então nos lembramos das aplicações


Não, é claro, lembramos deles o tempo todo, mas enfrentamos um problema. Quem trabalhou com aplicativos móveis sabe que o problema com eles é muito mais do que com o site. Isso se deve principalmente à distribuição de novas versões.

Primeiro, você precisa trabalhar com serviços externos da App Store e do Google Play e aguardar a aprovação das novas versões (e às vezes a verificação pode levar um tempo considerável). Em segundo lugar, mesmo se seu aplicativo já passou no teste e apareceu na loja, isso não significa que os usuários farão uma atualização.

No caso do frontend do site, os próprios desenvolvedores controlam quando uma nova versão é lançada e sabem com certeza que depois disso todos os usuários receberão uma versão atualizada do site. No caso de aplicações, não existe essa garantia. Para obter essa garantia, eles costumam usar a "atualização forçada" do aplicativo. Poucas pessoas adoram esse método e, é claro, sempre, se possível, você precisa manter a compatibilidade com versões anteriores entre o aplicativo e o back-end. Portanto, seguimos o caminho de fazer alterações precisamente no lado de back-end com alterações mínimas no front-end em aplicativos. Mas, como muitas vezes acontece, o plano é confrontado com uma dura realidade.

Algumas mudanças foram muito mais fáceis de fazer no lado do front-end do que no back-end, portanto, no processo de desenvolvimento de uma "API de plug-in", o front-end foi um pouco, mas alterado. Em particular, o antigo banco de dados TheQuestion usava IDs numéricos de 64 bits. O banco de dados dos conhecedores e, consequentemente, o banco de dados combinado e a nova API do TheQuestion usaram IDs de 128 bits da cadeia. Em geral, para um front-end escrito em Node.js, essa diferença não é significativa. Mas para aplicativos fortemente tipados, isso acabou sendo fatal. Perdemos a compatibilidade com versões anteriores, e aplicativos mais antigos não podiam funcionar com a "API do plugin".

Em algum momento, havia até um projeto chamado "API de plug-in para API de plug-in", cuja essência era escrever uma pequena camada entre o novo back-end e os aplicativos que converteriam todos os dados no formato antigo. No entanto, rapidamente abandonamos essa ideia. Essa camada acabaria sendo uma "muleta" muito rígida, que no futuro definitivamente nos traria muitos problemas. Por exemplo, você não pode simplesmente pegar e converter códigos de 128 bits em códigos de 64 bits. Eu teria que traduzir com perda de informações e, portanto, possíveis colisões por ID, ou manter uma tabela intermediária com IDs antigos e novos correspondentes (para todos os elementos do banco de dados). Tanto isso como outro - não é a melhor solução arquitetônica.

Além do ID, houve várias outras alterações que também eram muito mais fáceis de oferecer suporte no front-end e no lado do aplicativo. Como resultado, decidimos implementar alterações nos aplicativos e ainda usar a atualização forçada. Em pouco tempo, desenvolvemos novas versões de aplicativos compatíveis com a "API de troca", porque não havia muitas alterações no front-end e elas não eram muito graves. Enviado para a App Store e o Google Play, moderado com sucesso e começou a esperar.

Fase X. Vamos lá!


Então todo o código está escrito. O suporte com a "API de substituição" é testado e disparado. Novas versões do aplicativo foram testadas nas lojas e estão prontas para publicação. Agora tudo isso tinha que ser lançado em produção.

Como a cópia de novos dados do banco de dados antigo para o novo ocorre de forma assíncrona e leva algum tempo, não é possível alternar o back-end (e o banco de dados sob ele) em um site em funcionamento. Isso pode resultar em perda ou inconsistência dos dados do usuário. Por isso, escolhemos uma data, alertamos os usuários e preparamos uma placa “Trabalho técnico em andamento”.

E então chegou a hora, ou melhor, a noite X. O plano de lançamento era assim:

  1. No site TheQuestion, penduramos um esboço "O trabalho está em andamento".
  2. Transferimos aplicativos para o modo somente leitura. Os usuários podem ler o conteúdo do banco de dados antigo, mas não podem criar um novo.
  3. TheQuestion Readonly. : . , , .
  4. . , .
  5. .
  6. , , API.
  7. API . — , API .
  8. , , .
  9. « », API.
  10. .

Bem, isso não parece tão assustador. Na realidade, tudo correu bem. Das surpresas que encontramos, talvez demorasse muito tempo para publicar o aplicativo na App Store (o aplicativo foi verificado com antecedência, tratava-se apenas de aparecer na loja). No final, levou várias horas, razão pela qual toda a operação foi um pouco atrasada.

Além disso, no processo de troca, havia um recurso importante que complicava tudo muitas vezes e aumentava a responsabilidade. O fato é que o processo de comutação não pôde ser revertido.

Embora o processo de copiar e converter dados do banco de dados antigo do TheQuestion para um novo e integrado tenha sido configurado e depurado para nós, não houve processo de cópia reversa (do novo banco de dados para o antigo). Isso significa que, assim que abrirmos o site na “API de troca” e iniciarmos o tráfego do usuário, todas as perguntas, respostas, comentários e curtidas recém-criadas não poderão mais entrar facilmente no antigo banco de dados do TheQuestion. Se algo der errado após a abertura, por exemplo, um único banco de dados não pode lidar com a carga, não será possível reverter tudo rapidamente.

Na verdade, é claro, eu exagerei. De qualquer forma, não perderíamos dados do usuário. Tínhamos um plano B e uma maneira de fazer backup de dados manualmente de um novo banco de dados para um antigo. Mas ainda levaria algum tempo, e a reversão não teria ocorrido de maneira indolor para os usuários.

Felizmente, o plano A funcionou e nada teve que ser revertido.



Estágio final


Então, o back-end foi alterado, o banco de dados foi combinado, os aplicativos móveis não foram esquecidos. Para os usuários, nada mudou, porque o site Yandex.Ku, que deveria combinar dados de ambos os sites, ainda não foi lançado naquele momento. E para o seu lançamento, precisávamos resolver mais um problema.

No começo, escrevi que precisávamos combinar não apenas perguntas e respostas de dois serviços em um novo, mas também usuários. Os usuários deveriam ter conseguido não apenas ver seu conteúdo no Kew, mas também gerenciá-lo no Kew. Tecnicamente, combinar dados e transferir direitos de gerenciamento não é difícil. É muito mais difícil garantir que os direitos sejam transferidos para quem eles devem ser transferidos.

Ao passar de Znatokov para Kew, tudo é simples: nos dois casos, a mesma conta Yandex é usada. Mas o TheQuestion tem sua própria conta, que não pode ser autorizada no Kew. Felizmente, pensamos sobre isso de antemão. Muito antes das ações descritas acima, permitimos que os usuários do TheQuestion vinculassem seus perfis Yandex. E no momento da consolidação física dos serviços, mais de 90% dos usuários ativos já haviam feito isso. Isso nos permitiu iniciar sem esforço a migração de conteúdo e usuários.

Total


Quando nos mudamos, queríamos salvar cada usuário, por isso fomos conscientemente para a opção mais arriscada e demorada de combinar plataformas. Criamos uma base tecnológica unificada, aprendemos a transportar conteúdo e perfis instantaneamente. Em vez de fechamento inesperado e realocação forçada, eles mantiveram a funcionalidade dos serviços antigos, lançaram um novo e explicaram suas vantagens.



Lançamos o Yandex.Kew no ano passado. Agora, mais de 80% dos autores ativos de TheQuestion e Znatokov se mudaram voluntariamente para um novo lar.

All Articles