Matheus Fidelis
Matheus Fidelis Principal Engineer, Lifelong Learner, Site Reliability Engineer, Cloud and Containers Wizard, Software Alchemist and Home Bartender

Msc. Field Notes - Shard Router

Msc. Field Notes - Shard Router

Este artigo faz parte de uma organização de um material bruto excedente da minha tese de mestrado. Tem o objetivo de compilar as referencias tecnicas e experimentações práticas.

Este compilado foca no desenvolvimento e análise de um roteador baseado em dar suporte em celulas ou shards de bu;lheads, componente inicial para implementações arquitetura celular.

O projeto se baseia na aplicação simples e conceitual de padrões de arquitetura de roteamento celular ou bulkheads, que implementa roteamento determinístico baseado em hashing consistente. O roteador celular atua como um proxy reverso especializado que direciona requisições de clientes para células (shards) específicas, garantindo que requisições de um mesmo cliente sejam sempre processadas pela mesma célula ou shard.

  • Hashing Consistente: Para distribuição uniforme e estável de requisições
  • Roteamento Determinístico: Garantindo que clientes sejam sempre direcionados à mesma célula
  • Isolamento de Falhas: Através de bulkheads implementados a nível de roteamento
  • Observabilidade Granular: Com métricas específicas por célula e algoritmo de hash


Fundamentação Teórica

Sharding e Particionamento Horizontal

O sharding, ou particionamento horizontal, é uma técnica consolidada para distribuição de dados e processamento em sistemas distribuídos (Özsu & Valduriez, 2020). Diferentemente do particionamento vertical, que divide dados por colunas ou atributos, o sharding divide o conjunto de dados em partições horizontais baseadas em critérios específicos, como ranges de valores ou funções de hash. Esse conceito é diretamente associado a particionamento de dados fisicamente em bancos de dados, mas não se limita a eles. Iremos seguir aqui pra frente como um critério de segmentação total de infraestrutura, cliente e demais recursos.

graph TD subgraph "Particionamento Vertical" PV1[Tabela Original] PV2[Colunas A, B] PV3[Colunas C, D] PV4[Colunas E, F] PV1 --> PV2 PV1 --> PV3 PV1 --> PV4 end
graph TD subgraph "Sharding" PH1[Dataset Completo] PH2[Shard A
Users 1-1000] PH3[Shard B
Users 1001-2000] PH4[Shard C
Users 2001-3000] PH1 --> PH2 PH1 --> PH3 PH1 --> PH4 end


Análise de Blast Radius e Disponibilidade Sistêmica

Conceito de Blast Radius em Arquiteturas Distribuídas

O blast radius (raio de explosão) representa o escopo de impacto de uma falha em sistemas distribuídos. Em arquiteturas celulares, o blast radius é diretamente proporcional ao número de células distribuídas, oferecendo uma relação matemática clara entre disponibilidade e granularidade de distribuição.

A fórmula fundamental para cálculo de disponibilidade em caso de falha de uma célula é:

1
2
3
4
5
Disponibilidade = ((N - F) / N) × 100%

Onde:
- N = Número total de células/shards
- F = Número de células falhando


Impacto da Granularidade na Disponibilidade

A análise quantitativa demonstra como o aumento do número de células reduz exponencialmente o blast radius:

Relação Blast Radius vs. Número de Células

Número de Células Falhas (1 célula) Disponibilidade Blast Radius Clientes Afetados
3 1 66.7% 33.3% 1/3 da base
5 1 80.0% 20.0% 1/5 da base
10 1 90.0% 10.0% 1/10 da base
25 1 96.0% 4.0% 1/25 da base
50 1 98.0% 2.0% 1/50 da base
100 1 99.0% 1.0% 1/100 da base
1000 1 99.9% 0.1% 1/1000 da base



Trade-offs Operacionais

O aumento da granularidade celular apresenta trade-offs que devem ser considerados:

Benefícios da Alta Granularidade

