Catálogo de Concorrência
Cenários Canônicos & Mitigações
Explore 10 cenários de falhas de concorrência baseados em pesquisas acadêmicas de isolamento e casos reais de sistemas financeiros e distribuídos.
Deadlock em Deleção em Cascata de Foreign Key
Inserções simultâneas em itens filhos e deleção em cascata do pedido pai invertem a hierarquia de bloqueios.
Inserções simultâneas em itens filhos e deleção em cascata do pedido pai invertem a hierarquia de bloqueios.
Schema e dados iniciais
-- Schema
CREATE TABLE parent_orders (
id INT PRIMARY KEY,
status TEXT NOT NULL,
total_cents INT NOT NULL
);
CREATE TABLE child_items (
id INT PRIMARY KEY,
order_id INT NOT NULL REFERENCES parent_orders(id) ON DELETE CASCADE,
sku TEXT NOT NULL,
price_cents INT NOT NULL
);
-- Seed
INSERT INTO parent_orders VALUES (1, 'OPEN', 5000);
INSERT INTO child_items VALUES (1, 1, 'ITEM-A', 2500), (2, 1, 'ITEM-B', 2500);Carga concorrente (chaos.yaml)
version: "1.0"
name: "foreign_key_cascade_deadlock"
operations:
- name: "add_order_item"
steps:
- "INSERT INTO child_items VALUES ($monotonic_counter(10, 1), 1, 'ITEM-NEW', 1500)"
- "UPDATE parent_orders SET total_cents = total_cents + 1500 WHERE id = 1"
- name: "cancel_order_cascade"
steps:
- "UPDATE parent_orders SET status = 'CANCELLED' WHERE id = 1"
- "DELETE FROM parent_orders WHERE id = 1"
invariants:
- name: "referential_integrity"
query: "SELECT count(*) AS orphan_items FROM child_items LEFT JOIN parent_orders ON child_items.order_id = parent_orders.id WHERE parent_orders.id IS NULL;"
assert: "orphan_items == 0"Por que a invariante quebra
T1 bloqueia a linha filha e requisita lock na linha pai para atualizar o total; T2 bloqueia a linha pai e requisita lock em cascata nos filhos para deletar. A inversão de hierarquia induz um deadlock no Wait-For Graph.
T1: Child ──► Parent ◄──► T2: Parent ──► ChildDelta debugging reduz 20 operações às 2 que causam a falha.
Correção em produção
Índice na Chave Estrangeira e Padronização de Locks Pai-Filho
Deadlocks em deleções em cascata ocorrem porque: (1) a ausência de índice na coluna de FK força table scans na tabela filha, gerando bloqueios abrangentes desnecessários; e (2) transações de inserção travam primeiro a tabela filha e depois o pai, enquanto cascatas travam primeiro o pai e depois os filhos. A criação do índice na FK associada ao bloqueio prévio do registro pai (FOR UPDATE) equaliza a hierarquia e extingue o risco de deadlock.
PostgreSQL, MySQL (InnoDB), SQLite
-- 1. Criar Índice Dedicado na Coluna de Chave Estrangeira (CRÍTICO!)
CREATE INDEX idx_child_items_order_id ON child_items(order_id);
-- 2. Padronizar a Sequência de Bloqueios Pai -> Filhos em Cancelamentos:
BEGIN;
-- Bloquear a linha pai explicitamente antes de disparar deleções:
SELECT id FROM parent_orders WHERE id = 1 FOR UPDATE;
-- Deletar os filhos de forma explícita e controlada:
DELETE FROM child_items WHERE order_id = 1;
-- Deletar o pedido pai:
DELETE FROM parent_orders WHERE id = 1;
COMMIT;