ChaosSQL

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.

G-DL

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.sql & seed.sql
-- 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)

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 ──► Child

Delta 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

PostgreSQL, MySQL (InnoDB), SQLite

remediation_fix.sql
-- 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;