Sistemas com recursos insuficientes “caem” em ambiente produtivo, comprometendo a confiabilidade e ameaçando a continuidade dos negócios. Por outro lado, sistemas com recursos excessivos, corroem reservas financeiras, abreviando o tempo de vida de startups ou o lucro.
Conceitos simples para resolver problemas complexos
Jeff Dean, da Google, defende a utilização de uma técnica simples para estimativas, chamada “Back-of-the-Envelope calculations“. Trata-se do uso combinado da imaginação criativa com algum conhecimento de números relacionados à performance.
Jeff Dean, Google Senior Fellow
A Google mantém 11 níveis de distinção para seus engenheiros. O nível mais alto – Google Senior Fellow – foi conferido a apenas dois profissionais na organização: Jeff Dean e Sundar Pichai.
Sundar Pichai é o atual CEO da Google.
Jeff Dean é o SVP da Google Research e tem no currículo, além do próprio buscador, BigTable, MapReduce e ProtocolBuffers e o TensorFlow.
Seguindo as recomendações de Dean, ao projetar uma API, por exemplo, deveríamos estimar quantas requisições os principais endpoints têm que atender (importante, também, para o estabelecimento de Rate Limiters) por um período. Daí, considerando horários comuns de consumo, é possível inferir quantas requisições precisam ser suportadas por hora, minuto e segundo balizando o throughput ideal. Adicionadas às margens de segurança, tem-se uma boa ideia de dimensionamento de infraestrutura, frente a arquitetura.
Rate Limiters
Em sistemas distribuídos, um rate limiter é utilizado para restringir o tráfego de uma aplicação cliente para os servidores. Toda vez que um limite de requisições é atingido, o excedente é contido (em uma fila, por exemplo) para processamento posterior ou é descartado. São essenciais, mas precisam ser implementados adequadamente.
A adoção de rate limiters previne problemas de negação de serviço (DoS), em função de uso abusivo, intencional ou não, de um recurso. Por isso, eles são comuns nas arquiteturas de sistemas projetados para a escalabilidade. O Twitter, por exemplo, restringe o número de tuítes, por usuário, a 2400 por dia. No Instagram, é possível realizar no máximo 200 chamadas às APIs por hora com um mesmo token.
Além de evitar a negação de serviços, rate limiters ajudam a controlar custos, sobretudo em ambientes onde os gastos estejam associados a pagamentos para terceiros conforme ordem de uso.
Rate limiters podem ser implementados em client-side, server-side ou em um middleware. Implementações em client-side tem como mérito principal a redução da pressão sobre a rede. Já implementações em server-side ou middleware são mais “confiáveis”, por estarem em ambientes gerenciados, e robustas, visto que permitem regras mais complexas.
O custo computacional e de tempo para o atendimento de cada requisição também pode ser calculado considerando operações como busca e leitura de dados em dispositivos de armazenamento, transferência de dados na rede, compactação e etc. Daí, projetando o volume de requisições esperadas, pode-se avaliar a eficiência de uma solução proposta.
Há três conceitos básicos para a prática de Back-of-the-Envelope calculations:
- Unidades básicas de medida de volumes de dados (potências de 2)
- Métricas de latência
- Disponibilidade
Em uma palestra em que Dean conta um pouco da trajetória da arquitetura do sistema de buscas da Google, ele demonstra a eficácia da técnica para projetar um sistema de thumbnails (aliás, esse é um desafio clássico em entrevistas de System Design para grandes companhias).
Unidades básicas de medida de volumes de dados
Mesmo em sistemas gigantescos, que lidam com quantidades monstruosas de dados, é fundamental expressar corretamente volumes, usando informações básicas.
A unidade fundamental de medida de volumes de dados é o byte, que é uma sequência de 8 bits (binary digits). As demais unidades importantes são expressas em potências de 2 (2n).
- 1 Kilobyte (KB), 210 bytes (1024 bytes)
- 1 Megabyte (MB), 220 bytes (1.048.576 bytes ou ~1 milhão de bytes)
- 1 Gigabyte (GB), 230 bytes (1.073.741.824 bytes ou ~1 bilhão de bytes)
- 1 Terabyte (TB), 240 bytes (1.099.511.627.776 bytes ou ~1 trilhão de bytes)
- 1 Petabyte (PB), 250 bytes (1.125.899.906.842.624 bytes ou ~1 quatrilhão de bytes)
Megabyte ou Megabit
É importante diferenciar Megabytes (MB) de Megabits (Mb [com b minúsculo])
Enquanto 1 MB corresponde a 1024 bytes, 1 Mb corresponde a 1024 bits. Ou seja, 1Kb é 8 vezes menor que um 1KB.
KB, MB, GB, TB e PB são unidades de medida geralmente associadas a volumes de dados armazenados. Kb, Mb, Gb, Tb e Pb são unidades de medida geralmente associadas a dados transferidos.
Métricas básicas de latência
Em 2012, Dr. Jeffrey Dean, da Google, compartilhou o custo da latência para algumas operações relevantes.
Mesmo não estejam atualizados, sobretudo por ignorarem a evolução tecnológica, estes números continuam úteis para quem projeta sistemas.
Latency Comparison Numbers (~2012) ---------------------------------- L1 cache reference 0.5 ns Branch mispredict 5 ns L2 cache reference 7 ns 14x L1 cache Mutex lock/unlock 25 ns Main memory reference 100 ns 20x L2 cache, 200x L1 cache Compress 1KB with Zippy 3,000 ns 3 us Send 1KB over 1 Gbps network 10,000 ns 10 us Read 4KB randomly from SSD* 150,000 ns 150 us ~1GB/sec SSD Read 1MB sequentially from memory 250,000 ns 250 us Round trip within same datacenter 500,000 ns 500 us Read 1MB sequentially from SSD* 1,000,000 ns 1,000 us 1 ms ~1GB/sec SSD, 4X memory Disk seek 10,000,000 ns 10,000 us 10 ms 20x datacenter roundtrip Read 1 MB sequentially from disk 20,000,000 ns 20,000 us 20 ms 80x memory, 20X SSD Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms Notes ----- 1 ns = 10^-9 seconds 1 us = 10^-6 seconds = 1,000 ns 1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns
As diferenças indicadas ficam ainda mais impactantes quando consideramos escala. Abaixo, segue a mesma relação escaladas em um bilhão.
Lets multiply all these durations by a billion: Magnitudes: ### Minute: L1 cache reference 0.5 s One heart beat (0.5 s) Branch mispredict 5 s Yawn L2 cache reference 7 s Long yawn Mutex lock/unlock 25 s Making a coffee ### Hour: Main memory reference 100 s Brushing your teeth Compress 1K bytes with Zippy 50 min One episode of a TV show (including ad breaks) ### Day: Send 2K bytes over 1 Gbps network 5.5 hr From lunch to end of work day ### Week SSD random read 1.7 days A normal weekend Read 1 MB sequentially from memory 2.9 days A long weekend Round trip within same datacenter 5.8 days A medium vacation Read 1 MB sequentially from SSD 11.6 days Waiting for almost 2 weeks for a delivery ### Year Disk seek 16.5 weeks A semester in university Read 1 MB sequentially from disk 7.8 months Almost producing a new human being The above 2 together 1 year ### Decade Send packet CA->Netherlands->CA 4.8 years Average time it takes to complete a bachelor's degree
Inferências importantes:
- Operar dados em memória é muito mais performático do que operar em disco
- Comprimir dados, usando algoritmos simples, consome pouco tempo
- Geralmente vale muito a pena comprimir dados, antes de transmitir por internet
Métricas básicas de disponibilidade
A disponibilidade de um sistema é expressa pelo percentual de operações executadas abaixo de uma expectativa de latência. Um sistema com 100% de disponibilidade conclui todas as suas operações dentro do tempo previsto. Bons sistemas tem disponibilidade acima de 99%.
É comum que serviços on-line estabeleçam SLAs especificando um nível mínimo de disponibilidade. Geralmente, pela quantidade de “noves”. Assim:
- Sistemas com disponibilidade de 99% (dois “noves”) respondem acima do tempo esperado durante ~14 minutos, todos os dias.
- Disponibilidade de 99,9% (três “noves”), implica em tempos de resposta ruins por ~1,44 minutos, todos os dias (oito horas em um ano).
- Disponibilidade de 99,99% (quatro “noves”), implica em tempos de resposta ruins por ~9 segundos, todos os dias (~52 minutos em um ano).
- Disponibilidade de 99.999% (cinco “noves”) implica em tempos de resposta ruins por ~5 minutos em um ano.
Eventualmente, disponibilidade é expressa de outra forma em acordos de SLA. Por exemplo, as máquinas virtuais no Azure operam em julho de 2021 conforme os seguintes termos:
- Para todas as máquinas virtuais que têm duas ou mais instâncias implantadas em duas ou mais zonas de disponibilidade na mesma região do Azure, há garantias de conectividade de máquina virtual para pelo menos uma instância pelo menos 99,99% do tempo.
- Para todas as máquinas virtuais que têm duas ou mais instâncias implantadas no mesmo conjunto de disponibilidade ou no mesmo grupo de hosts dedicados, há garantias de conectividade de máquina virtual para pelo menos uma instância pelo menos 99,95% do tempo.
- Para qualquer máquina virtual de instância única usando Premium SSD ou Ultra Disk para todos os discos do sistema operacional e discos de dados, há garantias de conectividade de máquina virtual de pelo menos 99,9%.
- Para qualquer máquina virtual de instância única usando discos gerenciados SSD padrão para discos do sistema operacional e discos de dados, há garantias de conectividade de máquina virtual de pelo menos 99,5%.
- Para qualquer máquina virtual de instância única usando discos gerenciados de HDD padrão para discos do sistema operacional e discos de dados, há garantias de conectividade de máquina virtual de pelo menos 95%.
Um exemplo prático
Oren Eini (Ayende), fez uso dessa técnica em um post de uma série discutindo o desafio de reconstruir uma rede social como o twitter.
A ideia de “Back-of-the-envelope calculations”, que em bom português seria algo como “conta de padeiro”, é utilizar cálculos simples para fundamentar a tomada de decisões técnicas. Funciona para a Google, provavelmente funcionará também para você também. É uma solução simples, talvez até em demasia, mas é “mais do que o nada” que parece ser o padrão comum nas organizações.