New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sync command dispatch #82
Comments
@slashdotdash Great write-up! This looks like a great addition to Some feedback: (1) The method name (2) I'm not sure whether the addition of An alternative could be to add a list of event handlers to wait for on command dispatch. Example: :ok = Router.dispatch(%CreateArticle{...}, consistency: :strong, wait_for: [ArticleProjector])
# or only with :wait_for (consistency: :strong is assumed then)
:ok = Router.dispatch(%CreateArticle{...}, wait_for: [ArticleProjector]) (3) In the current approach, it's not entirely clear to me what happens if only The above are just some ideas, the original proposal would be great in itself without any changes! |
@slashdotdash Additional thought on (3): if the event handlers ultimately decide whether or not strong consistency is enforced, do we actually need a This still leaves point (2) above open for discussion: in practice, is it a good idea to have Event Handlers have the final say in this, or should this better be handled on dispatch? |
@florish Allowing dispatch with strong or eventual consistency allows the consumer to dictate their consistency requirements. Typically commands dispatched by event handlers or process managers can always run with eventual consistency since they are "background" tasks. In this scenario there's no need to incur the overhead of collecting & waiting for the strongly consistent event handlers. |
@florish Think I agree with you that adding Dispatching a command with |
@slashdotdash, I agree with @florish projections should not care whenever they used for strong consistency or not. It's should be decided when we dispatch command. |
Also I want to discuss about a more complex case when we need to wait multiple projections of same kind after dispatching a command. For example I have a command Or similar, I have a I think something like: dispatch(%CreateOrder{items: [item_uuid1, item_uuid2]}, wait_for: [%OrderItemProjectionUpdated{id: item_uuid1}, %OrderItemProjectionUpdated{id: item_uuid2}])`
dispatch(%CreateUser{id: user_id, roles: [%Customer{}, %Passenger{}]}, wait_for: [%UserProjectionUpdated{id: user_id}, %UserProjectionUpdated{id: user_id}]), %UserProjectionUpdated{id: user_id}])]` Can be helpful, but it's more complex, and in such cases you maybe prefer more refined control on events order and error output. |
This gist of |
@astery By using dispatch with consistency |
Dispatching a command returns an
:ok
response when successfully handled by the registered command handler or aggregate.This provides a strong consistency guarantee for the write side (in a CQRS application). You are guaranteed that the write succeeded once you receive the
:ok
.Problem description
Event handlers in Commanded are asynchronous when processing persisted events. This allows parallelisation of handlers for both performance and independence so that one slow handler doesn't negatively affect any other. The downside of this approach is eventual consistency for your read models; after successful command dispatch you have no way of knowing when the handlers have processed the events created by the command .
The current solution to eventual consistency in the read model is to use one of the following approaches: "fake" the response by using data from the command; poll the read model until it has been updated; use pub/sub from the read model to notify subscribers when the event has been processed.
This requires you to write boilerplate code for every command dispatch that needs to subsequently query the read model that will be updated by events created from the command. Most web apps use the POST/Redirect/GET pattern that is affected by this problem. As an example, after publishing a blog post the user should be redirected to view their new post.
Proposal
The proposed solution is to allow "synchronous" command dispatch and allow you to configure which event handlers should run with strong, not eventual, consistency.
Example
Add a
consistency
option to the commanddispatch
function:The supported consistency options would be
:eventual
(the current behaviour and default) and:strong
.For each event handler, and process manager, you would be able to specify its consistency guarantee.
Example
Article read model projection (an event handler) requests
:strong
consistency:An email sending event handler can be configured for
:eventual
consistency since it can safely run async:It would also be possible to define the consistency for process managers in exactly the same manner.
When dispatching a command using
consistency: :strong
the dispatch will block until all of the strongly consistent event handlers and process managers have handled all events created by the command. This guarantees that when you receive the:ok
response from dispatch your strongly consistent read models will have been updated and can safely be queried.Summary
This proposal would be backwards compatible with any existing usage. The default consistency guarantee will be eventual, as it already is. But you will now be allowed to opt-in to strong consistency when dispatching a command.
The text was updated successfully, but these errors were encountered: