5
\$\begingroup\$

Details about spring's caching framework are here. When I was reading this link, I thought the composite cache mentioned there was one that used levels of caching based on the order given to the constructor. On reading the documentation here, I realized that it was not so; this was so that different caching solutions could be used in different methods.

With the following code, one can have a GuavaCache as L1 as that uses plain Java objects and wouldn't incur network latency or serialization and deserialization overhead. In case of a miss in L1 one would perform a get from L2 (and put it in L1 if found there). For L2 one might use a distributed caching solution - probably a cache running on another fleet of hosts.

TwoLevelCacheManager

package hello;

import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.NoOpCacheManager;

import java.util.Collection;

public class TwoLevelCacheManager implements CacheManager {
    CacheManager currentLevel;
    CacheManager nextLevel;

    TwoLevelCacheManager(CacheManager singleLevel) {
        this(singleLevel, new NoOpCacheManager());
    }

    TwoLevelCacheManager(CacheManager currentLevel, CacheManager nextLevel) {
        //assert both contain same cache names or dynamic creation true?
        this.currentLevel = currentLevel;
        this.nextLevel = nextLevel;
    }

    @Override
    public Cache getCache(String name) {
        return new TwoLevelCacheCache(name, currentLevel.getCache(name), nextLevel.getCache(name));
    }

    @Override
    public Collection<String> getCacheNames() {
        return currentLevel.getCacheNames();
    }
}

TwoLevelCacheCache

package hello;

import org.springframework.cache.Cache;

public class TwoLevelCacheCache implements Cache {
    private String name;
    private Cache levelOneCache;
    private Cache nextLevelCache;

    TwoLevelCacheCache(String name, Cache cache) {
        this(name, cache, new NoOpCache(name));
    }

    TwoLevelCacheCache(String name, Cache levelOneCache, Cache nextLevelCache) {
        this.name = name;
        this.levelOneCache = levelOneCache;
        this.nextLevelCache = nextLevelCache;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Object getNativeCache() {
        return this;
    }

    @Override
    public ValueWrapper get(Object o) {
        ValueWrapper value = levelOneCache.get(o);
        if (value == null) {
            value = nextLevelCache.get(o);
            if (value != null) {
                levelOneCache.put(o, value.get());
            }
        }
        return value;
    }

    @Override
    public <T> T get(Object o, Class<T> aClass) {
        T value = levelOneCache.get(o, aClass);
        if (value == null) {
            value = nextLevelCache.get(o, aClass);
            if (value != null) {
                levelOneCache.put(o, value);
            }
        }
        return value;
    }

    @Override
    public void put(Object o, Object o1) {
        levelOneCache.put(o, o1);
        nextLevelCache.put(o, o1);
    }

    @Override
    public ValueWrapper putIfAbsent(Object o, Object o1) {
        //synchronize?
        ValueWrapper value = get(o);
        if (value == null) {
            put(o, o1);
        }
        return value;
    }

    @Override
    public void evict(Object o) {
        levelOneCache.evict(o);
        nextLevelCache.evict(o);
    }

    @Override
    public void clear() {
        levelOneCache.clear();
        nextLevelCache.clear();
    }
}
\$\endgroup\$
  • \$\begingroup\$ TwoLevelCacheCache class name should be changed to TwoLevelCache. We should make get(Object 0) and put(Object o, Object o1) thread safe. \$\endgroup\$ – Pramod Mar 19 at 16:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.