user606521
user606521

Reputation: 15474

Redis caching - how to avoid race conditions?

I want to cache DB queries using redis. For now pseudo code in single API endpoint looks like this:

GET /data
  1. Check if object is in REDIS cache (by cacheKey)
  2. If it is in cache just return it to client
  3. If it is not in cache, then fetch data from DB
  4. Write data to REDIS cache (at cacheKey)
  5. Return data to client

I am caching objets that don't change often so I set expiration to 24h for example. The problem is when I want to update such object. The pseudo code looks like this:

PUT /data
  1. Update data in DB
  2. Write data to cache (or just set cache to null / delete form cache)

The problem is that after new data has been written to cache (or cache has been deleted) then still GET /data can overwrite new data in cache by old data. This happens when point 4 form GET /data is executed just after point 2 from PUT /data.

Is there any mechanism that will prevent overwriting new data with old data in cache?

Upvotes: 4

Views: 4435

Answers (2)

Itamar Haber
Itamar Haber

Reputation: 50112

The NX approach as @hobbs had suggested still has a race condition. I'd add to it using WATCH/MULTI/EXEC blocks around your GET workflow for transactional-like behavior.

With NX-only approach, there's still a chance that between 3 and 4, a PUT will get in the way.

Upvotes: 5

hobbs
hobbs

Reputation: 240639

In the case where the PUT puts the fresh data in the cache at the same time as it puts it in the database (write-through cache), you can use SET / HSET when writing to the cache from your PUT, and SETNX / HSETNX when writing to the cache from your GET. Since the GET is only going to write to the cache if the key doesn't exist, normally a SETNX will succeed. But if someone else (possibly a PUT) came along and set the key in the meantime, the SETNX will fail because the key already exists, and you'll get the result you want.

In the case where the PUT just deletes the data in cache to be repopulated by the next GET, I don't think you can provide any better guarantee than you already have. The data will live for one expiration time after it was retrieved from the database, which isn't strictly wrong, it's just worse than you would like.

Upvotes: 2

Related Questions