(redis) race between cachePut/cacheGet can result in miswrites

Description

In the following code, a race between cachePut/cacheGet can result in the following behavior:

var x = {value: 42} cachePut("key", x) // other args to this aren't important var y = cacheGet("key") y.value = 43 expect(cacheGet("key").value).toBe(42) // fails, 43 was written to redis

The core of the issue seems to be that cachePut is not synchronous with respect to write completion, and after cachePut returns, but before the write to redis completes, cacheGet will return a reference to the value which is queued for writing. If, prior to write completion, that reference itself is written to, the changes will be reflected when the the write to redis occurs.

This manifested for us in the following way:

// thread1 var template = {value: "Hello {user}"} cachePut("key", template) // other args to this aren't important notifyWorldThatCacheIsCoherent() // assumes cachePut is synchronous // thread2 blockOnCoherentCache(); var assumedCopyOfTemplate = cacheGet("key") replace_USER_withName(assumedCopyOfTemplate, "Alice"); assert(assumedCopyOfTemplate.value == "Hello Alice") // much later blockOnCoherentCache(); var assumedCopyOfTemplate = cacheGet("key") // here we have {value: "Hello Alice"} replace_USER_withName(assumedCopyOfTemplate, "Bob"); assert(assumedCopyOfTemplate.value == "Hello Bob") // fails

Probably the driver’s implementation of cachePut should be synchronous, or support a synchronous mode.

(aside: is there a guarantee that cachePut is synchronous, or is it up to the driver? It doesn’t seem to be mentioned in cachePut documentation. If a driver is asynchronous, is there an API to be notified of write completion (cachePut returns a Future<void> or something)? Most code seems to assume cachePut is synchronous.)

Environment

None

Activity

Show:

Pothys - MitrahSoft 29 May 2024 at 08:05

I checked this ticket and it is still a issue with Lucee version 6.1.0.162-SNAPSHOT.

Zac Spitzer 4 March 2023 at 23:31
Edited

https://github.com/lucee/extension-redis/issues/13

I created a test case for this, with a pool of 1 or 24 connections and added some sleeps

https://github.com/lucee/extension-redis/pull/14

[java]    [script]         testAdditional.RedisRace --------------------- [java]    [script] poolsize 24, no sleep [java]    [script] poolsize 1, no sleep [java]    [script] [true,true,false,false,false,false,false,false,false,false,false] [java]    [script] poolsize 24, sleep(1) [java]    [script] [false,false,false,false,false,false,false,false,false,false,false] [java]    [script] poolsize 1, sleep(1) [java]    [script] [false,false,false,false,false,false,false,false,false,false,false] [java]    [script]          (2 tests passed in 488 ms) [java]    [script] ERRORED [java]    [script] [java]    [script]         Suites/Specs: 1/4 [java]    [script] [java]    [script]         Failures: 1 [java]    [script]         Errors:   1 [java]    [script]         Pass:     2 [java]    [script]         Skipped:  0 [java]    [script] [java]    [script] [java]    [script] test for race Redis Cache, pool size = 24, no sleep [java]    [script] [java]    [script]         Errored: variable [X] doesn't exist [java]    [script] [java]    [script]         C:\work\lucee-extensions\extension-redis\tests\RedisRace.cfc:87 [java]    [script]         C:\work\lucee-extensions\extension-redis\tests\RedisRace.cfc:102 [java]    [script]         C:\work\lucee6\test\_testRunner.cfc:273 [java]    [script]         C:\work\lucee6\test\run-tests.cfm:234 [java]    [script]         /bootstrap-tests.cfm:108 [java]    [script] [java]    [script] [java]    [script]         expression [java]    [script] [java]    [script]         lucee.runtime.exp.ExpressionException: variable [X] doesn't exist [java]    [script]         at lucee.runtime.type.scope.UndefinedImpl.get(UndefinedImpl.java:243)

Details

Assignee

Reporter

Priority

New Issue warning screen

Before you create a new Issue, please post to the mailing list first https://dev.lucee.org

Once the issue has been verified, one of the Lucee team will ask you to file an issue

Created 3 March 2023 at 14:50
Updated 29 May 2024 at 08:06

Flag notifications