Um caso técnico completo: reserva de estoque com concorrência
Condições de corrida, chaves de idempotência e outbox transacional — simulados passo a passo.
11.1O trabalho começa pelo contrato
Nomeie os invariantes antes de delegar
“Crie um endpoint de reserva” deixa decisões importantes em aberto. O engenheiro identifica primeiro o que deve permanecer verdadeiro — inclusive sob falha — e resolve questões de domínio, como expiração, com quem é responsável por elas.
- I1A quantidade disponível nunca fica negativa.
- I2Dentro da janela de retenção da chave, repetir a mesma intenção produz uma única reserva lógica.
- I3Reutilizar a chave com conteúdo diferente gera um resultado explicitamente definido.
- I4Um cliente só atua sobre seus próprios dados.
- I5Uma falha parcial nunca produz reserva sem evento rastreável.
- I6O resultado pode ser descoberto quando a conexão cai depois do commit.
objetivo: "Reservar unidades de um item para o cliente autenticado"escopo: inclui: [API de reserva, persistencia, evento, testes, telemetria] exclui: [pagamento, alteracao do fluxo de expedicao]invariantes: - "disponibilidade >= 0" - "uma reserva logica por cliente e chave durante a janela de retencao" - "mesma chave com payload diferente produz conflito" - "nenhum acesso a dados de outro cliente"evidencias: - "teste com requisicoes concorrentes disputando o ultimo item" - "teste de repeticao apos commit e antes da resposta" - "teste de isolamento entre clientes" - "teste de recuperacao da publicacao do evento"decisoes_pendentes: - "politica de expiracao da reserva e da chave"
O contrato só estará pronto para implementação completa depois de resolver as pendências que afetam a semântica.
11.2A fronteira de atomicidade
Duas requisições, uma última unidade
Uma solução ingênua lê a disponibilidade e depois grava a redução: duas requisições podem agir sobre uma premissa que deixou de ser verdadeira. Avance pelos dois desenhos.
Disputa pela última unidade
Estado compartilhado
- disponível
- 1
- bloqueio da linha
- —
- reservas concedidas
- 0
Aperte reproduzir ou avance passo a passo.
Ler, depois gravar
-- Fragmento ilustrativo: quantidade deve ser validada como inteira positiva.-- A identidade do cliente vem da autenticacao, nao do corpo da requisicao.UPDATE inventorySET available = available - :quantityWHERE tenant_id = :authenticated_tenant AND item_id = :item_id AND available >= :quantityRETURNING available;
11.3Falha parcial
Gravar a reserva e publicar o evento são duas operações
Se uma acontece e a outra falha, os sistemas divergem. A outbox transacional grava o evento na mesma transação e o publica depois. A entrega pode repetir; consumidores precisam lidar com duplicatas. [F30]
Banco + broker
Estado compartilhado
- reserva gravada
- sim
- evento na outbox
- —
- efeitos no consumidor
- 0
Aperte reproduzir ou avance passo a passo.
Escrita dupla
11.4Dividindo o trabalho
Frentes com interfaces definidas
Essas frentes podem ser pessoas, agentes ou ambos — em paralelo só onde as dependências permitem. A investigação inicial pode revelar uma decisão de domínio que precisa vir antes.
11.5Evidência além do caminho feliz
Os sete cenários que o engenheiro pede
Mocks não demonstram bloqueio e isolamento. Coordene a sequência relevante com barreiras — apenas lançar requisições juntas pode não reproduzir a disputa. Quando viável, uma versão deliberadamente vulnerável prova que o teste detecta o defeito. Marque o que seus testes cobrem.
0 / 7
11.6Implantável e observável
Entregue com sinais e condições de interrupção
A entrega inclui a alteração, a migração compatível, os testes e os sinais de operação — com evidência vinculada à versão validada. Numa implantação gradual, acompanhe conflitos de reserva, latência, erros, atrasos na outbox e divergências de invariantes, com condições de interrupção definidas antes da exposição. Reverter o binário não desfaz uma mutação de dados já persistida. [F33]
A maior contribuição humana foi intenção, concorrência, falha parcial, contratos, evidência independente e operação. A implementação foi um campo de execução delegável dentro dessas condições.
Pontos-chave
- 1
Comece pelos invariantes e resolva as decisões de domínio que mudam a semântica.
- 2
Coloque verificação e efeito na mesma operação atômica; ler-depois-gravar gera corrida.
- 3
Combine chaves de idempotência, outbox transacional e deduplicação no consumidor para falhas parciais.
- 4
Exija evidência além do caminho feliz com banco real e concorrência coordenada — e registre o que aprendeu.
Verifique seu entendimento
Pergunta 1 de 3
Por que uma reserva do tipo ler-depois-gravar pode vender duas vezes a última unidade?
Ler a seção completa do relatório originalO texto integral — com todas as fontes e nuances.
Cenário ilustrativo criado para este relatório. O objetivo é mostrar como as competências se combinam; os exemplos não constituem uma implementação pronta para produção.
Uma aplicação precisa permitir que um cliente reserve unidades de um item. Existem múltiplos clientes organizacionais, requisições simultâneas, retries após timeout e um evento que informa a criação da reserva a outro serviço.
11.1. O trabalho começa pelo contrato
Pedir “crie um endpoint de reserva” deixa decisões importantes em aberto. Antes de delegar, o engenheiro identifica os invariantes:
- A quantidade disponível nunca pode ficar negativa.
- Durante a janela contratada de retenção da chave, repetir a mesma intenção deve produzir uma única reserva lógica.
- Reutilizar a mesma chave com conteúdo diferente precisa gerar um resultado explicitamente definido.
- Um cliente só pode atuar sobre seus próprios dados.
- Uma falha parcial não pode produzir reserva sem rastreabilidade do evento correspondente.
- O sistema deve permitir descobrir o resultado quando a conexão cai depois da confirmação no banco.
O engenheiro também resolve com o domínio se a reserva expira, como é cancelada e por quanto tempo a chave de idempotência continua válida. O contrato deve dizer o que ocorre quando a chave expira e quando a reserva expira antes dela: recuperar a operação original ou iniciar outra intenção com uma nova chave são comportamentos distintos. Essas decisões alteram a modelagem.
Um contrato resumido poderia ser:
objetivo: "Reservar unidades de um item para o cliente autenticado"
escopo:
inclui: [API de reserva, persistencia, evento, testes, telemetria]
exclui: [pagamento, alteracao do fluxo de expedicao]
invariantes:
- "disponibilidade >= 0"
- "uma reserva logica por cliente e chave durante a janela de retencao"
- "mesma chave com payload diferente produz conflito"
- "nenhum acesso a dados de outro cliente"
evidencias:
- "teste com requisicoes concorrentes disputando o ultimo item"
- "teste de repeticao apos commit e antes da resposta"
- "teste de isolamento entre clientes"
- "teste de recuperacao da publicacao do evento"
decisoes_pendentes:
- "politica de expiracao da reserva e da chave"
Os campos são um modelo ilustrativo. O contrato só estará pronto para implementação completa depois de resolver as pendências que afetam a semântica.
11.2. O engenheiro define a fronteira de atomicidade
Uma solução ingênua lê a disponibilidade e depois grava a redução. Duas requisições podem observar o mesmo estoque e agir sobre uma premissa que deixou de ser verdadeira.
Para um item representado por uma única linha, uma operação condicional pode ser parte da solução:
-- Fragmento ilustrativo: quantidade deve ser validada como inteira positiva.
-- A identidade do cliente vem da autenticacao, nao do corpo da requisicao.
UPDATE inventory
SET available = available - :quantity
WHERE tenant_id = :authenticated_tenant
AND item_id = :item_id
AND available >= :quantity
RETURNING available;
No PostgreSQL, a semântica de atualização concorrente em Read Committed inclui reavaliar a condição sobre a versão atualizada da linha após aguardar um concorrente. É essa propriedade documentada, junto das restrições do esquema, que deve fundamentar o raciocínio; o formato visual da consulta não prova sua adequação. F28
O fragmento não resolve sozinho idempotência, criação da reserva, autorização completa, expiração ou publicação de eventos. Um desenho possível garante uma única linha de estoque por (tenant_id, item_id) e usa uma restrição única para (tenant_id, idempotency_key) no registro de operações. A coordenação da chave, o decremento condicional, a reserva, o resultado recuperável e a inserção na outbox participam da mesma transação.
O caminho de repetição consulta o resultado confirmado e compara os parâmetros relevantes. O tratamento de concorrência precisa distinguir conflito, operação ainda não confirmada e resultado concluído, sem permitir um segundo efeito. O contrato também define se recusas, como estoque insuficiente, ficam associadas à chave ou permitem uma nova tentativa. Essa é uma proposta de desenho para o exemplo, cuja implementação exige validação própria.
O contrato de repetição também precisa distinguir a mesma intenção de outra operação com parâmetros iguais. A abordagem de identificadores explícitos apresentada pela AWS ajuda a formular esse aspecto. F29
11.3. O evento exige tratamento de falha parcial
Gravar a reserva e publicar diretamente em um broker são duas operações distintas. Se uma acontecer e a outra falhar, pode haver divergência.
Uma possibilidade é gravar um evento em uma outbox na mesma transação da reserva e usar um publicador para entregá-lo posteriormente. Esse padrão trata a fronteira entre persistência e publicação. A entrega pode repetir mensagens; consumidores precisam lidar com duplicatas conforme o contrato. A documentação da AWS descreve o padrão e suas condições. F30
O engenheiro decide se esse desenho corresponde aos requisitos e define como observar atrasos, retentar publicação e reconhecer eventos processados. A IA pode implementar cada componente e produzir alternativas. A coerência do contrato entre eles permanece uma decisão de engenharia.
Para o exemplo, cada evento tem um identificador estável. O consumidor pode registrar esse identificador e aplicar seu efeito na mesma transação local, ou executar uma operação cujo contrato seja idempotente. Se o efeito ocorre em outro serviço, a garantia depende também desse serviço. A outbox não fornece, sozinha, efeito único no destino.
11.4. O trabalho é dividido com interfaces já definidas
Uma organização possível seria:
| Frente | Entrega | Condição de integração |
|---|---|---|
| Investigação | Mapa do código existente e das regras de estoque | Referências verificadas a arquivos e comportamento atual. |
| Implementação | Alteração de API e persistência | Contrato de idempotência e esquema acordados. |
| Verificação | Cenários de concorrência, repetição e isolamento | Critérios derivados dos invariantes e uso de banco real. |
| Operação | Telemetria, migração e plano de implantação | Compatibilidade com o ambiente e sinais de falha definidos. |
Essas frentes podem ser pessoas, agentes ou combinações. A divisão só deve ser paralela onde as dependências permitirem. A investigação inicial pode revelar que uma decisão de domínio precisa ser resolvida antes de qualquer implementação.
11.5. A evidência vai além do caminho feliz
O engenheiro pede resultados observáveis para cenários específicos:
- Duas requisições disputam a última unidade: apenas uma obtém a reserva.
- Duas requisições usam a mesma chave: há um único efeito no estoque.
- A mesma chave reaparece com outra quantidade: ocorre o conflito especificado.
- O processo termina depois do commit e antes da resposta: a repetição recupera o resultado definido.
- Um cliente tenta referenciar o item de outro: não há exposição nem mutação indevida.
- O broker aceita o evento, mas o publicador termina antes de registrar a publicação: a retomada pode repetir a mensagem; a deduplicação ou idempotência do consumidor evita um novo efeito.
- A reserva expira durante operações concorrentes: a regra acordada preserva consistência.
Mocks isolados podem auxiliar testes locais, mas não demonstram o comportamento de bloqueio e isolamento do banco. Os testes pertinentes devem exercitar esse comportamento, coordenando a sequência relevante de operações com barreiras ou pontos de sincronização. Apenas lançar requisições juntas pode deixar de reproduzir a disputa desejada. Quando viável, introduzir uma versão deliberadamente vulnerável à corrida permite confirmar que o teste detecta o defeito pretendido.
11.6. O resultado precisa ser implantável e observável
A entrega inclui a alteração, a migração compatível, os testes pertinentes e os sinais de operação. A evidência deve identificar a versão validada. Se a integração com outra mudança alterar o comportamento, a validação correspondente precisa ser repetida sobre o resultado integrado.
Para uma implantação gradual, podem ser acompanhados conflitos de reserva, latência, erros, atrasos na outbox e divergências de invariantes. As condições de interrupção devem ser definidas antes da exposição. Mudanças de dados exigem cuidado próprio: reverter o binário não desfaz automaticamente uma mutação já persistida.
O capítulo sobre canarying do Google SRE oferece uma referência para combinar automação de releases, mudanças pequenas e avaliação de uma versão antes de ampliar sua exposição. F33
11.7. Onde esteve a competência do engenheiro
A maior contribuição humana esteve em definir a intenção, reconhecer concorrência e falhas parciais, escolher os contratos, separar frentes, exigir evidência independente e preparar operação. A implementação foi um campo de execução delegável dentro dessas condições.
Ao terminar, o engenheiro registra os aprendizados úteis: um exemplo de teste de concorrência, a política de idempotência, um procedimento de migração e os sinais de diagnóstico. A próxima tarefa semelhante começa de uma posição melhor.