Uma parte geralmente esquecida, embora fundamental, do projeto de sistemas de alto desempenho é o planejamento da capacidade. Particularmente, não prestei muita atenção a esse tópico no passado. Eu tinha a tendência de pensar nisso apenas em um estágio muito inicial de projeto de sistemas e pensei que só era valioso para sistemas em grande escala.
Na verdade, o capacity planning é um exercício incrivelmente informativo. Ele pode ajudá-lo(a) a entender a viabilidade de um projeto específico e também pode sugerir opções melhores. Abordá-lo como parte do processo de design em qualquer mudança considerável - "considerável" definido como uma função de como a mudança afeta a escala relativa do sistema existente - pode ser extremamente útil.
Dito isso, pensei em compartilhar alguns dos fundamentos do capacity planning para design de sistemas.
§Esclareça o escopo do design
Se você estiver construindo um novo sistema do zero ou introduzindo mudanças, o processo usual é entender o que o software deve fazer. Compreender requisitos funcionais, como quais recursos são necessários, e requisitos não funcionais, como restrições que o sistema deve obedecer são um bom lugar para começar. Um exemplo de requisito funcional é adicionar upload de vídeo a uma postagem/feed de mídia social que só oferece suporte a imagens. Um requisito não funcional pode ser a necessidade que esses vídeos funcionem em conexão 2g.
Depois de entender o que a mudança acarreta, explore suas características. Por exemplo, observe as proporções de leitura versus escrita. Sistemas diferentes precisarão de otimizações diferentes devido a essas características. Por exemplo, um feed de notícias provavelmente terá mais leituras do que escritas e um aplicativo de bem-estar que rastreia sua atividade enquanto se exercita provavelmente será o contrário.
Outro conjunto de características que afetará o exercício de capacity planning é compreender como os requisitos não funcionais afetam storage e bandwidth. Por exemplo, para um aplicativo de mensagens, escolher a persistência local no cliente em vez do servidor para mensagens afetará drasticamente o investimento em armazenamento. No entanto, ele também terá seu próprio conjunto de desafios ao sincronizar dados entre dispositivos.
Neste estágio, esclarecer o que importa ajudará na definição dos melhores trade-offs e, potencialmente, simplificará a implementação.
§Estime algumas das opções
Depois de esclarecer o escopo, você está pronto para passar para algumas estimativas. Neste estágio, você provavelmente tem uma ideia aproximada de quais recursos escolher para estimativa. Sua medida inicial para quantificar a capacidade é geralmente uma taxa de transferência (throughput) presumida, inferida de um recurso semelhante ou outro número, como usuários ativos diariamente.
Em primeiro lugar faça uma estimativa aproximada de quanto custa um request em bytes. Arredondar para cima é uma boa ideia para manter os números conservadores. Estimativas para texto são simples pois você pode assumir um conjunto de campos e seus tamanhos em bytes. Para armazenamento de objetos, como imagens ou vídeos, pense em versionamento, por exemplo, o tamanho médio de uma miniatura x tamanho inteiro, ou vídeo em 480p x 1080p. Observe como isso interage com os requisitos não funcionais e quais opções você tem disponíveis para cumprir a especificação.
Depois de saber quanto custa cada registro, pense em bandwidth por operação. Pense em como você pode reduzir potencialmente a quantidade de dados enviados nos dois sentidos: cliente para servidor e servidor para cliente. Especialmente para leituras, você geralmente tem oportunidades de economia. Por exemplo, em vez de buscar um registro completo, com todos os campos, todas as vezes, você provavelmente pode enviar uma fração do conteúdo do servidor em alguns momentos. Exemplos disso são truncar conteúdo ou renderizar versões. Resumindo, nem toda interação precisará do registro inteiro, então você pode fatorar esse aspecto e descontar dos cálculos de acordo com o comportamento esperado.
A partir daí, é apenas matemática de bytes e back of the envelope calculations. Com o tamanho médio de registro por operação, você tem estimativas e pode entender trade-offs para diferentes opções de design.
§Busque oportunidades de otimização
Em seguida, otimização. Uma oportunidade muito comum para a otimização do armazenamento é definir a retenção de dados. Nem todos os sistemas precisam - ou mesmo podem, devido à compliance - manter todos os dados para sempre. Uma oportunidade típica é examinar a granularidade dos dados, por exemplo, agregar dados por uma dimensão, como por exemplo tempo ou usuário. Especialmente em grande escala, uma política de retenção de dados apropriada pode melhorar drasticamente o desempenho de sistemas e economizar custos.
Outra oportunidade para reduzir a sobrecarga é caching. Para sistemas read-heavy, geralmente, uma pequena parte do conteúdo obtém uma quantidade desproporcional de leituras, e o armazenamento em cache pode reduzir drasticamente bandwidth nesses casos. Há uma infinidade de maneiras de explorar a otimização de leituras, como carregar conteúdo via lazy loading com base no que está na janela de visualização, fazer uso de CDN (content delivery network), melhorar gradativamente a qualidade de vídeo/áudio ao longo do tempo de engajamento do usuário e assim por diante. Eles também dependem muito de requisitos não funcionais.
Da mesma forma, você pode aproveitar do uso de cache para sistemas write-heavy como uma forma de reduzir as necessidades de capacidade de armazenamento. Por exemplo, em uma arquitetura baseada em eventos time-series, omitir gravações onde não há mudança de estado. Sensores de leitura de temperatura que enviam atualizações em um intervalo de tempo fixo para um servidor são um excelente exemplo disso. As temperaturas não tendem a mudar drasticamente em um curto espaço de tempo, portanto, você pode armazenar em cache um par de sensor/temperatura e gravar em um banco para armazenamento de longo prazo apenas quando ocorrer uma mudança.
Em suma, existem muitas formas de se otimizar tendo em vista capacidade de armazenamento ou bandwidth. Acredito que dois aspectos principais a serem lembrados são como a otimização afeta a capacidade relativa do seu sistema e para o que você está otimizando.
§Esteja preparado(a) para operar
Muito do trabalho verdadeiramente desafiador para o capacity planning, em minha opinião, está em operar capacidade. Às vezes, as suposições que baseamos no capacity planning no estágio de design não são verdadeiras na prática. Por isso, estar preparado(a) para operar e, portanto, planejar continuamente é extremamente importante.
Uma boa prática para detectar equívocos é escrever algumas das suposições feitas na fase de design na forma de alertas. Digamos que no estágio de design você presuma que o sistema terá 20% de cache hits e, na prática, você tem 5%. Mesmo que você provavelmente tenha monitoramento em vigor, a suposição feita pode escapar. Monitorar as suposições feitas anteriormente permite que você atue de forma proativa ao invés de apenas saber quando chegar a sua próxima conta de infraestrutura ou quando ocorrer um incidente.
Mesmo que você tenha maneiras de confiar nos dados para prever e saber como a carga evolui, você não pode prever todos os cenários. Para reduzir esse risco, o que você pode fazer continuamente é usar alguns conceitos de modelagem de threats para entender quais proteções estão faltando, o que acontece se uma certa característica muda, ou se um requisito não funcional muda. Isso ajuda a indicar onde você pode ter pontos cegos.
Com isso dito, o capacity planning é um assunto profundo e fascinante. Pode informar as decisões de design, operações de sistemas de suporte e até mesmo ajudá-lo(a) a desafiar os requisitos iniciais. Ter isso como parte do processo de design pode ajudar a aumentar a consciência dos trade-offs que você faz e coloca você em uma posição mais forte para abordar mudanças.