Como estamos tornando a infraestrutura do Roblox mais eficiente e resiliente

À medida que a Roblox cresceu nos últimos 16 anos ou mais, o mesmo ocorreu com a escala e a complexidade da infraestrutura técnica que sustenta milhões de experiências colaborativas imersivas em 3D. O número de máquinas que suportamos mais que triplicou nos últimos dois anos, passando de aproximadamente 36.000 em 30 de junho de 2021 para quase 145.000 atualmente. Oferecer essas experiências sempre ativas para pessoas em todo o mundo requer mais de 1.000 serviços internos. Para nos ajudar a controlar custos e a latência da rede, implantamos e gerenciamos essas máquinas como parte de uma infraestrutura de nuvem privada híbrida e personalizada que opera principalmente no local.
Atualmente, nossa infraestrutura suporta mais de 70 milhões de usuários ativos diários em todo o mundo, incluindo os criadores que dependem da economia do Roblox para seus negócios. Todos esses milhões de pessoas esperam um nível muito alto de confiabilidade. Dada a natureza imersiva de nossas experiências, há uma tolerância extremamente baixa para atrasos ou latência, sem falar em interrupções. O Roblox é uma plataforma de comunicação e conexão, onde as pessoas se reúnem em experiências 3D imersivas. Quando as pessoas se comunicam por meio de seus avatares em um espaço imersivo, mesmo pequenos atrasos ou falhas são mais perceptíveis do que em uma conversa por mensagens de texto ou em uma teleconferência.
Em outubro de 2021, passamos por uma interrupção em todo o sistema. Começou pequeno, com um problema em um componente de um data center. Mas se espalhou rapidamente enquanto investigávamos e acabou resultando em uma interrupção de 73 horas. Na época, compartilhamos tanto detalhes sobre o que aconteceu quanto algumas de nossas primeiras lições aprendidas com o problema. Desde então, temos estudado esses aprendizados e trabalhado para aumentar a resiliência de nossa infraestrutura contra os tipos de falhas que ocorrem em todos os sistemas de grande escala devido a fatores como picos extremos de tráfego, condições climáticas, falhas de hardware, bugs de software ou simplesmente erros humanos. Quando essas falhas ocorrem, como garantimos que um problema em um único componente, ou grupo de componentes, não se espalhe para todo o sistema? Essa questão tem sido nosso foco nos últimos dois anos e, embora o trabalho ainda esteja em andamento, o que fizemos até agora já está valendo a pena. Por exemplo, no primeiro semestre de 2023, economizamos 125 milhões de horas de engajamento por mês em comparação com o primeiro semestre de 2022. Hoje, estamos compartilhando o trabalho que já realizamos, bem como nossa visão de longo prazo para construir um sistema de infraestrutura mais resiliente.

Criando uma rede de segurança
Em sistemas de infraestrutura de grande escala, falhas de pequena escala ocorrem várias vezes ao dia. Se uma máquina apresentar um problema e precisar ser retirada de serviço, isso é administrável, pois a maioria das empresas mantém várias instâncias de seus serviços de back-end. Assim, quando uma única instância falha, as outras assumem a carga de trabalho. Para lidar com essas falhas frequentes, as solicitações geralmente são configuradas para tentar novamente automaticamente caso ocorram erros.
Isso se torna um desafio quando um sistema ou uma pessoa tenta novamente de forma muito agressiva, o que pode fazer com que essas falhas de pequena escala se propaguem por toda a infraestrutura para outros serviços e sistemas. Se a rede ou um usuário tentar novamente com persistência suficiente, isso acabará sobrecarregando todas as instâncias desse serviço e, potencialmente, outros sistemas, globalmente. Nossa interrupção de 2021 foi resultado de algo bastante comum em sistemas de grande escala: uma falha começa pequena e depois se propaga pelo sistema, crescendo tão rapidamente que é difícil resolvê-la antes que tudo pare de funcionar.
Na época da nossa interrupção, tínhamos um data center ativo (com componentes dentro dele atuando como backup). Precisávamos da capacidade de fazer o failover manualmente para um novo data center quando um problema derrubasse o existente. Nossa prioridade principal era garantir que tivéssemos uma implantação de backup do Roblox, então construímos esse backup em um novo data center, localizado em uma região geográfica diferente. Isso adicionou proteção para o pior cenário possível: uma interrupção se espalhando para componentes suficientes dentro de um data center a ponto de torná-lo totalmente inoperante. Agora temos um data center lidando com cargas de trabalho (ativo) e outro em standby, servindo como backup (passivo). Nossa meta de longo prazo é passar dessa configuração ativa-passiva para uma configuração ativa-ativa, na qual ambos os data centers lidam com cargas de trabalho, com um balanceador de carga distribuindo as solicitações entre eles com base na latência, capacidade e integridade. Quando isso estiver em vigor, esperamos ter uma confiabilidade ainda maior para todo o Roblox e ser capazes de fazer o failover quase instantaneamente, em vez de levar várias horas.

