Skip to content
About
Main content

Trigger-based CDC

What do triggers buy in fidelity, and what do they cost the source on every write?

Where no readable log exists, the only remaining place to observe a mutation is inside the transaction that performs it. So you install a : a procedure the database runs automatically on each row change. The trigger writes a record to a (a table that holds the change history).

-- A trigger that records every change to orders in a shadow table
CREATE TABLE orders_changes (
    change_id  bigserial PRIMARY KEY,
    op         char(1),            -- I, U or D
    order_id   bigint,
    old_row    jsonb,
    new_row    jsonb,
    changed_at timestamptz DEFAULT now()
);

CREATE FUNCTION capture_orders() RETURNS trigger AS $$
BEGIN
  IF TG_OP = 'INSERT' THEN
    INSERT INTO orders_changes(op, order_id, new_row)
    VALUES ('I', NEW.order_id, to_jsonb(NEW));
  ELSIF TG_OP = 'UPDATE' THEN
    INSERT INTO orders_changes(op, order_id, old_row, new_row)
    VALUES ('U', NEW.order_id, to_jsonb(OLD), to_jsonb(NEW));
  ELSE
    INSERT INTO orders_changes(op, order_id, old_row)
    VALUES ('D', OLD.order_id, to_jsonb(OLD));
  END IF;
  RETURN NULL;
END $$ LANGUAGE plpgsql;

CREATE TRIGGER orders_cdc AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW EXECUTE FUNCTION capture_orders();

The benefit: capture is transactionally consistent for free. The change record or aborts together with the change, so you can never observe a change that did not happen.

The cost: the write amplification lands on the write path, synchronously. Typical overhead is 2–5× per statement, and worse for bulk operations, which now run row by row. You have also put a second writer inside every transaction: the shadow table's hot rows, indexes and locks now join the source's contention surface. (A "hot key" is one row that many transactions fight over; the shadow table's newest rows become exactly that.)

The trade is stark: Both pay; neither escapes. This is the conserved-cost rule again: the cost moved, it was not removed.

In one line: triggers buy transactional fidelity with the source's own write latency.