Redução de Blast Radius:

  • 10 células: Falha afeta 10% dos usuários
  • 100 células: Falha afeta 1% dos usuários
  • 1000 células: Falha afeta 0.1% dos usuários

Isolamento Melhorado:

  • Falhas ficam contidas em domínios menores
  • Debugging e troubleshooting mais focado
  • Rollbacks afetam menos usuários


Custos da Alta Granularidade

Complexidade Operacional vs. Granularidade Celular

Aspecto Baixa Granularidade
(3-10 Células)
Média Granularidade
(25-50 Células)
Alta Granularidade
(100+ Células)
Complexidade Geral Moderada Moderada Alta
Monitoramento Simples
- Poucos endpoints
- Dashboards básicos
Estruturado
- Alertas configurados
- Métricas agregadas
Sofisticado
- Observabilidade avançada
- APM necessário
Deployment Automatizado
- CI/CD recomendado
- Blue/Green deploy
Automatizado
- CI/CD recomendado
- Blue/Green deploy
CI/CD Avançado
- Canary releases
-
Recursos Computacionais Baixo
- 3-10 instâncias
- Overhead mínimo
Moderado
- 25-50 instâncias
- Overhead controlado
Alto
- 100+ instâncias
- Overhead significativo
Custo Operacional Baixo Médio Alto



Overhead Operacional Detalhado:

  • Recursos Computacionais: Aumento linear proporcional ao número de células
  • Monitoramento e Observabilidade: Necessidade de ferramentas sofisticadas (Prometheus, Grafana, Jaeger)
  • Automação: Obrigatória para granularidade alta, opcional para baixa granularidade
  • Equipe Especializada: Requisitos crescentes de expertise em SRE e DevOps



Modelo Matemático de Disponibilidade

Para múltiplas falhas simultâneas, o modelo estende-se para:

1
2
3
4
5
6
Disponibilidade = ((N - F) / N) × 100%

Exemplos práticos:
- 100 células, 2 falhas: ((100-2)/100) = 98% disponível
- 100 células, 5 falhas: ((100-5)/100) = 95% disponível
- 1000 células, 10 falhas: ((1000-10)/1000) = 99% disponível


Implementação na Prova de Conceito

A PoC desenvolvida permite configuração dinâmica do número de células através de variáveis de ambiente:

1
2
3
4
5
6
7
# Configuração para baixo blast radius (alta granularidade)
export SHARD_01_URL="http://cell-001:8080"
export SHARD_02_URL="http://cell-002:8080"
...
export SHARD_100_URL="http://cell-100:8080"

# Resultado: 1% blast radius por falha

O roteador automaticamente distribui a carga entre todas as células configuradas, garantindo que a falha de qualquer célula individual afete apenas 1/N da base de usuários.


Racional prático de blast radius

Com base na análise de blast radius, recomenda-se:

  1. Startups/Pequenas Aplicações: 5-10 células (blast radius: 10-20%)
  2. Aplicações Médias: 25-50 células (blast radius: 2-4%)
  3. Aplicações Críticas: 100+ células (blast radius: <1%)
  4. Sistemas de Alta Disponibilidade: 1000+ células (blast radius: <0.1%)


Implementação e Aspectos Técnicos

A implementação da PoC utiliza sharding baseado em chaves de identificação de clientes, conforme evidenciado na estrutura de configuração:

1
2
3
4
5
6
7
8
type ShardRouterImpl struct {
    hashRing    interfaces.HashRing
    shardingKey string
}

func (sr *ShardRouterImpl) GetShardingKey(r *http.Request) string {
    return r.Header.Get(sr.shardingKey)
}

Esta abordagem garante que todas as requisições de um determinado cliente sejam consistentemente direcionadas à mesma célula, propriedade fundamental para manutenção de estado e cache locality (DeCandia et al., 2007).


Hashing Consistente

O hashing consistente, introduzido por Karger et al. (1997), resolve limitações do hashing tradicional em ambientes distribuídos dinâmicos. Enquanto o hashing simples requer redistribuição global de chaves quando nós são adicionados ou removidos, o hashing consistente minimiza a movimentação de dados, redistribuindo apenas uma fração das chaves.