Mudança para uma infraestrutura celular
Nossa próxima prioridade foi criar paredes de proteção robustas dentro de cada data center para reduzir a possibilidade de falha total de um data center. As células (algumas empresas as chamam de clusters) são essencialmente um conjunto de máquinas e são a forma como estamos criando essas paredes. Replicamos serviços tanto dentro quanto entre as células para aumentar a redundância. Em última análise, queremos que todos os serviços da Roblox sejam executados em células para que possam se beneficiar tanto das paredes de proteção robustas quanto da redundância. Se uma célula deixar de funcionar, ela pode ser desativada com segurança. A replicação entre células permite que o serviço continue em execução enquanto a célula é reparada. Em alguns casos, o reparo da célula pode significar um reprovisionamento completo da célula. Em todo o setor, limpar e reprovisionar uma máquina individual, ou um pequeno conjunto de máquinas, é bastante comum, mas fazer isso para uma célula inteira, que contém cerca de 1.400 máquinas, não é.
Para que isso funcione, essas células precisam ser amplamente uniformes, para que possamos mover cargas de trabalho de uma célula para outra de forma rápida e eficiente. Estabelecemos certos requisitos que os serviços precisam atender antes de serem executados em uma célula. Por exemplo, os serviços devem ser conteinerizados, o que os torna muito mais portáteis e impede que qualquer pessoa faça alterações de configuração no nível do sistema operacional. Adotamos uma filosofia de infraestrutura como código para as células: em nosso repositório de código-fonte, incluímos a definição de tudo o que está em uma célula para que possamos reconstruí-la rapidamente do zero usando ferramentas automatizadas.
Nem todos os serviços atendem a esses requisitos atualmente, por isso temos trabalhado para ajudar os proprietários de serviços a atendê-los sempre que possível e criamos novas ferramentas para facilitar a migração de serviços para as células quando estiverem prontos. Por exemplo, nossa nova ferramenta de implantação automaticamente “distribui” a implantação de um serviço entre as células, para que os proprietários dos serviços não precisem se preocupar com a estratégia de replicação. Esse nível de rigor torna o processo de migração muito mais desafiador e demorado, mas a recompensa a longo prazo será um sistema em que:
- É muito mais fácil conter uma falha e impedir que ela se espalhe para outras células;
- Nossos engenheiros de infraestrutura podem ser mais eficientes e agir mais rapidamente; e
- Os engenheiros que criam os serviços no nível do produto, que são finalmente implantados nas células, não precisam saber nem se preocupar em quais células seus serviços estão sendo executados.
Resolvendo desafios maiores
Da mesma forma que portas corta-fogo são usadas para conter chamas, as células atuam como paredes resistentes a explosões dentro de nossa infraestrutura para ajudar a conter qualquer problema que esteja provocando uma falha dentro de uma única célula. Eventualmente, todos os serviços que compõem o Roblox serão implantados de forma redundante dentro e entre as células. Quando esse trabalho estiver concluído, os problemas ainda poderão se propagar o suficiente para tornar uma célula inteira inoperante, mas seria extremamente difícil que um problema se propagasse além dessa célula. E se conseguirmos tornar as células intercambiáveis, a recuperação será significativamente mais rápida, pois poderemos fazer o failover para uma célula diferente e impedir que o problema afete os usuários finais.
Onde isso se torna complicado é separar essas células o suficiente para reduzir a chance de propagação de erros, mantendo ao mesmo tempo o desempenho e a funcionalidade. Em um sistema de infraestrutura complexo, os serviços precisam se comunicar entre si para compartilhar consultas, informações, cargas de trabalho etc. À medida que replicamos esses serviços em células, precisamos ser cuidadosos sobre como gerenciamos a comunicação cruzada. Em um mundo ideal, redirecionamos o tráfego de uma célula com problemas para outras células saudáveis. Mas como gerenciamos uma “consulta fatal” — aquela que está fazendo com que uma célula fique com problemas? Se redirecionarmos essa consulta para outra célula, ela pode fazer com que essa célula fique com problemas, exatamente da maneira que estamos tentando evitar. Precisamos encontrar mecanismos para desviar o tráfego “bom” das células com problemas, ao mesmo tempo em que detectamos e suprimimos o tráfego que está fazendo com que as células fiquem com problemas.
No curto prazo, implantamos cópias de serviços de computação em cada célula de computação para que a maioria das solicitações ao data center possa ser atendida por uma única célula. Também estamos balanceando a carga de tráfego entre as células. Olhando mais adiante, começamos a construir um processo de descoberta de serviços de última geração que será aproveitado por uma malha de serviços, que esperamos concluir em 2024. Isso nos permitirá implementar políticas sofisticadas que permitirão a comunicação entre células apenas quando não houver impacto negativo nas células de failover. Também em 2024 será lançado um método para direcionar solicitações dependentes a uma versão de serviço na mesma célula, o que minimizará o tráfego entre células e, consequentemente, reduzirá o risco de propagação de falhas entre elas.
Em picos de demanda, mais de 70% do tráfego de nossos serviços de back-end é atendido fora das células, e aprendemos muito sobre como criar células, mas prevemos mais pesquisas e testes à medida que continuamos a migrar nossos serviços até 2024 e além. À medida que avançamos, essas barreiras de proteção se tornarão cada vez mais robustas.

