Simple, transaction-aware cache decorator that holds cache values transiently until commit to avoid polluting the cache with invalid values in case of a rollback.
final Cache myCache = myCacheManager.getCache("my-cache");
final Cache myWrappedCache = new EnhancedTransactionAwareCacheDecorator(myCache, cacheCacheResult);
A benefit of knowing the transaction boundaries is that we can support caching of a value coming from the delegate cache, as it may be remote and impose roundtrip and deserialization costs compared to a simple Hashmap lookup. This will of course only be beneficial if the same value is accessed more than once inside the same transaction. This can be enabled by setting cacheCacheResult
to true
.
Why not just use Spring's own TransactionAwareCacheDecorator?
Spring's decorator has a massive flaw in that it does not keep cache changes visible inside the transaction. This means that if you populate your cache using cache.put("foo", "bar")
and then subsequently perform cache.get("foo")
you will get a null
result. Or more worryingly, if the value was already set before this transaction started, you update it, you will still see the old value! It is not until after the transaction has committed that the cache returns the correct value. This has two major implications: Potentially performance (if the cache result is bypassed for each invocation) and definitely visibility/observability.
This decorator on the other hand, hold a transient cache for the duration of the transaction, and fetches the data from that before it attempts to fetch data from the actual cache. This allows you to have full caching performance and observabilty, and still the safety of only merging the transient data to the real cache in case of transaction commit.
Operation | TransactionAwareCacheDecorator (Spring) | EnhancedTransactionAwareCacheDecorator (This) |
---|---|---|
put (new value) |
❌ Not visible | ✅ Visible |
put (existing value) |
❌ Not visible | ✅ Visible |
evict |
❌ Not visible | ✅ Visible |
clear |
❌ Not visible | ✅ Visible |
No, it will not. This decorator will just prevent the underlying cache to be populated with data before the transaction is committed. Also, the cache isolation can be considered READ COMMITTED
, i.e. if another transaction updates the cache (and commits) it will be instantly visible, exception if the cacheCacheResult
is enabled, and the value has already been read.