graph TB subgraph "Hashing Tradicional" HT1[3 Servidores] HT2[Key % 3] HT3[Server 0: 33%] HT4[Server 1: 33%] HT5[Server 2: 33%] HT1 --> HT2 HT2 --> HT3 HT2 --> HT4 HT2 --> HT5 HT6[ +1 Servidor] HT7[Key % 4] HT8[75% das chaves
redistribuídas] HT6 --> HT7 HT7 --> HT8 end style HT8 fill:#ffcdd2
graph TB subgraph "Hashing Consistente" HC1[Hash Ring] HC2[Virtual Replicas] HC3[Minimal Redistribution] HC1 --> HC2 HC2 --> HC3 HC4[ +1 Servidor] HC5[25%
das chaves movidas] HC4 --> HC5 end style HC5 fill:#c8e6c9

A PoC implementa múltiplos algoritmos de hash, permitindo análise comparativa de desempenho e distribuição:

1
2
3
4
5
6
7
const (
    MD5     HashAlgorithm = "MD5"
    SHA1    HashAlgorithm = "SHA1" 
    SHA256  HashAlgorithm = "SHA256"
    SHA512  HashAlgorithm = "SHA512"
    MURMUR3 HashAlgorithm = "MURMUR3"
)

Estudos empíricos realizados com a implementação revelam variações significativas na qualidade da distribuição entre algoritmos. O SHA1 apresentou a menor variância (121.67) e diferença entre melhor e pior shard (2.7%), enquanto algoritmos não-criptográficos como FNV64 demonstraram distribuição inadequada (variância de 156,116.33).

graph TB subgraph "Comparação" CAH1[Input: client-id] subgraph "Algoritmos de Hashing" AC1[SHA1
✅ Melhor Distribuição
Desvio: 11.03] AC2[SHA256
⚠️ Distribuição Moderada
Desvio: 64.60] AC3[SHA512
✅ Boa Distribuição
Desvio: 28.31] AC4[MD5
⚠️ Distribuição Aceitável
Desvio: 42.05] ANC1[MURMUR3
❌ Distribuição Irregular
Desvio: 95.84] ANC2[FNV64
❌ Distribuição Inadequada
Desvio: 395.12] end subgraph "Distribuição nos Shards" DS1[Shard A: 32-34%] DS2[Shard B: 30-35%] DS3[Shard C: 31-37%] end CAH1 --> AC1 CAH1 --> AC2 CAH1 --> AC3 CAH1 --> AC4 CAH1 --> ANC1 CAH1 --> ANC2 AC1 --> DS1 AC1 --> DS2 AC1 --> DS3 end style AC1 fill:#c8e6c9 style AC3 fill:#e8f5e8 style AC2 fill:#fff3e0 style AC4 fill:#fff3e0 style ANC1 fill:#ffcdd2 style ANC2 fill:#ffcdd2


Bulkheads e Isolamento de Falhas

O padrão Bulkhead, inspirado na construção naval, propõe a compartimentalização de sistemas para conter falhas (Nygard, 2018). Na arquitetura celular, cada célula funciona como um bulkhead independente, onde falhas em uma célula não propagam para outras células do sistema.

graph TB subgraph "Arquitetura sem Bulkheads" AB1[Load Balancer] AB2[Shared Resource Pool] AB3[Service A] AB4[Service B] AB5[Service C] AB6[💥 Falha em cascata] AB1 --> AB2 AB2 --> AB3 AB2 --> AB4 AB2 --> AB5 AB3 -.->|falha propaga| AB4 AB4 -.->|falha propaga| AB5 AB5 --> AB6 end style AB6 fill:#ffcdd2
graph TB subgraph "Arquitetura com Bulkheads (Celular)" BC1[Shard Router] subgraph "Célula B" APPB[App B] --> DBB[Database B] end subgraph "Célula A - Falha Isolada" APPA[App A] --> DBA[Database A] --> FALHA[💥 Falha no Shard] end subgraph "Célula C" APPC[App C] --> DBC[Database C] end BC1 --> APPA BC1 --> APPB BC1 --> APPC end style FALHA fill:#ffcdd2

