-
Notifications
You must be signed in to change notification settings - Fork 12
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
Kill dispatching exceptions #1104
Conversation
# Conflicts: # license-report.md
Codecov Report
@@ Coverage Diff @@
## master #1104 +/- ##
============================================
- Coverage 92% 90.78% -1.22%
+ Complexity 3993 3928 -65
============================================
Files 539 540 +1
Lines 12850 12865 +15
Branches 711 723 +12
============================================
- Hits 11822 11679 -143
- Misses 807 959 +152
- Partials 221 227 +6 |
@armiol, @alexander-yevsyukov, PTAL. I'm planning on improving the coverage in further PRs. |
@dmdashenkov please rephrase this one:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The names CannotDispatchEventTwice
and CannotDispatchCommandTwice
sounds as if we want dispatch a signal twice but cannot do that because of some reason. Please think of names that reflect the fact that we do not want double delivery.
If we also need to include entity states into this, it could be just “Propagation” is not correct word for this context. We do not want this process be seen as creating more and more messages of the kind. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my comments.
@@ -135,7 +131,7 @@ protected BoundedContext(BoundedContextBuilder builder) { | |||
*/ | |||
protected final void init() { | |||
eventBus.init(this); | |||
tenantIndex.registerWith(this); | |||
tenantIndex.init(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reads as if TenantIndex
initializes the `BoundedContext.
@@ -201,7 +197,7 @@ public static BoundedContextBuilder multitenant(String name) { | |||
public <I, E extends Entity<I, ?>> void register(Repository<I, E> repository) { | |||
checkNotNull(repository); | |||
Security.allowOnlyFrameworkServer(); | |||
repository.injectContext(this); | |||
initialize(repository); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about just init()
?
* @param context | ||
* the Context to which this instance belongs | ||
*/ | ||
void init(BoundedContext context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again, it would be better to name it injectContext()
, or register()
, or (got forbid) setContext()
. It would read better on the usage side.
@@ -71,7 +72,7 @@ private void checkNotAlreadyRegistered(Class<? extends Message> stateClass) { | |||
if (alreadyRegistered != null) { | |||
throw newIllegalStateException( | |||
"A repository for the state class %s already registered: %s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add backticks for the code related parts of the message and the period at the end of the sentence.
… `BatchDispatch`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my comments.
* If the given {@code contextPart} is {@link ContextAware}, | ||
* {@linkplain ContextAware#registerWith registers} it with this context. | ||
*/ | ||
private void registerWithThis(Object contextPart) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just register()
?
@@ -71,7 +71,7 @@ private void checkNotAlreadyRegistered(Class<? extends Message> stateClass) { | |||
RepositoryAccess alreadyRegistered = repositories.get(stateClass); | |||
if (alreadyRegistered != null) { | |||
throw newIllegalStateException( | |||
"A repository for the state class %s already registered: %s", | |||
"A repository for the state class %s already registered: `%s`", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please finish with period.
@@ -33,7 +33,7 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final class PropagationProcess
Don't we need to rename this class too?
@@ -36,7 +36,7 @@ import "spine/core/event.proto"; | |||
|
|||
// Report of a batch propagation of several signals. | |||
// | |||
message Propagation { | |||
message BatchDispatch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename to BatchDispatchOutcome
.
@@ -36,7 +36,7 @@ import "spine/core/event.proto"; | |||
|
|||
// Report of a batch propagation of several signals. | |||
// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the empty line.
EmptyClass, | ||
MethodResult<Empty>> { | ||
EmptyClass> | ||
implements NonProducingMethod<EventSubscriber, EventClass, EventEnvelope> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name NonProducingMethod
is a bit weird. VoidMethod
may not be much better, but at least shorter.
import io.spine.core.ActorContext; | ||
import io.spine.core.Command; | ||
import io.spine.server.entity.ProducedCommands; | ||
import io.spine.server.entity.Success; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Success
does not seem to belong to entity
package. Yes, most of the dispatching end up in entities, but we also have CommandHandler
and EventHandler
.
MessageEnvelope<?, ?, ?> handledSignal) { | ||
if (result != null) { | ||
String errorMessage = format( | ||
"Method `%s` should not produce any result. Produced: %s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please finish with dot.
/** | ||
* A {@link io.spine.server.command.Commander Commander} which retries the task | ||
* creation if it fails. | ||
*/ | ||
public final class CreationRetry extends AbstractCommander { | ||
|
||
private static final Set<TaskId> rejectedTasks = new HashSet<>(); | ||
private static final Set<TaskId> rejectedTasks = newHashSet(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Google considers these static factory method as deprecated since Java 7.
…pine.server.dispatch`
…ess` to `BatchDispatch`
@alexander-yevsyukov, PTAL again. Let's discuss the leftover comments vocally. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Before this PR, all the errors caused by message dispatching were thrown as exceptions. Exception handling was distributed between various
onError
methods as well as between different levels of abstraction, from buses to transactions.Now, all the dispatching errors are converted into system events. Users may choose to subscribe to those events for diagnostics and recovery.
System Events
There are 4 new types of system events:
HandlerFailedUnexpectedly
is the most common one. It is emitted at any generic error which occurs during a message handling, including runtime exceptions in handler methods, errors in the framework code, and errors in integrating user code and the framework code. Non-entity constraint violations also lead toHandlerFailedUnexpectedly
events (while entity constraint violations causeConstraintViolated
events to be emitted).RoutingFailed
event is emitted when the user code which routes messages to entity instances fails with an exception. The main difference between this event andHandlerFailedUnexpectedly
is that the framework does not know the ID of the target entity; thus, the data included in the event payload is a bit different.CannotDispatchEventTwice
and 4.CannotDispatchCommandTwice
are events emitted when a duplicate event or command is dispatched to an entity. These events can be emitted at theInbox
level, i.e. before the delivery.Message Propagation
Previously, when propagating a message through the system, the control flow heavily relied on the exceptions to be thrown when something does wrong. Now, a method dispatching a message would not throw an exception even if dispatching fails. That's why we introduce
PropagationOutcome
, a structure which aggregates an outcome of a message propagation, successful or otherwise.A successful propagation may result in events, a rejection, or commands. Those results are posted into the respective buses on the
MessageEndpoint
level. Previously, commands used to be posted from within the entity level. Now, they are processed side-by-side with the events.An unsuccessful propagation is described with a
spine.core.Error
.Error
s are converted into system events and posted into the system Event Bus.When many events are processed in a single transaction, one of them may fail and halt processing. Events which were not sent for processing because another event had failed result in
interrupted
outcomes.Some messages may be forced on an entity by measures beyond normal propagation (such as catch-up processes). If an event does not belong to an entity, the entity may ignore it. Such processing is reflected in the
PropagationOutcome
as theignored
state.MessageDispatcher
contractFor quite a long time,
MessageDispathcer
s returned the IDs of the target entities after dispatching a message. Those values were never used in production code and sometimes had to be artificially manufactured (e.g. in abstract message handlers). Now,MessageDispatcher.dispatch
does not return anything.MessageDispatcher.onError
was deleted, since all the errors are now broadcast via system Event Bus.