-1

You're writing the code for an API that accepts payloads from users and, before performing the actual write/update/delete in the db must validate the input for consistency and this is done by consulting various backend services. The client basically waits for an "accepted" or "rejected" response.

Obviously, you don't want anything to change while you're processing the request to avoid inconsistencies, but of course other requests could arrive almost at the same time and they should be processed too.

How do people go about preventing this kind of races?

The only strategy I can think of, is serialization of incoming requests into some sort of queue and then processing them serially one at a time from the queue. But this has the downside of blocking the client until its request has been processed, which might take some time (if we want to give clients meaningful result messages, that is).

Are there other (better) methods?

To make things more concrete, let's say we accept Persons and Addresses. A Person object includes an address, which must be a valid address. A Person is not inserted if the address is not valid or does not exist; an Address cannot be deleted if there are Persons associated with it. So when inserting a Person, we want to check whether the address is valid first (and once we know it's valid, we don't want the Address to vanish before we insert the Person); when deleting an Address, we want to check whether there are Persons associated and reject the deletion if there are, and accept the deletion if there are none (but we don't want other Persons to appear with the same Address in the brief time between the check and the deletion). For the purpose of this discussion, the backend where the data is saved need not be relational or support transactions.

2
  • I'd be grateful for any comment or link that I might have missed. I've searched this and other stackexchange sites before posting, but I didn't find an explication that covered the example.
    – John Smith
    Dec 26, 2020 at 15:40
  • See optimistic vs. pessimistic locking
    – John Wu
    Jan 12, 2021 at 1:01

1 Answer 1

0

You can use "Eventual Consistency". The idea is that at any time any condition you set may be violated as long as in the end there is no violation.

If you add a person with an address, you first check the address is valid. If it isn't, adding has failed and you are done. If it is, you add the address, then you add the person, and finally you bind the person to the address; you do all three without checking for errors but you capture any error along the way. Once you tried to perform all three, you check for errors.

What can go wrong?

  • The address already existed when you tried to insert it? That is no problem. If adding the address failed, the person can still be added and bound to the already existing address.

  • The person couldn't be bound to the address as the address was deleted before you bound the person to it? Just re-add the address and bind it again to the person. Repeat until this succeeds or until the person is deleted.

All you need to ensure is that when fetching a person from the database, it must be bound to an address, so persons not yet bound to an address are filtered from result sets (you pretend they don't exist or have not yet been added).

You want to delete an address? Just check if it is referenced. If it isn't, delete it. But what if a person with that address is added exactly that moment? No problem. It will just re-add the address and it will do so over and over again, see above.

So temporary a person without an address may be in the database but eventually every person in the database will have an address and this address will be valid. You just need to ensure that you never give out temporary results.

Of course, you may repeat the same action multiple times in a tight loop but therefore you don't need to block other operations at any time and at some point this fight will be won for sure. In many situation this leads to better performance than your suggested solution.

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.