Java ehCache, how to replace members - java

I have a simple method that I have annotated for caching.
#Cacheable(value = "devices", key = "#hardwareId", unless = "#result == null")
public Device get(String hardwareId)
I have a mechanism to know when someone changes the underlying database. So that I know to Evict a member from the cache, so that the next call in will go back to the database.
getCache().remove(hardwareId);
What I would like to do it REPLACE the element in the cache. The reason for this is that the call back to the database can take 1000ms & I'd like to not have that blip on the performance of the method.
As far as I can tell I have two options.
Option 1:
When I evict the member, call back into the service at that time.
getCache().remove(hardwareId);
service.get(hardwareId);
Option 2:
Create an instance of 'net.sf.ehcache.bootstrap.BootstrapCacheLoader'
that registers on startup the same class to be notified element
being removed from a cache (notifyElementRemoved()).
On #PostContruct get all methods annotated with #Cacheable. Create a Map of
'cacheName' to Method instance (java reflection Method)
When notifyElementRemoved() is triggered, uses the cache name to get the Method instance, with that invoke it to trigger the cache to be repopulated.
Method method = map.get(cacheName);
// Add magic here to get the service.
Object serviceInstance = applicationContext.getBean("deviceService");
if (Proxy.isProxyClass(serviceInstance.getClass())) {
Proxy.getInvocationHandler(serviceInstance).invoke(serviceInstance, method, new Object[] {objectKey});
} else {
method.invoke(serviceInstance, objectKey);
}
The downside of option 1 is that I have to go modify 30+ classes to put in the logic to call back into the service.
The downside of option 2 is that it's a bit complex, it feels like it would be good if ehCache could provide this feature. It knows what method it wrapped, it knows what the key/parameters were that called into this method.
The downside of both options is that there will always be a time when the cache does not contain the member & could cause a blip in performance.
My question is, does ehCache provide the feature I want or is there another mechanism out there to do REPLACEMENT of members in the cache with zero time of the cache being empty?

Don't do option 2. Too complicated. In general, the way it goes is to have a #Cacheable and a #CachePut method. Why not using that?
#Cacheable(value = "devices", key = "#hardwareId", unless = "#result == null")
public Device get(String hardwareId)
#CachePut(value ="devices", key= "#hardwardId", unless = "#result == null")
public Device update(String hardwareId)
It should cleanly solve your problem.
BTW, you don't need to specify the key. It is implicit.

Related

Guava caching using independent keys

When working with user objects coming from a database one has usually an id and a username and it's common to search a user by id or by username.
If I now want to find users and like to use Guava caches I have to create two caches. One is caching by id, one is caching by username.
But both point to the same object.
Is it possible to use one LoadingCache only?
I thought about using the User Object itself as key LoadingCache<User, User> and implement equals and hashcode in the User object.
In the equals Method it's easy to say two User objects are equal if either the id or the username is equal.
But how can I generate a good hashCode Method that works for this scenario?
Any ideas on that?
When working with user objects coming from a database one has usually an id and a username and it's common to search a user by id or by username.
Remark: "search" means something different to me, then accessing. Maybe the id and the username have different usage patterns? Maybe the username is only needed at login time?
Avoid using two different concepts for referencing / accessing a user in your application. Decide for one use it consistently. Is the username unique? Can it change?
Two caches: You can use two caches and populate the "sister cache" from the loader with name2user.put(user.getName(), user) or id2user.put(user.getId(), user). This way the identical user object is in both caches. Still, I don't like it, because of cleanlyness and consistency issues.
The third issue is data duplication, if you decide to change to another solution. A cache may store the value not by reference but copy it into compact byte arrays and store it off-heap (EHCache3, Hazelcast, etc.). (Clean) code should not rely on the fact, that the cache stores its data by reference in-heap, if there is no real need for it.
As assumed above the two access paths will not be equal in usage. My recommendation:
One cache to cache the user data with: id -> User
Second cache for resolving the id only: name -> id
Don't mind the additional cache access in the case of name. Of course the loader of the second cache my already request a User for its purpose so you might want to prepopulate the first cache with it.
Thank you very much for the answers, especially from the Guava developer itself. The suggest Solution was way to much work for me, I'm lazy ;).
So if I never the less will have to caches, I decided to solve it this way.
final LoadingCache<Serializable, Optional<ITemplate>> templatesById = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE).expireAfterAccess(MAX_CACHE_LIFE_TIME, TimeUnit.MINUTES)
.build(new CacheLoader<Serializable, Optional<ITemplate>>() {
#Override
public Optional<ITemplate> load(final Serializable id) {
final ITemplate template = readInternal(id);
final Optional<ITemplate> optional = Optional.ofNullable(template);
if (template != null) {
templatesByKey.put(template.getKey(), optional);
}
return optional;
}
});
final LoadingCache<String, Optional<ITemplate>> templatesByKey = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE).expireAfterAccess(MAX_CACHE_LIFE_TIME, TimeUnit.MINUTES)
.build(new CacheLoader<String, Optional<ITemplate>>() {
#Override
public Optional<ITemplate> load(final String key) {
final ITemplate template = byKeyInternal(key);
final Optional<ITemplate> optional = Optional.ofNullable(template);
if (template != null) {
templatesById.put(template.getId(), optional);
}
return optional;
}
});
It means, that I don't wast memory for having two instances of a template in two caches. So I just add a template to both caches, if it was received from the database.
It works really good and is damn fast.
The only question was, when to tell the cache to refresh.
In my scenario it's only needed on delete or update.
#Override
#Transactional
public void update(final ITemplate template) {
super.update(new DBTemplate(template));
templatesById.invalidate(template.getId());
templatesByKey.invalidate(template.getKey());
}
That's it.
Any comments on that?

