Happy Thanksgiving to all partaking!
I have some questions regarding event scheduling using Axon, and really in event sourcing frameworks in general.
Context: I have an aggregate that is created with a property defining when something should happen. Let’s say the aggregate is
LeftoverFood and the property is
spoilsAt. I came up with a few ways of implementing this pattern:
A service can run a spring-scheduled cron job that checks a list of leftovers for those that need to be marked as spoiled. (It could also schedule a task for each aggregate, but the cron job is better for scalability and doesn’t sacrifice granularity in this case.) . The service listens for a
LeftoverFoodCreatedevent, adding the target leftovers to a list of leftovers that may spoil in the future. The service triggers a
SpoilLeftoverscommand near the appropriate time, and removes the food on a
LeftoverFoodaggregate can use the
QuartzEventSchedulerto schedule an event at a future time. I could not find a concrete example for using the event scheduler, but the example for
DeadlineManagersuggested that the event should be scheduled during a command handler such that it wont be rescheduled during a replay.
I think both of these patterns have benefits and drawbacks in the event souring world. If we consider a timer to be part of the state of the system, the first solution seems more correct. Replaying the events will set the timer to the correct point. The drawback is that the timer will only be set on one worker, and will not persist across JVM reboots. This also requires that all timer-related events are routed to the same instance. In addition, it becomes possible for the timer to go off multiple times since it is recreated during each replay, and the time may be in the past, although this does not guarantee that the desired effect was achieved. The resulting command handler must have logic preventing it from being repeated.
The second solution can be used in a cluster and persists across reboots (using JDBC), but in many ways has been removed from the state of the system produced by events. Replaying the events will not recreate the timer (when created by a command handler), and you’re now trusting that Quartz does its job properly. Further, the timer could go off during a replay since the quartz sub-system is operating on other threads. What issues would this cause? Overall this seems more reliable than using spring scheduler, but doesn’t feel correct in an event-sourced world.
Is there a workflow that allows the Quartz event scheduler to be more “evented”? Perhaps a timer should actually be an aggregate, and part of initialization (after replay) would be to schedule timers that have not yet fired.