Any solution trying to divide the load into different elements of the same collection (for example, orders) is doomed to failure. The reason is that if you get a high level of transactions, you will have to start doing one of the following:
- allow nodes to talk to each other (
hey guys, are anyone working with this? ) - Divide the formation of the identifier into segments (node a creates an ID 1-1000, node B 1001-1999), etc., and then just let them process their segment
- dynamically divide the collection into segments (and each node process the segment.
so what's wrong with these approaches?
The first approach is simply copying transactions in the database. If you cannot spend a lot of time optimizing your strategy, it is best to rely on transactions.
The second two options will decrease performance, because you need to dynamically route messages by identifiers, as well as change the strategy at runtime to also include newly inserted messages. Ultimately this will fail.
Decision
Here are two solutions that you can also combine.
Retry automatically
Instead, you have an entry point somewhere that reads from a message queue.
In it, you have something like this:
while (true) { var message = queue.Read(); Process(message); }
What you could do to get a very simple fault tolerance is to retry after a failure:
while (true) { for (i = 0; i < 3; i++) { try { var message = queue.Read(); Process(message); break; //exit for loop } catch (Exception ex) { //log //no throw = for loop runs the next attempt } } }
Of course, you can just catch the db exceptions (or rather transaction failures) to just play those messages.
Microservices
I know Micro-service is a sound word. But in this case, this is a great solution. Instead of having a monolithic kernel that processes all messages, split the application into smaller parts. Or in your case, just turn off the processing of certain types of messages.
If you have five nodes on which your application is running, you can make sure that node A receives messages related to orders, node B receives messages related to delivery, etc.
Thus, you can still scale your application horizontally, you do not get conflicts, and this requires little effort (several message queues and reconfiguration of each node).