GH GambleHub

Bloqueios distribuídos

1) Para quê (e quando) bloqueios distribuídos

O bloqueio distribuído é um mecanismo que garante a interconexão de uma seção crítica entre vários nós do cluster. Tarefas típicas:
  • Liderança para a tarefa de fundo/shedooler.
  • Limite o único executor sobre o recurso compartilhado (mover arquivos, migrar o esquema, fazer pagamentos exclusivos).
  • Processamento em sequência de uma unidade (wallet/order) se não for possível obter idempotação/ordenamento de outra forma.
Quando é melhor não usar a fechadura:
  • Se você puder fazer upsert idempotental, CAS (compare-and-set) ou fila de chave (per-key ordering).
  • Se o recurso permitir operações de comutação (CRDT, contadores).
  • Se o problema for resolvido por uma transação em um único armazém.

2) Modelo de ameaças e propriedades

Rejeitos e dificuldades:
  • Rede: atrasos, separação (partition), perda de pacotes.
  • Processos: pausa GC, stop-the-world, crash após a tomada do castelo.
  • Hora: A deriva do relógio e o deslocamento quebram as abordagens TTL.
  • «Zombies», depois da rede, pode pensar que ainda possui o castelo.
Propriedades desejadas:
  • Segurança: no máximo um dono válido (safety).
  • Vitalidade: O castelo é liberado quando o proprietário falhar (liveness).
  • Justiça, falta fome.
  • Independente do relógio: a correção não depende de wall-clock (ou compensado por fencing tocens).

3) Modelos básicos

3. 1 Lease (castelo de aluguel)

O castelo é emitido com a TTL. O proprietário é obrigado a prolongá-lo até o vencimento (heartbeat/keepalive).

Os benefícios são a auto-criatura do crash.
Riscos: Se o proprietário continua a trabalhar, mas perdeu a extensão, pode haver dupla posse.

3. 2 Fencing tocen (tornozelo de vedação)

A cada captura bem sucedida, o número cresce monotonicamente. Os consumidores do recurso (BB, fila, armazenamento de arquivos) verificam o token e rejeitam as transações com o número antigo.
Isso é essencial no TTL/lease e nas divisões de rede - protege contra o «antigo» dono.

3. 3 cadeados Quorum (Sistemas de CP)

Utilizam o consenso distribuído (Raft/Paxos; etcd/ZooKeeper/Consul), a gravação está associada a um tópico de consenso → não há split brain na maioria dos nós.

Além disso, garantias fortes de segurança.
Menos: sensibilidade ao quórum (quando perde a vitalidade).

3. 4 cadeados AP (in-memory/dinheiro + replicação)

Por exemplo, um cluster Redis. Alta disponibilidade e velocidade, mas sem segurança rígida nas divisões de rede. Exigem fencing do lado sink.

4) Plataformas e pattern

4. 1 etcd/ ZooKeeper/Consul (recomendado para strong locks)

Nós efêmeros (ZK) ou sessões/leases (etcd): a chave existe enquanto a sessão estiver viva.
Keepalive de sessão; perda de quórum → a sessão expira → o castelo é liberado.
Nós de ordem (ZK 'EPHEMERAL _ SEQUENTIAL') para a fila de espera → justiça.

Desenho em etcd (Go):
go cli, _:= clientv3. New(...)
lease, _:= cli. Grant(ctx, 10)            // 10s lease sess, _:= concurrency. NewSession(cli, concurrency. WithLease(lease. ID))
m:= concurrency. NewMutex(sess, "/locks/orders/42")
if err:= m. Lock(ctx); err!= nil { / handle / }
defer m. Unlock(ctx)

4. 2 Redis (com cuidado)

Clássico - 'SET key value NX PX ttl'.

Problemas:
  • A replicação/feelover pode permitir proprietários simultâneos.
  • Redlock de várias instâncias reduz o risco, mas não elimina; disputado em ambientes com rede precária.

É mais seguro usar o Redis como uma camada rápida de coordenação, mas sempre complementar o fencing tocen no recurso de destino.

Exemplo (Lua-unlock):
lua
-- release only if value matches if redis. call("GET", KEYS[1]) == ARGV[1] then return redis. call("DEL", KEYS[1])
else return 0 end

4. 3 cadeados BD

PostgreSQL advisory locks: lock dentro do cluster Postgres (processo/sessão).
Ainda bem que todas as secções críticas estão na mesma base.

SQL:
sql
SELECT pg_try_advisory_lock(42); -- take
SELECT pg_advisory_unlock(42); -- let go

4. 4 Cadeados de arquivo/nuvem