Migração de uma infraestrutura sempre ativa
A Roblox é uma plataforma global que atende usuários em todo o mundo, por isso não podemos migrar serviços durante horários de menor movimento ou “períodos de inatividade”, o que complica ainda mais o processo de migrar todas as nossas máquinas para células e nossos serviços para rodarem nessas células. Temos milhões de experiências sempre ativas que precisam continuar a ser suportadas, mesmo enquanto transferimos as máquinas nas quais elas rodam e os serviços que as sustentam. Quando iniciamos esse processo, não tínhamos dezenas de milhares de máquinas ociosas e disponíveis para migrar essas cargas de trabalho.
No entanto, tínhamos um pequeno número de máquinas adicionais que foram adquiridas em antecipação ao crescimento futuro. Para começar, criamos novas células usando essas máquinas e, em seguida, migramos as cargas de trabalho para elas. Valorizamos a eficiência tanto quanto a confiabilidade; portanto, em vez de sair comprando mais máquinas quando ficamos sem máquinas “sobressalentes”, construímos mais células limpando e reabastecendo as máquinas das quais havíamos migrado. Em seguida, migramos as cargas de trabalho para essas máquinas reabastecidas e reiniciamos o processo. Esse processo é complexo — à medida que as máquinas são substituídas e ficam disponíveis para serem incorporadas às células, elas não são liberadas de maneira ideal e ordenada. Elas estão fisicamente fragmentadas pelas salas de dados, obrigando-nos a provisioná-las de forma fragmentada, o que requer um processo de desfragmentação no nível do hardware para manter os locais do hardware alinhados com domínios de falha física em grande escala.
Uma parte de nossa equipe de engenharia de infraestrutura está focada na migração de cargas de trabalho existentes de nosso ambiente legado, ou “pré-célula”, para as células. Esse trabalho continuará até que tenhamos migrado milhares de serviços de infraestrutura diferentes e milhares de serviços de back-end para células recém-construídas. Esperamos que isso leve todo o próximo ano e possivelmente se estenda até 2025, devido a alguns fatores complicadores. Primeiro, esse trabalho requer a criação de ferramentas robustas. Por exemplo, precisamos de ferramentas para reequilibrar automaticamente um grande número de serviços quando implantamos uma nova célula — sem impactar nossos usuários. Também observamos serviços que foram construídos com suposições sobre nossa infraestrutura. Precisamos revisar esses serviços para que não dependam de elementos que possam mudar no futuro, à medida que avançamos para as células. Também implementamos tanto uma maneira de identificar padrões de design conhecidos que não funcionam bem com a arquitetura celular, quanto um processo de testes metódico para cada serviço migrado. Esses processos nos ajudam a evitar quaisquer problemas para os usuários causados pela incompatibilidade de um serviço com as células.
Hoje, cerca de 30.000 máquinas estão sendo gerenciadas por células. É apenas uma fração de nossa frota total, mas tem sido uma transição muito tranquila até agora, sem impacto negativo para os jogadores. Nosso objetivo final é que nossos sistemas alcancem 99,99% de tempo de atividade para os usuários todos os meses, o que significa que não interromperíamos mais do que 0,01% das horas de engajamento. Em todo o setor, o tempo de inatividade não pode ser completamente eliminado, mas nosso objetivo é reduzir qualquer tempo de inatividade do Roblox a um nível que seja quase imperceptível.
Preparando-nos para o futuro à medida que crescemos
Embora nossos esforços iniciais estejam se mostrando bem-sucedidos, nosso trabalho com as células está longe de terminar. À medida que o Roblox continua a crescer, continuaremos trabalhando para melhorar a eficiência e a resiliência de nossos sistemas por meio dessa e de outras tecnologias. À medida que avançamos, a plataforma se tornará cada vez mais resiliente a problemas, e quaisquer problemas que ocorram devem se tornar progressivamente menos visíveis e menos perturbadores para as pessoas em nossa plataforma.
Em resumo, até o momento, nós:
- Construído um segundo data center e alcançado com sucesso o status ativo/passivo.
- Criado células em nossos data centers ativo e passivo e migrado com sucesso mais de 70% do tráfego de nossos serviços de back-end para essas células.
- Estabelecido os requisitos e as melhores práticas que precisaremos seguir para manter todas as células uniformes à medida que continuamos a migrar o restante de nossa infraestrutura.
- Demos início a um processo contínuo de construção de “barreiras de proteção” mais robustas entre as células.
À medida que essas células se tornam mais intercambiáveis, haverá menos interferência entre elas. Isso abre algumas oportunidades muito interessantes para nós em termos de aumentar a automação em torno do monitoramento, da resolução de problemas e até mesmo da transferência automática de cargas de trabalho.
Em setembro, também começamos a realizar experimentos ativo/ativo em nossos data centers. Esse é outro mecanismo que estamos testando para melhorar a confiabilidade e minimizar os tempos de failover. Esses experimentos ajudaram a identificar vários padrões de projeto de sistema, principalmente em torno do acesso a dados, que precisamos reformular à medida que avançamos para nos tornarmos totalmente ativo-ativo. No geral, o experimento foi bem-sucedido o suficiente para deixá-lo em execução para o tráfego de um número limitado de nossos usuários.



