I have been searching for the correct messaging pattern to use for the following problem:
I have one queue that has messages for every userid in the domain, each message is a userChanged event. The business requirement is that all messages for a particular userid must be processed in FIFO order and if an error occurs during the processing of a particular userid's message, no more processing should occur until that error is marked as processed successfully. All messages must be processed and this solution needs to be deployed in a clustered ESB environment.
I would like to demux these events into a FIFO queue for each userid so I can process messages from different userids in parallel and process each message for a user sequentially.
So far, I have come up with two possible solutions involving Mule and Rabbit but both would involve either custom components or existing components that I am not aware of.
Have all the messages on the first RabbitMQ queue read by a Mule flow with an AMQP inbound endpoint that grabs the userid from the header. It then uses an AMQP outbound endpoint which dynamically creates a persistent queue if it doesn't exist already that is named something like:
userChangedEvent-#[flowVars.userid]
and publishes the message to that queue. The custom component(s) would need to do the following: a. Create a shared object map that would identify all the dynamic amqp listener flows there are if it doesn't exist b. Check the map to see if there is an instance of the listener flow for the userid. If there is not, add an instance of the dynamic amqp listener flow that will listen to theuserChangedEvent-#[flowVars.userid]
queue, add this to the map using the userid as the key. c. Start the flow. d. The dynamic flow would need to be configured to process in a single threaded configuration, manually ack the messages after the business logic was completed successfully and to stop the flow in the case of an error.Have one amqp inbound endpoint reading all the messages off the queue and then a "collating" router would route messages with the same userid to the same instance of the reusable business logic flow for that userid, creating an instance if one for a given userid doesn't exist. Outstanding questions for this scenario are: a. Does such a router exist or would it need to be developed? b. The business logic flow would need to be executed in a single thread so the backlog of messages would need to be maintained in a durable fashion for each instance of the flow. c. An error in execution of one of the instances should stop that instance from processing more messages until it is resolved.
I have thought of workarounds such as "bucketing" the userids into various predefined queues (userChangedEvent-0to1000, userChangedEvent-1000to2000, etc)
so that we can predefine all the flows and associated amqp listeners we need and eliminate the need for dynamic flows, but this strikes me as an inelegant solution.
I get the feeling that there must be a messaging pattern to address this but I ripped up my copy of EIP to no avail! Any suggestions would be appreciated.
Update: This is conceptually what I am after, a demuxer with lanes (what I was calling buckets) but I was thinking that dynamic lane creation (1 per userid) would be even better: http://www.coralblocks.com/index.php/2014/06/demultiplexing-with-coralqueue-for-parallel-processing/