Skip to content
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

Add batching support #509

Open
wants to merge 37 commits into
base: master
Choose a base branch
from

Conversation

cdegroot
Copy link
Contributor

@cdegroot cdegroot commented Oct 31, 2022

This adds the option of having batched event handlers to speed things up. Please refer to the included documentation for details.

Related pull requests:

We (www.metrist.io) are running this in production with the following versions:

├── commanded (https://github.com/Metrist-Software/commanded.git - batching-support) *override*
...
├── commanded_ecto_projections (https://github.com/Metrist-Software/commanded-ecto-projections.git - batching-support) *override*
│   ├── commanded (https://github.com/Metrist-Software/commanded.git - origin/batching-support)
...
├── commanded_eventstore_adapter ~> 1.2 (Hex package)
│   ├── commanded ~> 1.4 (Hex package)
│   ├── eventstore ~> 1.3 (Hex package)

For comments spanning PRs, please use Slack

davydog187 and others added 30 commits August 17, 2022 09:44
- upcast test for handle_batch/2
- batch_reset_event_handler_test
Copy link
Member

@slashdotdash slashdotdash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pull request @cdegroot it's a long requested feature. I've made a few minor comments or questions.

Comment on lines 1096 to 1108
defp handle_batch(events, context, %Handler{last_seen_event: last_seen_event} = state) do
%{event_number: last_event_number} = last_event = List.last(events)

if not is_nil(last_seen_event) and last_event_number <= last_seen_event do
Logger.debug(fn ->
describe(state) <> " has already seen event ##{inspect(last_event_number)}"
end)

confirm_receipt([last_event], state)
else
do_handle_batch(events, context, state)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list of events can be filtered to remove any that have already been seen, then the remainder of the batch can be handled.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Done.

Comment on lines 509 to 514
In both cases, the error handler will be called. When just a reason is
returned, the assumption is that a system issue is preventing the process
from continuing and the error handler will be called with a `nil` event
argument. When an event and a reason are returned, the assumption is that
something is wrong with the event itself and therefore, it is passed in
as the first argument for the error handler.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing nil to the error callback function will break the existing contract. Could we pass the first event in the batch to prevent a breaking change and for simplicity?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this was mismatched docs and implementation. Currently when no specific event is passed, we send the last event in the batch to allow skipping the entire batch. I've updated these docs with the assumption of that behaviour being ok. Otherwise we can update it to send the first event (and the docs accordingly)

Comment on lines +746 to +749
if Keyword.has_key?(config, :concurrency) and Keyword.has_key?(config, :batch_size) do
raise ArgumentError,
"both `:concurrency` and `:batch_size` are specified, this is not yet supported. Please choose one or the other."
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why both of these options cannot be supported by Commanded? The eventstore will support batch size for multiple consumers and will attempt to distribute events appropriately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use concurrency at the moment, and this is a bit of a scratch-your-own-itch PR, of course (as usual). Thinking through single-threaded batching was already complicated enough and we really want to dogfood code we submit, so we decided to make it a "not supported" thing for now. The "yet" is there to entice someone who does want/need it to fix that gap :)

Comment on lines 550 to 551
- `:skip` - skip the failed event by acknowledging receipt. Note that this is not
a valid return value in batching mode.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment still applicable, as it appears that :skip is supported for batch errors?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, :skip is indeed supported, this just got missed as part of that. I've taken that bit out and clarified it's affects on batching

@nikkocampbell
Copy link

Apologies for the delay on addressing your comments @slashdotdash. They should all be more or less addressed now, let us know if there's anything else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants