1

I have a /cancel_reservation endpoint which takes a reservationId and needs authentication. Inside this method the reservation is cancelled and a refund is issued. Now if the same users calls this method twice concurrently, race conditions might occur and the user could be refunded twice.

For now I solved this issue by having a static Set which contains locked reservation ids and the method first checks that the reservation id is not locked. But I reckon there are better ways to solve this?

edit: Reservations are saved on Couchbase but I'd rather not use db optimistic/pessimistic locking in order to become db independent. Also I doubt optimistic locking will ever help in this scenario.

Also the flow of this method goes like this:

Fetch reservation ⮞ Regular checks ⮞ Cancel reservation with a 3rd party service ⮞ Refund the user ⮞ Update reservation status+other data ⮞ Persist reservation

deniswsrosa
  • 2,421
  • 1
  • 17
  • 25
prettyvoid
  • 3,446
  • 6
  • 36
  • 60
  • 1
    Are you doing this completely in memory, and there's no database? – Kayaman Oct 23 '19 at 11:15
  • 3
    If you do have a database, there should be a transactional method to ensure atomic updates in there. Optimistic locking, primary key constraints, check-and-set or such. – Thilo Oct 23 '19 at 11:25
  • 1
    I pull reservations from Couchbase but I'd rather have a programmatic solution in order to be database independent. Also regarding optimistic locking, don't think that will help because the 2 concurrent fetches will still work and a refund will be processed. – prettyvoid Oct 23 '19 at 11:28
  • not sure about` couchbase` if using jpa then you can use `@version` annotation, or can implement similar solution. – bananas Oct 23 '19 at 11:36
  • Optimistic locking can work, because you can do a Compare-And-Swap on a refund flag, setting it to "true" only if it was "false" before. Or increasing a `@version` only if the version matches what you have seen before. That way only one of the concurrent refunds will succeed (and you can abort the others). – Thilo Oct 23 '19 at 11:49
  • Trying a `INSERT INTO REFUNDS (id, timestamp, userId) VALUES(?,?,?)` and having it fail with a primary key error if the `id` has been used before should be pretty database-independent. They all support enforcing primary keys. – Thilo Oct 23 '19 at 11:54
  • what about putting the `cancel reservation` into a Queue and something will pull from the queue? – riorio Oct 23 '19 at 12:08

0 Answers0