4

I have a load balanced service that needs two data inputs to correctly give a result.

One of these inputs comes from a queue, the other comes from user input.

Most of the time the data that comes from the queue arrives long before the user input. This data from the queue is saved to a database and when the user input arrives it is used to create the result.

But about 10% of the time the user input happens first. When that happens I need to wait (meaning block the process) for the queue data to arrive prior to giving a response to the user.

The problem with the 10% scenario is knowing when the queue data has arrived so I can continue. I could keep querying the database, asking "are you there?", "are you there now?", "How about now?" But I don't like that for many reasons.

(I also considered using process to process communication, but that does not work because my service is load balanced.)

Is there some way that other applications deal with this kind of thing? Something that does not have me pinging the database asking over and over till the data is there?

7

Don't view the result as the outcome of a request, but as the followup of an event. The question doesn't specify much so I can only speak in abstract terms. I believe the following to be the best approach.

Task, Input from user:

  • If input from queue exists, do the task: create result
  • Else, preserve the input to be used in "input from queue"

Task, Input from queue:

  • If input from user exists, do the task: create result
  • Else, preserve the input to be used in "input from user"

With this approach, the negative side is the duplication of responsibility. That is the tradeoff. The implementation depends on the nature and cost of "create result". The main benefit is this approach is cheaper and simpler than keeping a process around waiting for a message or sending requests every so often. I am afraid this answer might be irrelevant depending on the specifics, but I find the philosophy to be valid whether the subject is processes, messages, functions, files, events, etc.

  • 1
    I like this. The only problem I see is when both happen at the same time. Saving the input takes time. If I check before I save I could get a race condition. (I need to do the action once and only once.) – Vaccano Mar 19 at 2:25
  • Thank you. This can be extended to files and databases in the form of locks. You can place a lock before doing the queries. Would that not solve the issue? – Lacey Mar 19 at 2:36
  • Thank you for your response. However, as I looked at this more, I realized that it can have race condition issues. (Where both inputs check to see if the other has arrived at the same time. Since both see "no" they wait for the other, and the job never gets done.) I don't have a viable locking system that I would trust for this. I have come up with a different solution that I plan to post as an answer. – Vaccano Mar 20 at 17:14
  • 2
    @Vaccano it is perfectly possible to avoid all race conditions here. This is fairly easy to solve because in case of a race condition both event processors want to write an entry with the same ID. For example, just lock the whole database during processing an event. That's going to be a bottleneck though. A RDBMS allows you to apply an unique constraint so that only one entry with an ID can exist. This implies that writes with that ID will have some order, and the second will fail as it violates the unique constraint. There are ways to make this scalable in case you can't have a central DB. – amon Mar 20 at 22:06
1

One way to approach this and avoid the possibility of a race condition is to create a table structure something like this:

----------------------
| ID | user  | queue |
----------------------
| 12 |   N   |   N   |
----------------------

If you know about this id before either comes in, you can create the row at that time. Then in both the user and queue handlers, you select for update and then update the respective column. At the same time you check for the other column. If t is set, you know both are available and can continue. Either way you update and commit the transaction.

Alternately, you can first attempt to insert the row with the id and respective column set to 'Y' (or whatever you wish to use for) and if it fails follow up with the update. In an ideal world, you would not need to follow up with an update since the failure to insert would indicate the other side was complete. However, if something goes wrong and one side ends up doing this twice, you would have an issue. If you ensure both columns are set, you can be certain that you are ready. This also allows for the approach to be used for more than 2 inputs.

In order to handle the blocking on the client, you can use another queue. When the client request comes in, you fire off the update logic and wait for the response on the queue. Then the above logic will send a message to the queue when both inputs have been resolved.

0

I plan to do this by creating an "Input Queue" for each instance of my load balanced services. I will take the input (Either User or what was labeled "Queue" in my question) and put them in a queue. I intend to hash a key value that is in both inputs and use that hash to select the queue the input goes in. This will ensure that one queue (and one service instance) will get both inputs and that they will happen synchronously.

Once I can ensure that, I can wait till I get both messages and then perform the operation for the service (Like Lacy indicated). (Safe from any race condition issues.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.