Is it appropriate to use AtomicReference.compareAndSet to set a reference to the results of a database call?

I am implementing a simple cache with the cache stored as an AtomicReference.
private AtomicReference<Map<String, String>> cacheData;
The cache object should be populated (lazily) from a database table.
I provide a method to return the cache data to a caller, but if the data is null (ie. not loaded), then the code needs to load the data from the database. To avoid synchronized I thought of using the compareAndSet() method:
public Object getCacheData() {
cacheData.compareAndSet(null, getDataFromDatabase()); // atomic reload only if data not set!
return Collections.unmodifiableMap(cacheData.get());
}
Is it ok to use compareAndSet in this way ie. to involve a database call as part of the atomic action? Is it any better/worse than just synchronizing the method?
Many thanks for any advice..
You do not achieve expected behaviour. This expression:
cacheData.compareAndSet(null, getDataFromDatabase())
will always call getDataFromDatabase() first. This means that it doesn't matter if the data was cached or not. If it was, you still call the database, but discard the results. The cache is working, but the performance is equally poor.
Consider this instead:
if(cacheData.get() == null) {
cacheData.compareAndSet(null, unmodifiableMap(getDataFromDatabase()));
}
return cacheData.get());
It's not perfect (still getDataFromDatabase() can be called multiple times at the beginning), but will work later as expected. Also I moved Collections.unmodifiableMap() earlier so that you don't have to wrap the same map over and over again.
Which brings us to even simpler implementation (no synchronized or AtomicReference needed):
private volatile Map<String, String> cacheData;
if(cacheData == null) {
cacheData = unmodifiableMap(getDataFromDatabase());
}
return cacheData;

return "constant" value from method java

I'm not quite sure if this is possible so that's why I ask you guys. I want to write a method that knows if it has been visited before and if it has return the same value it has lasttime it were visited. I can't use fields/instance varibles for this.
This is want I want to do, without the instance variable foo:
private FooObject foo = null;
public int setFoo(FooObject in)
{
if(foo == null)
foo = in;
return foo.getX();
}
Could this be done?
that knows if it has been visited before
Knowledge of what happened before requires memory, aka state. Therefore you will need something to store that knowledge; you can't avoid it.
I agree with #Oli Charlesworth, you cannot do this without state. But using a field to save state is just one of many options. You could, for instance, save the state as a system property using System.setProperty() and System.getProperty(), or save the state to a file. You could even save the state to a database.
Without using an instance variable? no, it can't be done. You need a way for the method to "remember" something between calls - a local variable won't work, it has to be something that "lives" outside a method call. And that's what an instance variable is for: saving state, visible from all instance methods.
You can use a cache. Read from cache for looking for the input. If the input is already in cache, then return the value from cache. If don't, put the entry in cache and return the new calculated value.
CacheManager singletonManager = CacheManager.create();
Cache memoryOnlyCache = new Cache("testCache", 5000, false, false, 5, 2);
manager.addCache(memoryOnlyCache);
Cache cache = singletonManager.getCache("testCache");
Put new entry
Element element = new Element("key1", "value1");
cache.put(element);
Looking for in chache:
Element element = cache.get("key1");
Object value = element.getObjectValue();
For remove:
cache.remove("key1");
You can persist this to disk.
For more information, see in http://ehcache.org/
You could use the Preferences API. That allows you to store memory across application loads though, so if you want it to be restarted each time you load, you'll need to set a shutdown hook to clear the value from the Preferences API.

