You: "Transactional outbox.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # ====================================================================== # Q: CTO: "How do you publish the OrderPaid event so it is not lost if the # broker goes down?" # # You: "Transactional outbox. The order change and the outbox row are written in # the same transaction. A relay publishes the rows that have not been sent. With # skip_locked, multiple relays can run without picking up the same events. # Delivery is at-least-once, so consumers deduplicate by event_id." # ====================================================================== with transaction.atomic(): order.mark_paid() OutboxEvent.objects.create(event_type='order.paid', aggregate_id=order.id, payload={'order_id': order.id}) @shared_task def relay_outbox(): with transaction.atomic(): batch = (OutboxEvent.objects.select_for_update(skip_locked=True) .filter(published_at=None).order_by('id')[:100]) for ev in batch: broker.publish(ev.event_type, ev.payload, message_id=str(ev.id)) ev.published_at = timezone.now() ev.save(update_fields=['published_at']) |