On Jun 24, 2014, at 15:27, Galder Zamarreño <gal...@redhat.com> wrote:

> Hi all,
> 
> Last few days I’ve been having fun fixing [1] and the solution I’ve got to 
> has some links to [2].
> 
> The problem is that when getCacheEntry() is called, the entry returned could 
> be partially correct. Up until now, we’d send back a cache entry back to the 
> client that the user could not modify, but it was linked to the data 
> container cache entry, which is mutable and hence could change on the fly. As 
> a result of this, the value could sometimes be updated but the version would 
> belong to a previous value for example.
> 
> A first attempt to fix it was to provide a true immutable cache entry view 
> which was initialised on construction, but even here there was the chance of 
> having value and version mistmatch, because getCacheEntry does not acquire 
> locks, so an ongoing update could result in the cache entry being constructed 
> when the update was half way, so, it would have the right value but the old 
> version information.
> 
> All this didn’t work well with the replaceIfUmodified logic in [3]. For 
> example, you could easily get this situation:
> 
> Current Cache contents: key=k1, value=A, version=1
> 
> T1: Hot Rod client calls: replace(k1, value=B, old-version=1)
> T2: Hot Rod client calls: replace(k1, value=B, old-version=1)
> T1: Server calls getCacheEntry and retrieves value=A,version=1
> T1: Cached and stream versions match (both are 1), so call replace(k1, 
> old-value=A, new-value=B)
> T1: Server updates value to B but version is still 1
> T2: Server calls getCacheEntry and retrieves value=B,version=1 (wrong!)
> T1: Cached and stream versions match (both are 1), so call replace(k1, 
> old-value=B, new-value=B)
> T1: Server updates version to 2 
> T1: Returns Success, replace worked
> T2: Returns Success, replace worked
> 
> The end result is that cache contained B, but both replaces returned true. 
> This is wrong and would fail to consistenly keep a counter in the value part. 
> Imagine the value being a counter of number of times the replace succeeded. 
> In the test developed by Takayoshi, N times replace() would return true, but 
> the final value would be N-1 or N-2, or N-5 :|
> 
> To fix this, I’ve been working with Dan on some solutions and we’ve taken 
> inspiration of the new requirements appearing as a result of ISPN-2956. To be 
> able to deal with partial application of conditional operations properly, 
> transactional caches are needed. So, the solution that can be seen in [4] 
> takes that, and creates a transaction around the replaceIfUmodified and 
> forces the getCacheEntry() call to acquire lock via FORCE_WRITE_LOCK flag.

so the entire underlaying cache needs to be transactional then?

> This solves the issue explained above, but of course it has an impact of the 
> performance. The test now runs about 1.5 or 2 times slower.
> 
> This is probably the best that we can do in the 7.0 time frame, but there’s 
> several things that could improve this:
> 
> 1. True immutable entries in the cache. If the entries in the cache were 
> truly immutable, there would be no risk of sending back a partially correct 
> entry back to the client.
> 
> 2. A cache replace method that does not compare objects based on equality 
> (the current replace()), but a replace method that takes a function. The 
> function could compare that the old entry’s version and the cached entry’s 
> version match. The function would only be executed inside the inner 
> container, with all the locks held properly. I already hinted something 
> similar in [5].
> 
> Finally, this was not a problem when the value stored in Hot Rod was a 
> ByteArrayValue wrapping the byte array and the version, because the value was 
> treated atomically, and in hindsight, maybe adding getCacheEntry might have 
> been premature, but this method has proven useful for other use cases too 
> (rolling upgrades…etc).
> 
> Cheers,
> 
> [1] https://issues.jboss.org/browse/ISPN-4424
> [2] https://issues.jboss.org/browse/ISPN-2956
> [3] 
> https://github.com/infinispan/infinispan/blob/master/server/core/src/main/scala/org/infinispan/server/core/AbstractProtocolDecoder.scala#L302
> [4] https://github.com/galderz/infinispan/tree/t_4424
> [5] 
> https://github.com/infinispan/infinispan/blob/master/core/src/main/java/org/infinispan/AdvancedCache.java#L374
> --
> Galder Zamarreño
> gal...@redhat.com
> twitter.com/galderz
> 
> 
> _______________________________________________
> infinispan-dev mailing list
> infinispan-dev@lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/infinispan-dev

Cheers,
-- 
Mircea Markus
Infinispan lead (www.infinispan.org)





_______________________________________________
infinispan-dev mailing list
infinispan-dev@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/infinispan-dev

Reply via email to