Persist guava cache on shutdown

I use the following guava cache to store messages for a specific time waiting for a possible response. So I use the cache more like a timeout for messages:
Cache cache = CacheBuilder.newBuilder().expireAfterWrite(7, TimeUnit.DAYS).build();
cache.put(id,message);
...
cache.getIfPresent(id);
In the end I need to persist the messages with its currently 'timeout' information on shutdown
and restore it on startup with the internal already expired times per entry. I couldn't find any methods which give me access to the time information, so I can handle it by myself.
The gauva wiki says:
Your application will not need to store more data than what would fit in RAM. (Guava caches are local to a single run of your application. They do not store data in files, or on outside servers. If this does not fit your needs, consider a tool like Memcached.)
Do you think this restriction address also a 'timeout' map to persist on shutdown?
I don't believe there's any way to recreate the cache with per-entry expiration values -- even if you do use reflection. You might be able to simulate it by using a DelayedQueue in a separate thread that explicitly invalidates entries that should have expired, but that's the best I think you can do.
That said, if you're just interested in peeking at the expiration information, I would recommend wrapping your cache values in a class that remembers the expiration time, so you can look up the expiration time for an entry just by looking up its value and calling a getExpirationTime() method or what have you.
That approach, at least, should not break with new Guava releases.
Well, unfortunately Guava doesn't seems to expose this functionality but if you feel adventurous and absolutely must have this you could always use reflection. Just look at sources and see what methods do you need. As always care should be taken as your code might break when Guaval internal implementation changes. Code below seems to work with Guava 10.0.1:
Cache<Integer, String> cache = CacheBuilder.newBuilder().expireAfterWrite(7, TimeUnit.DAYS).build(new CacheLoader<Integer, String>() {
#Override
public String load(Integer key) throws Exception {
return "The value is "+key.toString();
}
});
Integer key_1 = Integer.valueOf(1);
Integer key_2 = Integer.valueOf(2);
System.out.println(cache.get(key_1));
System.out.println(cache.get(key_2));
ConcurrentMap<Integer, String> map = cache.asMap();
Method m = map.getClass().getDeclaredMethod("getEntry", Object.class);
m.setAccessible(true);
for(Integer key: map.keySet()) {
Object value = m.invoke(map, key);
Method m2 = value.getClass().getDeclaredMethod("getExpirationTime", null);
m2.setAccessible(true);
Long expirationTime = (Long)m2.invoke(value, null);
System.out.println(key+" expiration time is "+expirationTime);
}

Does EHCache require put() in order for changes to instance to reflect (XA)?

I've started working with EHCache as a transactional cache (XAResource) in a JTA UserTransaction and I'm seeing something which is a bit strange, at least in my mind, and I'd like to understand whether my "seeing" is wrong or my understanding.
The following code will return false
ut = getUserTransaction();
ut.begin();
MyClass a = myChache.get(key).getValue();
a.changeSomeInnerReferrence(newRefference);
ut.commit();
ut = getUserTransaction();
ut.begin();
MyClass b = myChache.get(key).getValue();
ut.commit();
return a.equals(b);
Let's assume MyClass has a member of the type MyOtherClass and that changeSomeInnerReferrence changes the reference from the current value to the parameter; Also assume that equals takes that member into consideration.
I noticed that unless I add myChache.put(key,a) before the ut.commit() the above code will return false.
Why is that? Is this the general behavior of caches? I would think that changing an inner reference would propagate into the cache once commit is called.
Thanks,
Ittai
Bit of a preface here, I haven't used EHCache in the context of JTA. It is possible it does something clever within a user transaction, but I kinda doubt it.
The general rule is that the element returned by cache.get(key) is by value. Changes to it aren't necessarily reflected in the underlying cache. The reasoning behind it becomes pretty clear if you imagine not having an in-memory store at all, but only a disk store. The disk store requires serializing the cache entries, so a put/get pair of operations will return you a different Java instance. Further, in such a situation it's not clear when any changes to the instance returned by cache.get() would/should get written back to disk. Using a put() makes that clear.
In the end, the thing you get from get() is your responsibility. You tell EHCache to take over by saying put().

Categories