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
DefaultEventPublicationRegistry markCompleted method's REQUIRES_NEW transaction propagation means events are marked completed before handler transactions are committed. #485
Comments
Can you provide a reproducer for the behavior you assume? I don't think what you describe is correct as the completing method interceptor is ordered before the standard transaction interceptor decorating the transactional event listener. In other words: only if the listener has completed successfully and the transaction wrapping the listener has completed, the event publication will be marked completed, too. We need to use EDIT: I think I misread your original comment, as the title is slightly misleading:
I originally thought you were trying to point out that the publication might be marked completed, although the listener failed. You obviously did not do that. That said, the second argument still holds true, and we generally live with the possibility of a publication staying uncompleted in rare error scenarios. In other words: we implement “at least once” semantics and expect the listeners to either perform idempotent work (i.e., being fine with being invoked more than once for a single event). |
Thanks, this is helpful. I did make a simple repo that demonstrates this github issue, if you're interested: |
We're currently investigating options to improve on the use case. I just wanted to describe how things work as they do right now and why that is. I don't think I follow on the concurrency-safe aspect of The test case looks rather convoluted and isn't as easy to follow. Are you aware of the |
I was not aware of the Scenario API, very cool! I'm not sure that could help with my particular test though, since I'm testing the Modulith internals here. Sorry for the complexity of that test, I realize it's not particularly readable. I'll try to explain what it's doing. The most important bullets are the last two.
I'm specifically concerned with replays here, but I've also found that on publishEvent, the event_publication row is committed as an incomplete event to the database before the event handler begins. So imagine a distributed environment, where a healthy application instance is healthily publishing events and handling them; we scale out and start up a new application instance; if we try to "replay" incomplete events on startup, we're going to retrieve some of these brand new events actively being handled by the healthy instance. I could see SchedLock being useful in a distributed system that's not using a single ACID database for its event_publication ledger, but in cases using relational databases and event_publication is in the same database, I think a pessimistic locking technique is probably appropriate here. But again, if "at least once" is the final decision for the event handling paradigm in Modulith, then event_publicatoin locking isn't necessary at all, and you can close this ticket. Thanks for your time! |
spring-modulith/spring-modulith-events/spring-modulith-events-core/src/main/java/org/springframework/modulith/events/core/DefaultEventPublicationRegistry.java
Lines 104 to 105 in c5f73e6
The markCompleted method of DefaultEventPublicationRegistry is invoked in a MethodInterceptor defined in CompletionRegisteringAdvisor, which intercepts the event handler method, invokes it, then calls the EventPublicationRegistry's markCompleted method to mark the eventPublication as complete.
Because of the REQUIRES_NEW transaction propagation type, markCompleted runs with a completely distinct database connection, and independent transaction from the event handler's transaction. This means there's a chance the event handler's transaction can commit, but the markCompleted transaction does not, which leaves the eventPublication appearing incomplete in the database even though it was completely successfully handled.
I propose that markCompleted should have the REQUIRED transaction propagation type. I believe this should cause markCompleted to commit the eventPublication as completed in the same transaction as the event handler.
Let me know what you think, I can submit a PR.
The text was updated successfully, but these errors were encountered: