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

Rewindable event handler separation #440

Merged

Conversation

Palmr
Copy link
Contributor

@Palmr Palmr commented Oct 26, 2022

This should give us type-safe rewindable and non-rewindable EventHandler implementations.

Non-rewindable implementations will not be able to throw RewindableException but will be able to implement EventHandler::setSequenceCallback and the opposite for rewindable implementations.

This will fix #437

…able

Using `EventHandler::setSequenceCallback` with the newly added concept of
Rewinding in the `BatchEventProcessor` is not going to go well as an
`EventHandler` can change the sequence before a rewind.

Non-rewindable `EventHandler` implementations are not able to throw a
`RewindableException`.
@Palmr Palmr added the bug label Oct 26, 2022
@Palmr Palmr added this to the 4.0 milestone Oct 26, 2022
@Palmr Palmr requested review from sea36 and swarren12 October 26, 2022 14:18
@Palmr Palmr added this to In progress in 4.0 Oct 26, 2022
private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
private BatchRewindStrategy batchRewindStrategy = new SimpleBatchRewindStrategy();
private int retriesAttempted = 0;
private final boolean rewindable;
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than a boolean, could we have behaviour?

Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking something like adding an private inner interface:

private final interface RewindBehaviour
{
      long onRewindableException(RewindableException e);
}

and then there could be two private methods:

private long beKindRewind(final RewindableException e)
{
    if (this.batchRewindStrategy.handleRewindException(e, ++retriesAttempted) == REWIND)
    {
        return startOfBatchSequence;
    }
    else
    {
        retriesAttempted = 0;
        throw e;
    }
}

private long rewindNotAllowed(final RewindableException e)
{
    throw new RuntimeException("Rewindable Exception thrown from a non-rewindable event handler", e);
}

Constructors would then take this::beKindRewind in place of true and this::rewindNotAllowed in place of false? (one catch here is that they might not be referencable in this() calls, so eh?).

This would save continuously checking an if that will never change; on the other hand it does possibly make navigation slightly harder?

Also, this really only works because retriesAttempted appears to be instance scoped? I'm assuming this is so we persist failures across calls until we get a success, but it's not entirely clear to me?


/**
* Construct a {@link EventProcessor} that will automatically track the progress by updating its sequence when
* the {@link EventHandler#onEvent(Object, long, boolean)} method returns.
*
* <p>This constructor will not support rewinding batches.
Copy link
Member

Choose a reason for hiding this comment

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

How about 'The created BEP will not support batch rewind". The constructor is just a constructor

* Construct a {@link EventProcessor} that will automatically track the progress by updating its sequence when
* the {@link EventHandler#onEvent(Object, long, boolean)} method returns.
*
* <p>This constructor will support rewinding batches.
Copy link
Member

Choose a reason for hiding this comment

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

Same same - the constructor isn't supporting anything!

*/
package com.lmax.disruptor;

interface BaseEventHandler<T>
Copy link
Member

Choose a reason for hiding this comment

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

Gentle preference for EventHandlerBase over BaseEventHandler, which sounds like an EventHandler that someone has done something unpleasant to.

Copy link
Member

@grumpyjames grumpyjames left a comment

Choose a reason for hiding this comment

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

I sort of hate that we've got three constructors on BEP; if it weren't such a ballache I'd be suggesting some named factory methods instead.

I agree with Simon's suggestion of behaviour rather than boolean.

Don't think any of my comments are showstoppers.

@sea36
Copy link
Contributor

sea36 commented Oct 27, 2022

RewindableException extends Throwable

Extending Throwable :(
https://stackoverflow.com/a/2756001

And if we're going that path, it isn't an Exception any more.

@swarren12
Copy link
Contributor

RewindableException extends Throwable

Extending Throwable :( https://stackoverflow.com/a/2756001

And if we're going that path, it isn't an Exception any more.

I also questioned this offline - my understanding of the reasoning here is that we have no way of allowing the regular handlers to throw an arbitrary exception but not a RewindableException. Making it an Error wouldn't work, as these are unchecked, so the only other option would be to change the signature of onEvent() in EventHandler.

Something like this would work in theory:

void onEvent(T event, long sequence, boolean endOfBatch) throws DisruptorException;

but that would require catching and wrapping every Exception, and would probably make migration onto 4.0 painful.

@Palmr Palmr force-pushed the rewindable-event-handler-separation branch from e5ea360 to 6f98f61 Compare December 14, 2022 21:49
@Palmr
Copy link
Contributor Author

Palmr commented Dec 14, 2022

On the topic of exceptions and use of Throwable: I think it's justified here as it's a specific exception we want to raise in a certain circumstance.

There's probably some major refactoring I'd like to make in a future major release to make EventHandler::onEvent return something as right now we're straying down the path of exceptions for control flow. Another case in #397

For now I think it's a fair trade off as the RewindableException is not something that should bubble out past the BatchEventProcessor and concerns of it not being caught by those only catching Exception should be mostly moot in this case.

…ndler-separation2

# Conflicts:
#	src/main/java/com/lmax/disruptor/BatchEventProcessor.java
#	src/main/java/com/lmax/disruptor/EventHandler.java

# Manually fixed up:
#	src/main/java/com/lmax/disruptor/EventHandlerBase.java
@Palmr Palmr force-pushed the rewindable-event-handler-separation branch from 992576a to ea61e82 Compare March 18, 2023 18:08
@Palmr Palmr requested review from swarren12 and grumpyjames and removed request for sea36 and swarren12 March 18, 2023 18:09
@Palmr Palmr force-pushed the rewindable-event-handler-separation branch from d248ea9 to 63d5af7 Compare March 18, 2023 18:54
An overly keen find & replace made more changes than necessary while changing from direct BatchEventProcessor construction to using the builder.
Copy link
Contributor

@swarren12 swarren12 left a comment

Choose a reason for hiding this comment

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

LGTM

Wonder if we can move the retriesAttempted variable off the handler into the retry strategy as a next-step refactor?

@grumpyjames
Copy link
Member

Man this interface is appalling for reviewing.

@grumpyjames grumpyjames merged commit e6f783b into LMAX-Exchange:master Mar 24, 2023
@Palmr Palmr moved this from In progress to Done in 4.0 Sep 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
4.0
Done
Development

Successfully merging this pull request may close these issues.

EventHandler::setSequenceCallback will not play well with RewindableException throwing event handlers
4 participants