S3/GCS + cabelo de metadado de objeto com condições de 'If-Match' (ETAG) → essencialmente CAS.
Adequado para bacapes/migração.

5) Design de um castelo seguro

5. 1 Identidade do proprietário

Guarde 'owner _ id' (nó # processo # pid # start _ time) + token aleatório para combinação em unlock.
O unlock não deve ser removido.

5. 2 TTL e extensão

O TTL <T _ fail _ detect (hora da detecção da falha) e ≥ p99 para a seção crítica x reserva.
A extensão é intermitente (por exemplo, a cada 'TTL/3'), com deadline.

5. 3 Fencing tocen em sinca

A secção que altera o recurso externo deve transferir 'fencing _ tocen'.

Sink (BD/dinheiro/armazenamento) armazena 'last _ tocen' e rejeita os menores:
sql
UPDATE wallet
SET balance = balance +:delta, last_token =:token
WHERE id =:id AND:token > last_token;

5. 4 Fila de espera e justiça

ZK - 'EPHEMERAL _ SEQUENTIAL' e observadores: O cliente aguarda a libertação do antecessor mais próximo.
Em etcd - chaves com revisão/versionização; A prioridade é 'moon _ revision'.

5. 5 Comportamento de split-brain

Abordagem COP: sem quórum, não se pode pegar o cadeado - é melhor ficar parado do que quebrar o safety.
Abordagem AP: é permitido progresso em ilhas divididas → precisa de fencing.

6) Liderança (líder elation)

Em etcd/ZK - «líder» é uma chave epérmica exclusiva; o resto está assinado para alterações.
Líder escreve heartbeats; A perda é a reeleição.
Todas as operações do líder acompanhe fencing tocen (número de época/revisão).

7) Erros e processamento

O cliente pegou o cadeado, mas o crash antes de trabalhar, ninguém se magoa; TTL/sessão será liberado.

O castelo expirou no meio do trabalho:
  • É obrigatório watchdog: se a extensão não for possível, interromper a secção crítica e reverter/compensar.
  • Nada de «acabar depois», a secção crítica não pode continuar sem o castelo.

Longa pausa (GC/stop-the-world) → prolongamento não aconteceu, outro levou o castelo. O fluxo de trabalho deve detectar a perda de posse (canal keepalive) e interromper.

8) Dedlocks, prioridades e inversão

Os dedlocks no mundo distribuído são raros (o castelo é normalmente um), mas se os castelos forem múltiplos, mantenha uma ordem de tomada unificada (lock ordering).
Inversão de prioridades: o proprietário de baixa prioridade mantém o recurso enquanto os de alta prioridade esperam. Soluções: limites TTL, preempção (se o negócio permitir), recursos sharding.
Fome: use as filas de espera (nódulos de ordem ZK) para fazer justiça.

9) Observabilidade

Métricas:
  • `lock_acquire_total{status=ok|timeout|error}`
  • `lock_hold_seconds{p50,p95,p99}`
  • 'fencing _ token _ value' (monotonia)
  • `lease_renew_fail_total`
  • 'split _ brain _ prevented _ total' (quantas tentativas foram recusadas por falta de quórum)
  • `preemptions_total`, `wait_queue_len`
Logi/Training:
  • `lock_name`, `owner_id`, `token`, `ttl`, `attempt`, `wait_time_ms`, `path` (для ZK), `mod_revision` (etcd).
  • Spans "aquire critical seção" release "com o resultado.
Alerts:
  • Crescimento de 'lease _ renew _ fail _ total'.
  • `lock_hold_seconds{p99}` > SLO.
  • Cadeados «órfãos» (sem heartbeat).
  • Filas de espera inchadas.

10) Exemplos práticos

10. 1 Castelo Redis seguro com fencing (pseudo)

1. Armazenando o contador de tokens em um estoque seguro (por exemplo, Postgres/etcd).
2. Com o sucesso do 'SET NX PX', lemos/incorporamos o token e fazemos todas as alterações no recurso com a verificação do token no serviço/base de dados.

python acquire token = db. next_token ("locks/orders/42") # monotone ok = redis. set("locks:orders:42", owner, nx=True, px=ttl_ms)
if not ok:
raise Busy()

critical op guarded by token db. exec("UPDATE orders SET... WHERE id=:id AND:token > last_token",...)
release (compare owner)

10. 2 etcd Mutex + watchdog (Go)

go ctx, cancel:= context. WithCancel(context. Background())
sess, _:= concurrency. NewSession(cli, concurrency. WithTTL(10))
m:= concurrency. NewMutex(sess, "/locks/job/cleanup")
if err:= m. Lock(ctx); err!= nil { /... / }