A implementação demonstra este isolamento através da estrutura de proxy reverso:

1
2
3
4
5
6
7
8
9
10
11
12
func (ph *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    shardKey := ph.router.GetShardingKey(r)
    shardURL := ph.router.GetShardHost(shardKey)
    
    // Isolamento: falha em um shard não afeta outros
    client := &http.Client{}
    resp, err := client.Do(proxyReq)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadGateway)
        return
    }
}


Observabilidade e Métricas

A observabilidade é crucial para operação de sistemas distribuídos (Majors et al., 2022). A PoC implementa coleta de métricas usando Prometheus, fornecendo visibilidade sobre:

  • Distribuição de requisições por shard
  • Taxa de sucesso/falha por célula
  • Latência de processamento
  • Utilização de recursos
graph TB subgraph "Sistema Observável" SO1[Cellular Router] subgraph "Células" SC1[Célula A] SC2[Célula B] SC3[Célula C] end subgraph "Coleta de Métricas" CM1[Prometheus Metrics] CM2[Request Counter] CM3[Response Counter] CM4[Health Checks] end subgraph "Visualização" SV1[Grafana Dashboard] SV2[📊 Distribuição por Shard] SV3[📈 Taxa de Sucesso/Falha] SV4[⏱️ Latência por Célula] SV5[🎯 Detecção de Hotspots] end SO1 --> SC1 SO1 --> SC2 SO1 --> SC3 SC1 --> CM1 SC2 --> CM1 SC3 --> CM1 CM1 --> CM2 CM1 --> CM3 CM1 --> CM4 CM1 --> SV1 SV1 --> SV2 SV1 --> SV3 SV1 --> SV4 SV1 --> SV5 end style CM1 fill:#e3f2fd style SV1 fill:#e8f5e8 style SV5 fill:#fff3e0


1
2
3
4
5
6
7
8
type PrometheusMetricsRecorder struct {
    requestsCounter prometheus.CounterVec
    responseCounter prometheus.CounterVec
}

func (pm *PrometheusMetricsRecorder) RecordRequest(shard string) {
    pm.requestsCounter.WithLabelValues(shard).Inc()
}


Arquitetura da Solução

Visão Geral do Sistema

A arquitetura implementada na PoC segue o padrão de proxy reverso com roteamento baseado em hashing consistente. O sistema é composto por três camadas principais:

  1. Camada de Roteamento: Responsável por receber requisições e determinar o shard de destino
  2. Camada de Hash Ring: Implementa o algoritmo de hashing consistente
  3. Camada de Células: Conjunto de serviços independentes (shards)
graph TB subgraph "Cliente" C1[Aplicação Cliente] C2[Header: client-id] end subgraph "Camada de Roteamento" R1[HTTP Server :8080] R2[Proxy Handler] R3[Shard Router] end subgraph "Camada Hash Ring" H1[Consistent Hash Ring] H2[SHA-512 Algorithm] H3[Virtual Replicas] end subgraph "Células (Shards)" S1[Célula A
Domain Shard] S2[Célula B
Domain Shard] S3[Célula C
Domain Shard] SN[Célula N
Domain Shard] end subgraph "Observabilidade" M1[Prometheus Metrics] M2[Health Checks] end C1 --> C2 C2 --> R1 R1 --> R2 R2 --> R3 R3 --> H1 H1 --> H2 H2 --> H3 H3 --> S1 H3 --> S2 H3 --> S3 H3 --> SN R2 --> M1 R1 --> M2 style S1 fill:#e1f5fe style S2 fill:#e1f5fe style S3 fill:#e1f5fe style SN fill:#e1f5fe


Fluxo de Processamento

O fluxo de processamento de uma requisição na arquitetura celular segue as seguintes etapas:

