Conversation
3ababd3 to
ee5c609
Compare
3b69ad2 to
c803f47
Compare
Add a compile-time feature that extends the idempotency_guard! macro to check for matching idempotency keys stored in event contexts, in addition to the existing pattern-matching behavior. When enabled: - EventContext gains set_idempotency_key() and idempotency_key() methods - ContextData gains idempotency_key() accessor - idempotency_guard! checks both key matches and pattern matches - Break pattern continues scanning for key matches after pattern break Usage requires iter_persisted() instead of iter_all() to access PersistedEvent items with context data. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
c803f47 to
d125c6b
Compare
HonestMajority
left a comment
There was a problem hiding this comment.
Cool solution! I like the magic part of it. Just have a couple of questions about side effects of this change
| // systems to protect against replays. | ||
| idempotency_guard!( | ||
| self.events.iter_all().rev(), | ||
| self.events.iter_persisted().rev(), |
There was a problem hiding this comment.
Are there any implications of this change? That we are no longer protected from a double command inside one atomic db op? Just trying to think through if this is a problem or not. Does it mean we can call user.update_name() with the same name (and same idempotency id) in the same db op?
|
|
||
| ### Break Pattern Behavior | ||
|
|
||
| When using the break pattern (`=>`) with the `idempotency-key` feature, the macro continues scanning all events for idempotency key matches even after the break pattern matches. This ensures that duplicate requests are always detected regardless of where they appear in the event history: |
There was a problem hiding this comment.
The normal case will be that there is no match for the idempotency key, meaning we generally will scan through the whole event history for each entity mutation, IIUC. What do you think about the performance implications of this? It is all in memory, so my guess is that it should generally be fine. Do you anticipate any problems, and any potential solutions?
Summary
Add idempotency-key feature for context-based duplicate detection in the idempotency_guard! macro.
Solves the "retry vs new request" problem - e.g., two $100 withdrawals can't be distinguished by pattern matching alone. With idempotency keys, the external request ID identifies duplicates.
Changes
Usage
EventContext::current().set_idempotency_key(format!("payment-{}", payment_id));
idempotency_guard!(
self.events.iter_persisted().rev(),
OrderEvent::PaymentApplied { payment_id: pid, .. } if pid == &payment_id
);