Hi all,
I'm struggling to find the best way to deal with event dispatching
problems that arise in a distributed system in particular
circumstances.
Consider the following generic use case UC1:
UC1-1. User submit command C1: CreateUser(name="Bob",
email="bob@foo.com")
UC1-2. C1 is dispatched to the pertaining handler
UC1-3. Command handling logic raise event E1: UserCreated(name="Bob",
email="bob@foo.com")
UC1-4. E1 is dispatched
UC1-5 Event handling logic submit a new command C2:
NotifyUser(email="bob@foo.com", ...)
This use case, developed in a single instance scenario, is depicted in
the following picture: http://bit.ly/pFKNpI
Now suppose to have a distributed scenario, with 2 instances, named A
and B for semplicity,
In this scenario the Command Bus is backed by a load balancing
Distributed Queue and the Event Bus is backed by a Distributed Topic.
Now the same use case, renamed UC2, become:
UC2-1...
UC2-2 C1 is dispatched to the pertaining handler of instance A (the
instance is determined by the load balancing policy)
UC2-3...
UC2-4a E1 is dispatched to instance A
UC2-4b E1 is dispatched to instance B
UC2-5a Event handling logic submit a new command C2:
NotifyUser(email="bob@foo.com", ...)
UC2-5b Event handling logic submit a new command C3:
NotifyUser(email="bob@foo.com", ...)
The following picture depicts this scenario: http://bit.ly/oRAblO
Clearly the command C3, submitted by step 5b, is an undesidered
duplicate.
By the way, there are a lot of other cases when such duplication
doesn't necessarely drive to negative effects but on the contrary is
higly desiderable or even necessary.
Suppose to change step 5 of the previous use case, now renamed UC3, in
this way:
UC3-5 Update client UI.
Now, in the same distributed scenario of the previous case, each node
can perform the same task and update data displayed on the screen of
local connected clients.
Another use case (UC4) may be the one that change again step 5 in this
way:
UC4-5 Submit new command C4: ActivateUser(name="Bob", ...)
Again step 5, issued in the same distributed scenario, would result
in a duplicate command, but compared to step UC2-5b, this doesn't lead
to a negative side effect.
To put in a nutshell, in a distributed system, an event dispatched to
an instance cannot be notified to each listener without prior
considering if its exection can lead to an undesidered side effect.
Such undesidered effect cannot occur if the listener business logic is
idempotent (UC4) or locally applied (UC3) , in all the other cases the
event must be ignored if the event itself is not locally raised.
In my opinion, one way to address this constraint could be:
1. Extend EventHandler method annotation to accept arguments that
describe the idempotence (Yes, No) and, optionally, the scope of the
eventual side effects (Distributed, Local) of the annotated method.
2. Enrich event metadata with hostname or ipaddress of the originator
instance before publishing
3. Extend AnnotationEventListenerAdapter to verify whether the event
handled is local or, if remote originated, that target method is
idempotent, before proceeding to method invocation.
What do you think about?