If I write to the database and then send to the broker, one of the two can fail.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # ====================================================================== # Q: How do you safely publish an event together with a database change? (the # dual-write problem) # # If I write to the database and then send to the broker, one of the two can # fail. Solution: Transactional Outbox. In the same transaction I write the # order and a row in the outbox table. A relay (Celery beat, a separate worker, # or CDC with Debezium) reads the outbox and publishes. Consumers are idempotent # (an inbox table with processed event_ids). # ====================================================================== with transaction.atomic(): order = Order.objects.create(...) OutboxEvent.objects.create( event_type='order.placed', aggregate_id=order.id, payload={'order_id': order.id, 'total': str(order.total)}) # relay: every few seconds for ev in OutboxEvent.objects.select_for_update(skip_locked=True)\ .filter(published_at=None)[:100]: broker.publish(ev.event_type, ev.payload, message_id=str(ev.id)) ev.published_at = timezone.now(); ev.save(update_fields=['published_at']) |