// Watchdog go func() {
<-sess. Done ()//loss of session/quorum cancel ()//stop working
}()

doCritical (ctx )//must respond to ctx. Done()
_ = m. Unlock(context. Background())
_ = sess. Close()

10. 3 Liderança em ZK (Java, Curator)

java
LeaderSelector selector = new LeaderSelector(client, "/leaders/cron", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership() с try-finally и heartbeat

10. 4 Postgres advisory lock com dedline (SQL + app)

sql
SELECT pg_try_advisory_lock(128765); -- attempt without blocking
-- if false --> return via backoff + jitter

11) Playbooks de teste (Game Days)

Perda de quórum: desativar 1-2 nó etCD → tentar pegar o cadeado deve não passar.
GC-pausa/stop-the-world: reter artificialmente o fluxo do proprietário → verificar que watchdog interrompe o trabalho.
Split-brain: emulação da divisão de rede entre o proprietário e o estoro do castelo → o novo proprietário recebe uma fencing tocen mais alta, o antigo é rejeitado pelo sinuoso.
Clock skew/draft: Tirar o relógio do proprietário (para Redis/lease) → certificar-se de que a correção é fornecida por tokens/verificações.
Crash before release: queda do processo → fechadura é liberada por TTL/sessão.

12) Anti-pattern

Castelo TTL limpo sem fencing no acesso a recursos externos.
Dependa do tempo local para ser correto (sem HLC/fencing).
Distribuir cadeados através de um assistente Redis em um ambiente com feedback e sem confirmação de réplicas.
Secção crítica infinita (TTL «séculos»).
Retire o cadeado «alheio» sem «owner _ id »/tocen.
Falta de backoff + jitter → «tempestade» de tentativas.
Um único castelo global «para tudo» - um saco de conflitos; O charding é melhor com a chave.

13) Folha de cheque de implementação

  • O tipo de recurso está definido e se o CAS/fila/idimpotência pode ser pago.
  • Mecanismo selecionado: etcd/ZK/Consul para a COP; Redis/dinheiro - apenas com fencing.
  • Implementados: 'owner _ id', TTL + extensão, watchdog, unlock correto.
  • O recurso externo verifica fencing tocen (monotonia).
  • Há uma estratégia de liderança e failover.
  • As métricas, as alertas, a logação de tokens e as revisões são configuradas.
  • Estão previstos backoff + jitter e temporizações no aquire.
  • Realizado game days: quórum, split-brain, pausas GC, clock skew.
  • Documentação da ordem da tomada de vários cadeados (se necessário).
  • Plano de degradação (brownout): o que fazer quando a fechadura não estiver disponível.

14) FAQ

Q: O cadeado Redis 'SET NX PX' é suficiente?
A: Somente se o recurso verificar fencing tocen. Caso contrário, dois proprietários podem ser divididos em rede.

Q: O que escolher por padrão?
A: Para garantias rigorosas - (COP). Para tarefas fáceis dentro de uma base de dados - advisory locks Postgres. O Redis é apenas com fencing.

Q: Que tipo de TTL colocar?
A: 'TTL p99 duração da secção crítica x 2' e curta o suficiente para limpar rapidamente os 'zombies'. Extensão a cada 'TTL/3'.

Como evitar a fome?
A: Fila de espera por ordem (ZK sequential) ou algoritmo fairness; limite de tentativas e planejamento justo.

O tempo precisa de sincronização?
A: Para ser correto, não (use fencing). Para previsibilidade operacional - sim (NTP/PTP), mas não se baseie em wall-clock na lógica do castelo.

15) Resultado

Bloqueios distribuídos confiáveis são construídos em estoques quórum (etcd/ZK/Consul) com lease + keepalive, e necessariamente completados com fencing tocen ao nível do recurso a ser alterado. Qualquer abordagem TTL/Redis sem vedação - risco de split brain. Pense primeiro em causosidade e idempotidade, use bloqueios onde não é possível, mede, teste os modos de falha - e suas «seções críticas» permanecerão críticas apenas no sentido e não no número de incidentes.

Contact

Entrar em contacto

Contacte-nos para qualquer questão ou necessidade de apoio.Estamos sempre prontos para ajudar!

Iniciar integração

O Email é obrigatório. Telegram ou WhatsApp — opcionais.

O seu nome opcional
Email opcional
Assunto opcional
Mensagem opcional
Telegram opcional
@
Se indicar Telegram — responderemos também por lá.
WhatsApp opcional
Formato: +indicativo e número (ex.: +351XXXXXXXXX).

Ao clicar, concorda com o tratamento dos seus dados.