sequenceDiagram participant C as Cliente participant P as Proxy Router participant H as Hash Ring participant S as Shard (Célula) participant M as Métricas C->>P: HTTP Request + client-id header P->>P: Extrair sharding key P->>H: GetNode(client-id) H->>H: Calcular hash SHA-512 H->>H: Localizar no ring H-->>P: URL do shard destino P->>M: Registrar requisição P->>S: Proxy request S-->>P: Response P->>M: Registrar resposta P-->>C: HTTP Response


Algoritmo de Distribuição

O algoritmo de hashing consistente implementado utiliza réplicas virtuais para melhorar a distribuição uniforme das chaves:

graph LR subgraph "Hash Ring" direction TB R1[Replica Shard-1-0
Hash: 0x1A2B] R2[Replica Shard-2-0
Hash: 0x3C4D] R3[Replica Shard-1-1
Hash: 0x5E6F] R4[Replica Shard-3-0
Hash: 0x7890] R5[Replica Shard-2-1
Hash: 0x9ABC] R6[Replica Shard-3-1
Hash: 0xDEF0] end K1[Key: user-123
Hash: 0x4567] --> R3 K2[Key: user-456
Hash: 0x8901] --> R6 K3[Key: user-789
Hash: 0x2345] --> R2 style R1 fill:#ffcdd2 style R3 fill:#ffcdd2 style R2 fill:#c8e6c9 style R5 fill:#c8e6c9 style R4 fill:#e1bee7 style R6 fill:#e1bee7


Análise de Desempenho dos Algoritmos de Hash

Metodologia de Avaliação

Para validar a eficácia dos diferentes algoritmos de hash na distribuição uniforme de chaves, foram realizados experimentos com 1 milhão de chaves UUID v4 distribuídas entre 3 shards. As chaves UUID v4 foram escolhidas por sua natureza aleatória e representatividade em cenários reais de produção. Os critérios de avaliação incluíram:

  • Uniformidade de distribuição: Medida pelo desvio padrão da distribuição
  • Variância: Indicador de dispersão dos valores
  • Diferença máxima: Distância entre o shard com maior e menor carga


Resultados Experimentais

A Tabela 1 apresenta os resultados comparativos dos algoritmos testados:

Análise Comparativa de Algoritmos de Hash

Algoritmo Desvio Padrão Variância Melhor Shard (%) Pior Shard (%) Diferença
SHA1 11.03 121.67 32.0% 34.7% 2.7%
SHA512 28.31 801.67 30.5% 37.2% 6.7%
SHA256 64.60 4173.67 26.3% 41.9% 15.6%
MD5 42.05 1768.33 28.2% 38.5% 10.3%
MURMUR3 95.84 9185.33 23.1% 48.2% 25.1%



Discussão dos Resultados

Os resultados evidenciam que o SHA1 apresenta a melhor distribuição uniforme, com menor desvio padrão (11.03) e diferença entre shards (2.7%). Este comportamento contraria expectativas iniciais que favoreciam SHA-512 devido à maior complexidade criptográfica.

O SHA-512, embora apresente distribuição aceitável (desvio padrão: 28.31), demonstra performance inferior ao SHA1 em termos de uniformidade. Contudo, mantém características criptográficas superiores, relevantes para cenários que exigem resistência a ataques de hash.

Algoritmos não-criptográficos como MURMUR3 apresentaram distribuição menos uniforme que esperado, contradizendo literatura que sugere sua superioridade em aplicações de hashing distribuído (Appleby, 2008).


Propriedades da Arquitetura Celular

Determinismo de Roteamento

Uma propriedade fundamental da arquitetura celular é o determinismo de roteamento. Requisições com a mesma chave de sharding são consistentemente direcionadas à mesma célula, independentemente do momento da requisição ou estado do sistema.

1
2
3
4
5
6
func (sr *ShardRouterImpl) GetShardHost(key string) string {
    node := sr.hashRing.GetNode(key)
    fmt.Printf("[%s] Mapping key %s to host: %s\n", 
               sr.hashRing.GetHashAlgorithm(), key, node)
    return node
}

