Replies: 4 comments 7 replies
-
|
Thanks for the well written essay on how a persistent outbox (or inbox) might be usable in specific scenarios to deal with consistency. There have been several threads, and lengthy conversations on why a persistent solution is better, and I have yet to see it proven given that even with NSB, you can only use it in a message handler (consumer), and even with that stated, they clearly define in the expectations:
From my perspective and understanding, the whole point of an outbox is to deal with unreliable communication between the endpoint and the message broker. I'm happy to entertain the inclusion of such a feature, but honestly we've tried and urgency hasn't been high. It's very database-specific (you can't have a generalized solution, since each database has different characteristics and constraints) so it has to be built for each database. Sure, there can be shared idioms, but each database would have its own set of guarantees. And let's be honest, it isn't really an outbox. It's a deduplication solution that uses extensive database transactions to provide a reasonable guarantee of idempotent operation. The fact that it stages messages in the database for delivery once the transaction is completed is an aspect of it. Related Stuff: The on-ramp, essentially a database transport that is store-and-forward to the actual transport, for truly transactional storage and delivery of messages (incomplete): #2234 Reliable messaging: #577 |
Beta Was this translation helpful? Give feedback.
-
|
@phatboyg I think you misunderstood the quote from NServiceBus's documentation. The mechanism may cause the message handler to be executed more than once, but because there are UNIQUE constraints defined for the message IDs, the transaction will fail in such scenarios - leaving the database in a consistent state (and not publishing outgoing messages). The only thing that is not covered by this infrastructure is integration with external systems (that can't be added to the transaction or be integrated using the same messaging transport). When implementing these parts - retries will have to be considered, sure. But I argue that what you referred to as "specific scenarios" is actually most of the cases. And why isn't it an Outbox? messages are buffered instead of delivered right away, which is how the pattern got its name. And yeah, there is a non-negligible amount of work needed to be done on a per-database basis (especially for databases that require rigid schema), but
I was unaware of PR #2234, and I'm happy to see progress in this area. I haven't got the chance to really look into what is included in the scope of this work but one thing I would love to see is support for non-relational databases that supports ACID transactions (like MongoDB 4.0+) |
Beta Was this translation helpful? Give feedback.
-
|
One thing I do miss in NSB's implementation is supporting multiple outboxes in the same endpoint, for cases where you interact with more than one database (not as part of the same request) |
Beta Was this translation helpful? Give feedback.
-
|
I have a work-in-progress branch using Entity Framework Core as an inbox/outbox for message consumers or sagas. It isn't anywhere complete yet, but it's a start in finding the seams to make it work reliably. I'll update again once I have something ready for review/testing. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I think adding a persistent message outbox/inbox feature to MassTransit is a great idea.
If you're unfamiliar with the concept, I suggest you read this page from NServiceBus's documentation (they call it an Outbox, but it is actually an Inbox too. I think both patterns are valuable and we need both in MassTransit).
I know I'm not the first to ask for this, but I'm unsatisfied with the reasons presented for not adding it as a built-in feature.
I'll try to present my view on this, and why it is such an essential feature.
The way I see it, MassTransit is an abstraction on top of messaging infrastructure. There are two main justifications for MassTransit's existence:
The first point is nice, but replacing the messaging transport is not such a common task (apart from testing).
The second point is stronger, in my opinion, so let's focus on that for a moment:
We want to help developers focus on solving their application-specific problems, by moving as much generic burden as possible to the framework. However, there are many cross-cutting concerns applications need that are not included in MassTransit, and that's OK (for obvious reasons). For evaluating whether a feature belongs in the MassTransit framework or not, I think these are the main factors:
Let's put the Persistent Outbox feature to each of these tests, one by one:
So I argue that almost 100% of MassTransit's users face this issue (You build an application --> you have state, and you use MassTransit --> your application is distributed --> you need to deal with eventual consistency). Note that maybe not all users are aware of the challenges of managing state in distributed systems - but I hope they realize them before their production environments fail on Friday afternoon.
I seriously doubt that the majority of MassTransit users implement this correctly in all of their message handlers. And if they don't they would have even larger issues regarding data corruption. These are not minor issues. At best (when implemented properly) they are time-consuming repetitive tasks that could have been handled in a central location by the infrastructure, but they are also extremely easy to forget or to implement incorrectly - causing data consistency issues that you may find only after they happen in production (where infrastructure starts to fail sometimes and retries are performed).
To summarize: a persistent inbox/outbox feature will shift a lot of common responsibility from the application level to the infrastructure level, allowing MassTransit users to focus on the specific problem they have at hand, instead of generic technical issues we all face, by developing applications with the assumption of an (almost*) exactly-once message delivery guarantee.
So what are we waiting for? Why not add this as a built-in feature?
(I would personally be happy to help with the implementation by the way)
*This is not magic. When dealing with external resources that are not covered by the transaction, idempotency will still need to be considered. However, I believe the majority of handlers do operate over a DB that supports ACID transactions - so even if this does not eliminate the problem in 100% of the cases, it will help a lot for sure.
Beta Was this translation helpful? Give feedback.
All reactions