Skip to content

Transaction-aware cache decorator that holds cache values transiently until commit to avoid polluting the cache with invalid values in case of a rollback.

License

Notifications You must be signed in to change notification settings

ethlo/spring-tx-cache-decorator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Transactional Cache Decorator

Maven Central Hex.pm Codacy Badge Coverage Status

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.

How to use

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

Will this make my cache transactional?

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.

References