Esta propriedade é essencial para:

  • Manutenção de cache local por célula
  • Consistência de sessão de usuário
  • Otimização de consultas relacionadas por cliente

Escalabilidade Horizontal

A arquitetura permite adição dinâmica de células sem interrupção do serviço. O uso de hashing consistente garante redistribuição mínima de chaves (aproximadamente K/N chaves movidas, onde K é o total de chaves e N o número de nós).

Tolerância a Falhas

O isolamento entre células proporciona contenção de falhas. A indisponibilidade de uma célula afeta apenas os clientes mapeados para aquela célula específica, mantendo o restante do sistema operacional.

Observabilidade Granular

O roteamento determinístico facilita observabilidade granular por célula, permitindo:

  • Métricas específicas por domínio de clientes
  • Detecção de hotspots de tráfego
  • Análise de padrões de uso por segmento


Implementação e Aspectos Técnicos

Padrões de Projeto Aplicados

A implementação utiliza diversos padrões consolidados:

Strategy Pattern: Para algoritmos de hash intercambiáveis

1
2
3
4
5
6
type HashAlgorithm string
const (
    SHA512  HashAlgorithm = "SHA512"
    SHA256  HashAlgorithm = "SHA256"
    // ...
)

Proxy Pattern: Para roteamento transparente de requisições

1
2
3
4
type ProxyHandler struct {
    router          interfaces.ShardRouter
    metricsRecorder interfaces.MetricsRecorder
}

Factory Pattern: Para criação de componentes configuráveis

1
2
3
4
5
6
7
8
func NewConsistentHashRing(numReplicas int) interfaces.HashRing {
    ring := &ConsistentHashRing{
        Nodes:       []Node{},
        NumReplicas: numReplicas,
    }
    ring.configureHashAlgorithm()
    return ring
}


Limitações e Trabalhos Futuros

Limitações Identificadas

  1. Rebalanceamento: Não há implementação automática de rebalanceamento quando células ficam sobrecarregadas
  2. Descoberta de Serviços: Configuração estática de shards limita elasticidade
  3. Consistência Cross-Cell: Transações que envolvem múltiplas células não são suportadas
  4. Circuit Breaker: Ausência de proteção contra cascata de falhas

Extensões Propostas

  1. Auto-scaling Celular: Algoritmos para adição/remoção automática de células baseado em métricas de carga
  2. Service Mesh Integration: Integração com Istio/Linkerd para descoberta de serviços e políticas de tráfego
  3. Distributed Tracing: Implementação de rastreamento distribuído para análise de latência cross-cell
  4. Consensus Protocols: Integração com Raft/PBFT para coordenação entre células


Referências

Appleby, A. (2008). MurmurHash3. SMHasher. https://github.com/aappleby/smhasher

DeCandia, G., Hastorun, D., Jampani, M., Kakulapati, G., Lakshman, A., Pilchin, A., … & Vogels, W. (2007). Dynamo: Amazon’s highly available key-value store. ACM SIGOPS operating systems review, 41(6), 205-220. https://doi.org/10.1145/1323293.1294281

Karger, D., Lehman, E., Leighton, T., Panigrahy, R., Levine, M., & Lewin, D. (1997). Consistent hashing and random trees: Distributed caching protocols for relieving hot spots on the World Wide Web. Proceedings of the twenty-ninth annual ACM symposium on Theory of computing, 654-663. https://doi.org/10.1145/258533.258660

Majors, C., Fong-Jones, L., & Miranda, G. (2022). Observability engineering: Achieving production excellence. O’Reilly Media.

Newman, S. (2015). Building microservices: Designing fine-grained systems. O’Reilly Media.

Nygard, M. T. (2018). Release it!: Design and deploy production-ready software (2nd ed.). Pragmatic Bookshelf.

Özsu, M. T., & Valduriez, P. (2020). Principles of distributed database systems (4th ed.). Springer. https://doi.org/10.1007/978-3-030-26253-2

Richardson, C. (2018). Microservices patterns: With examples in Java. Manning Publications.

comments powered by Disqus