Giter Club home page Giter Club logo

cache2k's Introduction

License Stack Overflow Maven Central CircleCI

cache2k Java Caching

cache2k is an in-memory high performance Java Caching library.

  Cache<String,String> cache = new Cache2kBuilder<String, String>() {}
    .expireAfterWrite(5, TimeUnit.MINUTES)    // expire/refresh after 5 minutes
    .setupWith(UniversalResiliencePolicy::enable, b -> b // enable resilience policy
      .resilienceDuration(30, TimeUnit.SECONDS)          // cope with at most 30 seconds
                                                         // outage before propagating 
                                                         // exceptions
    )
    .refreshAhead(true)                       // keep fresh when expiring
    .loader(this::expensiveOperation)         // auto populating function
    .build();

For a detailed introduction continue with Getting Started.

Features at a glance

Integrations

More...

For more documentation and latest news, see the cache2k homepage.

Contributing

See the Contributor Guide.

cache2k's People

Contributors

aymandf avatar cruftex avatar dsvoronin avatar filipemanuelofs avatar globalworming avatar mannodermaus avatar shpikat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cache2k's Issues

FunctionalCacheLoader braeks binary compatibility

The change in 1.0.0.CR3 from

Cache2kBuilder.loader(CacheLoader<K, V> l)

to

Cache2kBuilder.loader(FunctionalCacheLoader<K, V> l)

Breaks strict binary compatibility. Not sure whether we want this at this late stage.

Provide a single complete jar

cache2k comes in several jars. The reason for this is modularity and keeping the footprint small when functionality is not needed. E.g. android has no JMX support, so this needs to be optional.

However, in normal web applications we always want to have everything. Idea: provide a singe shaded jar, e.g. cache2k-complete which contains everything API and all functions.

Android compatibility regression in v0.20 and v0.21

v0.19.2 works fine on Android, but the Cache2k v.20 and v0.21 result as the following error:

java.lang.Error: cache2k implementation not found, cache2k-core.jar missing? 
    at org.cache2k.CacheManager.implNotFound(CacheManager.java:91) 
    at org.cache2k.CacheManager.getInstance(CacheManager.java:84)

the dependency is defined in gradle as follows

    compile 'org.cache2k:cache2k-api:0.21'
    compile 'org.cache2k:cache2k-core:0.21'//runtime

(the test is done without proguard)

Expiry after Access / Time to Idle / Idle Scan

Support of expiry after an entry is accessed is missing in core cache2k.

What is it?

An entry expires and is removed from the cache after being not accessed for a period of time. The main use is primarily for freeing resources and shrinking the cache when the application is not in use.

Current state:

The feature is available in the JCache support. However, it is not implemented very efficiently. Also, the standard functionality of JCache is not very practicable. JCache defines TTI, however it cannot be combined with expire after write.

Why it isn't available in cache2k, yet?

We almost never need it. Typically we run caches with a TTL between 30 seconds and 30 minutes. Which means that entries expire via the TTL setting and the cache shrinks. TTI would be needed if entries never expire via TTL or have a higher TTL than TTI. For example a web resource might be allowed to be cached for 6 days, however, if being not accessed we like to remove it from memory after one hour.

The second reason is the overhead. TTI is typically implemented via an LRU style linked list. This brings lock contention and concurrency issues.

Solutions

If needed several options are available via the existing cache2k features, e.g. the expiry can be modified in a cache request via the entry processor.

We also plan to do an efficient implementation of a TTI approximation which is based on the eviction algorithm.

Fixed-time caching, caches forever!

Hi,
I used to use Google guava cache for fixed-time caching of certain contents on a web application. After knowing about cache2k, I wrote a small facade to be able to switch between cache2k and guava, and changed my default cache framework to cache2k on a high traffic website (~100k unique users per day). I switched to cache2k for two reasons.
First it's out of the box JMX support.
Second I assumed that when my DB fails for any reason, and so org.cache2k.CacheSource.get(K) throws any exception, cache2k continues to return old cached values for the keys. So my website home page would continue to work even after DB gets down.

I was satisfied with JMX support but about the second I was wrong. Please tell me if this is intended behavior that cache fails when CacheSource.get() fails.

Well, this post is about another issue which seems to be very severe:
The problem is about fixed-time caching and strong caching of cache2k. In my high traffic website fixed-time caching never refreshed after a couple of days, that is, it kept old values and did not update values every 1 minute (1 minute was my fixed caching period). The case was getting bad when I added an API to do a org.cache2k.Cache.remove(K) for failed caches so that website admin can refresh a cache himself. It works for some days but again fails after that. and Cache.remove(K) did nothing.

Switching back to guava, everything works fine.

Android Samsung devices crash on init

On Android 5.0 (so far) 100% samsung.
Crash happens every time I try to init Cache like so:

CacheBuilder.newCache(Long.class, BasePresenter.class)
.maxSize(maxSize)
.expiryDuration(expirationValue, expirationUnit)
.build();

01-22 11:33:28.474: E/AndroidRuntime(11647): java.lang.LinkageError: Error instantiating org.cache2k.spi.Cache2kCoreProvider, got java.io.IOException: Class resource file not found: org/cache2k/services/org.cache2k.spi.Cache2kCoreProvider
01-22 11:33:28.474: E/AndroidRuntime(11647): at org.cache2k.spi.SingleProviderResolver.loadProvider(SingleProviderResolver.java:101)
01-22 11:33:28.474: E/AndroidRuntime(11647): at org.cache2k.spi.SingleProviderResolver.resolve(SingleProviderResolver.java:86)
01-22 11:33:28.474: E/AndroidRuntime(11647): at org.cache2k.CacheBuilder.(CacheBuilder.java:41)
01-22 11:33:28.474: E/AndroidRuntime(11647): at com.remind101.PresenterManager.(PresenterManager.java:24)
01-22 11:33:28.474: E/AndroidRuntime(11647): at com.remind101.PresenterManager.getInstance(PresenterManager.java:32)
01-22 11:33:28.474: E/AndroidRuntime(11647): at com.remind101.ui.fragments.BaseMvpFragment.onSaveInstanceState(BaseMvpFragment.java:48)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.support.v4.app.Fragment.performSaveInstanceState(Fragment.java:2109)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.support.v4.app.FragmentManagerImpl.saveFragmentBasicState(FragmentManager.java:1767)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1835)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.support.v4.app.FragmentController.saveAllState(FragmentController.java:125)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:523)
01-22 11:33:28.474: E/AndroidRuntime(11647): at com.remind101.ui.activities.ClassFeedActivity.onSaveInstanceState(ClassFeedActivity.java:304)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.Activity.performSaveInstanceState(Activity.java:1383)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1286)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.ActivityThread.callCallActivityOnSaveInstanceState(ActivityThread.java:4475)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:3847)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3910)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.ActivityThread.access$1200(ActivityThread.java:178)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1473)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.os.Handler.dispatchMessage(Handler.java:102)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.os.Looper.loop(Looper.java:145)
01-22 11:33:28.474: E/AndroidRuntime(11647): at android.app.ActivityThread.main(ActivityThread.java:5944)
01-22 11:33:28.474: E/AndroidRuntime(11647): at java.lang.reflect.Method.invoke(Native Method)
01-22 11:33:28.474: E/AndroidRuntime(11647): at java.lang.reflect.Method.invoke(Method.java:372)
01-22 11:33:28.474: E/AndroidRuntime(11647): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
01-22 11:33:28.474: E/AndroidRuntime(11647): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)

putIfAbsent does not set expiryDuration

hi, while using Version 0.23.1 my monitoring showed that a cacheEntry inserted or updated with putIfAbsent() does not expire. Documentation doesn't indicate that. I assume it is a bug?

Example programm:

Cache<String,String> testCache  = CacheBuilder.newCache(String.class, String.class).name("testCache").expiryDuration(3, TimeUnit.SECONDS).build();

testCache.putIfAbsent(Test.KEY, Test.VALUE);
for (int i =0; i <5; i++){
    TimeUnit.SECONDS.sleep(2);
    if (testCache.peek(KEY) == null){
        System.out.println("MISS");
        testCache.putIfAbsent(KEY, VALUE);
    } else {
        System.out.println("HIT");
    }
}

output:
HIT
HIT
HIT
HIT
HIT

same Code with put instead of putIfAbsent:
output:
HIT
MISS
HIT
MISS
HIT

The second output is the expected one. :)

Enhance exception support

We have already basic exception support. That means, exceptions are cached and rethrown.

Planned enhancements:

  • Separate expiry time configuration
  • Separate expiry controller / refresh controller
  • By default, suppress an exception if there is still data
  • Documentation

Exception in AsyncDispatcher.getLockObject if key.hashCode is negative

I'm using Cache2k 1.0.0.CR2 as JCache provider. If I use an object with a negative hash code as a key, Cache2k throws an ArrayIndexOutOfBoundsException in AsyncDispatcher.getLockObject. Here is the full exception:

java.lang.ArrayIndexOutOfBoundsException: -10
	at org.cache2k.core.event.AsyncDispatcher.getLockObject(AsyncDispatcher.java:55)
	at org.cache2k.core.event.AsyncDispatcher.queue(AsyncDispatcher.java:74)
	at org.cache2k.core.InternalCache2kBuilder$AsyncExpiredListener.onEntryExpired(InternalCache2kBuilder.java:397)
	at org.cache2k.core.WiredCache.callExpiryListeners(WiredCache.java:600)
	at org.cache2k.core.WiredCache.expireOrScheduleFinalExpireEvent(WiredCache.java:593)
	at org.cache2k.core.WiredCache.timerEventExpireEntry(WiredCache.java:582)
	at org.cache2k.core.TimingHandler$ExpireTask.fire(TimingHandler.java:487)
	at org.cache2k.core.TimingHandler$CommonTask.run(TimingHandler.java:470)
	at java.util.TimerThread.mainLoop(Timer.java:555)
	at java.util.TimerThread.run(Timer.java:505)

Here is a simple example to reproduce an issue:

    public void cache2k_expirationDurationWithNegativeHashCode() throws Exception {
        final int KEY = -10;
        final int VALUE = 20;

        MutableConfiguration<Integer, Integer> config = ExtendedMutableConfiguration.of(
                        Cache2kBuilder.of(Integer.class, Integer.class)
                                        .entryCapacity(100)
                                        .expireAfterWrite(1, TimeUnit.SECONDS));
        config.setTypes(Integer.class, Integer.class);
        config.setStoreByValue(false);

        CachingProvider cachingProvider = Caching.getCachingProvider();
        CacheManager mgr = cachingProvider.getCacheManager();

        try (Cache<Integer, Integer> cache = mgr.createCache("cache", config)) {

            cache.put(KEY, VALUE);

            Thread.sleep(1500);

            assertEquals(null, cache.get(KEY));
        }
    }

Integer overflow in ClockProPlus statistics

Running the caffeine GetPutBenchmark leads to an integer overflow in the statistics for ClockProPlus. The problem seams to be that 2G accesses are exceeded.

Cache{Cache2k.INIT.35}(size=9676, maxSize=65536, usageCnt=-2080767476, missCnt=0, fetchCnt=0, fetchButHitCnt=0, heapHitCnt=-2080777152, virginEvictCnt=0, fetchesInFlightCnt=0, newEntryCnt=9676, bulkGetCnt=0, refreshCnt=0, refreshSubmitFailedCnt=0, refreshHitCnt=0, putCnt=32768, putNewEntryCnt=9676, expiredCnt=0, evictedCnt=0, removedCnt=0, storageLoadCnt=0, storageMissCnt=0, storageHitCnt=0, hitRate=100.0%, collisionCnt=0, collisionSlotCnt=0, longestCollisionSize=0, hashQuality=100, msecs/fetch=-, created=2015-06-01 18:07:58.336, cleared=-, touched=2015-06-01 18:07:58.408, fetchExceptionCnt=0, suppressedExceptionCnt=0, internalExceptionCnt=0, keyMutationCnt=0, infoCreated=2015-06-01 18:08:18.241, infoCreationDeltaMs=1, impl="ClockProPlusCache", coldSize=9676, hotSize=0, hotMaxSize=63569, hotSizeAvg=-1, ghostSize=0, staleSize=0, coldHits=-2080777152, hotHits=0, ghostHits=0, coldRunCnt=0, coldScanCnt=0, cold24hCnt=0, hotRunCnt=0, hotScanCnt=0, hot24hCnt=0, directRemoveCnt=0, integrityState=0.17.fa4fbac7)

Actually this should only hit us, when the per entry access count reaches the 2G limit and there is no eviction.

Unmanaged / annonymous cache

At the moment every cache needs a unique name.

This is needed to have an unique identifier for JMX. We will need it later for external cache configuration, too.

The unique name requirement leads into trouble, when the cache instance has only a temporary or limited lifetime. Applications need to take care that a cache is properly destroyed since it is not garbage collected.

TODO:

  • Support for a cache that needs no name
  • Cache should be GCd when originator has no more reference on it
  • We should keep a WeakReference / make statistics available what caches exist
  • on finalize we need to do a proper destroy and make sure the cache has no
    thread/timers.

StackOverflowError when using Cache.asMap()

Hi there,

From my understanding, Cache.asMap() wraps the Cache object in a ConcurrentMapWrapper. In this class, the methods keySet(), values() and entrySet() are implemented using AbstractSet.
The problem is that AbstractSet.size() method was implemented as "return size()", which recursively calls itself and crashes whenever size() is called.
I believe the intent was to call ConcurrentMapWrapper.size() instead, in which case it should be implemented as "return ConcurrentMapWrapper.this.size()"

Sorry for poor formatting or w/e, first time creating an issue.

P.S. forgot to mention, it was version 0.27-BETA

Entry Weight Support / was: Limiting a cache by memory size?

Hello.

I'm evaluating various options for caching bitmaps in an imaging application. Is it possible to limit the size of a cache2k cache in bytes instead of by the number of entries? I'm loading lots of bitmaps of arbitrary unknown sizes, and so a cache of 100 * 1mb bitmaps is obviously a very different creature to a cache of 100 * 100mb bitmaps!

I can't seem to find anything in the API that would allow this, but perhaps I've overlooked something?

Persistence

The current code base contains support for persistence. However it is not yet production ready. Heavy stress tests show concurrency issues, especially with the iterator() and clear().

Actually I was quite convinced to take persistence out again for 1.0 and first make sure that everything is ship shape and well documented, which is still a lot of work.

Some more thoughts and background:

The clear() implementation actually does some fancy stuff. For the clear() operation to complete in an instant and not produce any delay (a couple of seconds could bring an application down), it switched to an asynchronous buffer and later reconnects the physical storage. Maybe this could be simplified for the first step.

The JSR107 support introduced the CacheWriter and also will need event listeners. This also yields consistency issues that we won't have without them. E.g. the writes to the cache writer need to be partially ordered, so that always the cached value represents the last written value. So, the hard problems, do not necessarily go away by removing the storage....

Meanwhile I also discovered that the internal entry states do not completely support the JSR107 methods. So the next step is to do a make over of the entry locking (the third one). After that maybe some issues are gone already and we can reevaluate what we do with persistence support.

Please do comment here if you have had success or failures with the persistence support.

Get specific cache stats

Hi,

I'm trying to get specific metrics from cache2k: hit rate, hit count, miss count, cache size etc. I want to feed these metrics into http://grafana.org/ so i can get a nice chart of the cache performance.

From what I could find in the documentation the only way to get this is to call toString() on the cache instance.

This means that I will need to parse the various metrics myself from a string. Is there a way to get the metrics separately?

Global shared timer / reduce threads used for expiry timers

Currently each cache needs a timer thread, in case expiry is enabled. An extra thread per cache might be quite resource intensive, in case there are many caches.

Use a shared timer for all caches. The timer needs to handle enough concurrency.

Background Refresh / Refresh Ahead Policy

Current semantic

When enabling background refresh with CacheBuilder.refreshAhead(true):

The old value will be returned by the cache, although it is expired, and will be replaced by the new value, once the loader is finished. In the case there are not enough threads available to start the loading, the entry will expire immediately and the next get() request will trigger the load.

Once refreshed, the entry is in a trail period. If it is not accessed until the next expiry, no refresh will be done and the entry expires regularly.

Analysis

We have indication from our applications, that this is not working well in all scenarios. Example:

  • expiry time is 5 minutes
  • entry is accessed every 30 minutes on average
  • Load has 3s latency

This means the second load is mostly useless and the cache has always a miss, when the entry is accessed by the application.

How long the refreshing is done should be separated from the normal expiry duration. Especially at nighttime the access frequency gets lower. Administrators should be able to decide between:

  • minimize user latency, which means more refreshing
  • minimize the memory footprint and loader calls, which means more latency for the user
Todo
  • in-depth analysis of current cache scenarios
  • what parameters make sense?
  • what is the best behavior for backgroundRefresh(true), for the ease of use principle we want to have a setting which is a good fit for most of the time

Any more thoughts?

slf4j support

With the 0.20 release we can use java.util.logging or commons logging. The dependency on commons logging is optional, so when the jar is present the output is gated through commons logging.

Also support slf4j?
Support slf4j and drop commons logging?

Stamp cached exceptions

Caching exceptions has a downside: We may "see" more exceptions than actually were initially thrown. Add a timestamp and/or id to a cached exception.

Provide a method for unwrapping the underlying cause of a PropagatedCacheException

When using read through access and an exception in the cache source occurs, a PropagatedCacheException(PCE) is thrown, which wraps the original exception in one or even multiple layers (for example if Caches are layered, Cache2: Cache1.get() -> Cache1.get()=PCE(UnderlyingException) => Cache2.get() = PCE(PCE(UnderlyingException))).

As a result PCE.getCause() cannot be used reliably to get the underlying original Exception.
Just skipping intermediate PCE's in PCE.getCause is not a good idea, as it would hide parts of the execution path in the stack trace.

Thus, PCE should have a method getOriginalCause(), like sketched here:

public Throwable getOriginalCause() {
    while (t.getCause() instanceof PropagatedCacheException) {
        t = (Exception) t.getCause();
      }
      if (t.getCause() != null) {
        t = (Exception) t.getCause();
      }
      // Maybe throw (and chuck?) instead
      return t;

}

Moreover it would be much more straightforward if a cache could be configured to always throw the original cause by default.
The cache is nothing more than a transparent layer. Imagine a method foo() which may throw a NullPointerException:

public void foo(Object o) {
Objects.reqireNonNull(o); // throws NPE if o == null
return o;
}

we can write a try/catch-block to catch it:

try {
   foo();
} catch (NullPointerException e) {
// handle exception
}

Now if using foo() as cache source, by reading the source code, e.g.

CacheBuilder ... .source(this::foo)

or (expanded)

CacheBuilder ... .source((e) -> {
Objects.requireNonNull(e);
return e;
})

one would expect a NullPointerException might be thrown. However, a PCE wrapping the NPE is thrown, which might be very unexpected due to the idea of transparency with read-through operation mode.
This also reduces complexity and improves readability:

Cache<Object, Object> cache =
CacheBuilder.newInstance(Object.class, Object.class).source(this::foo).build();

try {
   cache.get(new Object());// with cache.source = foo()
} catch (NullPointerException e) {
// do something
}

is much more readable than

try {
   cache.get(new Object());// with cache.source(this::foo)
} catch (NullPointerException|PropagatedCacheException ex) {
  if (ex instanceof PropagatedCacheException 
     // There might be a very bad exception not coming from the cache source -> no cause
    && ex.getCause() != null) {
    ex = ex.getCause()
  }
  if (!(ex instanceof NullPointerExcetion)) {
    // rethrow, something bad happened
    throw e;
  }
  // handle NPE
}

The programmer might even not have the possibility of a PCE in mind if using a cache.

Generic types and type information for cache construction

Although there is a special method for building caches with collections as value, the available pieces of information are not used when building the cache

For example
Cache<String, Set> userToItemCache =
CacheBuilder.newCache(String.class, Set.class, SeenItem.class)
.expirySecs(30 * 60)
.build();

This would return a Cache<String, Set>, although it would be much better to return a Cache<String, Set<SeenItem>.

Maybe there can be a special buildCollectionCache() method

  public abstract <C extends Collection<T>> Cache<K, C<T>> build(Class<C> _collectionClass);

which simply uses build() and casts the result accordingly? This helps to prevent boilerplate code, as it is a common use case to have Collections as values.

Conditional expiry of entries (e.g.: don't expire if CacheSource is unavailable)

Hey,

I have a Cache object that uses a CacheSource. In my scenario entries normally expiry within 10 minutes but I would like to conditionally change the expiry time just before it's time to expire.
One example is if the CacheSource becomes unavailable, in that case I would like to keep using the Cache in Heap and not expire the entries until the source is available again.

Some possible suggestions:

1)Provide a simple way to disable expiring on a global Cache level,

2)On a per entry level, EntryExpiryCalculator doesn't do the calculation just before expiring but if it did the code could look similar to this:

    public long calculateExpiryTime(K _key, T _value, long _fetchTime, CacheEntry<K, T> _oldEntry) {
       Long expiry = _oldEntry.getLastModification() + TTL;
       /* If cachesource is down, postpone next expiration check by 30 seconds */
       if (!heartBeat.isAlive())
           expiry = _fetchTime + 30000;
       return expiry;
    }
  1. Another alternative would be to build this as an option to the Cache configuration that when selected automatically checks the CacheSource (According to a method that the user has to implement, which returns a boolean) and disables expiry when needed.

Is there a way I can currently achieve this behaviour with the current state of the library? If not I might not be able to use Cache2K.

Thanks,
Gihad

KeyValueSource should not implement closeable

In 1.0.0.CR2 the close method was added in the KeyValueSource interface. Tests with existing code bases and reviews revealed that this was a bad design decision. Should be removed again for V1.

Test for the JCache AsyncDispatcher

org.cache2k.jcache.provider.event.AsyncDispatcher

This class serializes async events on a specific key, while allowing maximum parallelism otherwise.
We should have a thorough test for this.

Either we can do a unit test, or, test it on JCache level.

List / count the number of open caches

When unit testing it is desirable to have a standard way to assert that there are no more caches, i.e. that all caches used in unit tests have been properly closed to prevent possible memory leaks.
This is especially true when multiple tests are set up and torn down serially, which might result in duplicate caches if

This could be done e.g. via

@After
public void tearDown() {
assertFalse("There are no more caches afterCacheManager.getDefault().iterator().hasNext()
}

However this approach has two drawbacks:

  • A list names for all cache managers has to be known to run this assertion for all caches managed by all cache managers, which is very error prone
  • It is quite tedious to write this

As the CacheManager class has a map of all existing cache managers, there should be a method like
"checkNoMoreCaches()".

Maybe such thing also could be moved to an extra class or class in a module which provides helpers for unit testing caches.
Probably this also could be made part of a JUnit Rule which can be put in any place required.

Improve Resilience and Exception Support Features

The current behavior is described here:

In short:

  • Resilience: Exceptions originating from the cache source (loader in JSR107 lingo) can be suppressed, if old data is available
  • Caching and rethrowing of exceptions: If no old data is available exceptions are cached and rethrown on a get. Other caches without exceptions support would query the data source again and again, which might lead to a worse load in case there is a temporary problem with the data source

Both behaviors are enabled by default.

Problems:

  • While the features are useful and make an application more robust, it dangerously may hide permanent problems, e.g. caused by packed loss, below the cache layer. There should be better monitoring / notification options.
  • Rethrowing a cached exception causes confusion, since the exception actually happened earlier
  • In case of a permanent exception the old value will be returned forever. At some point in time it may be better to discard the old value and propagate the exception, so it becomes obvious that there is a permanent failure.

Enhancement ideas:

ExceptionPropagator

On access an exception is rethrown as PropagatedCacheException. Make this configurable. I think there is limited use to rethrow the original exception, since the stack trace gets crippled, but it might be useful to customize the exception text.

SourceExceptionListener

Gets called for every exception that actually happened. This is useful for adding logging. Lower priority, can be done by wrapping the cachesource, if needed.

CacheConfig.setMaximumStaleTime

Maximum time span the cache is allowed to return stale data.

ResiliencePolicy.propageteException(long lastFetch, V oldData, long now, Throwable exception)

Configurable policy that decides whether to propagate the exception or return stale data. Prio: I would prefer the single stale time parameter rather then making it fully flexible.

CacheInfoMXBean.getStaleEntryCount()

The number of entries with stale data (stale means expired and not able to retrieve fresh one).

CacheInfoMXBean.getExceptionsEntryCount()

The number of entries containing exceptions.

CacheInfoMXBean.getAlert() / CacheManagerInfoMX.getAlert()

Alert is the single consolidated health status that can be monitored. Return warning, if more the 20% of a cache content is stale data or cached exceptions.

Cache.invalidate(K key)

Alternative to Cache.remove() for read through configuration. Invalidates an entry, so it gets retrieved from the source on the next access. However, invalidate does not remove the old data, so in case of an exception the cache can still return it.

Fix/investigate probable race on 32 bit systems

I suspect a race condition regarding non atomic writes of a long value on a 32 bit system.
OTOH, I expect only a negligible effect in the valid -> invalid transition of an entry.

At least, needs some investigation and tests.

Always get error , Cache name contains illegal chars: '<', name="TaxCalculatorInstance.<clinit>.47"

Hi, I am trying to create a static field in my class Test.java as this,

public static Cache<String,String> idCache = CacheBuilder.newCache(String.class, String.class).build();

But when I create an instance of Test.java, I got the exception.

Caused by: org.cache2k.impl.CacheUsageExcpetion: Cache name contains illegal chars: '<', name="TestInstance. < c l init > .47"
at org.cache2k.impl.CacheManagerImpl.checkName(CacheManagerImpl.java:130)

I need to share this cache among all the instances of Test.java. So, I use static.

I am confused now, not sure did I use Cache2K incorrectly ?

Support industry standard health checks via JMX

cache2k is already enabled for a simple health check: CacheInfoMXBean.getAlert()

  1. Support the established convention how this is exported: http://hawtio.github.io/hawtio/plugins/health/
  2. The 'Warning' alert can have many different causes. At the moment there is text hint to the cause, instead an administrator would need to investigate all the statistic values. Make some text available that briefly describes the warning cause.

The relevant definition is:

  • level: The severity level such as INFO, WARN, ERROR, CRITICAL to give some indication of how serious the problem is. INFO means its OK and healthy.
  • message: A textual description of the health check status.

How to configure a cache properly?/Provide more JavaDoc for CacheConfig

Properly configuration of a cache is vital for expected behavior and maybe performance. However most of the methods for CacheConfig/CacheBuilder lack any explanations in JavaDoc and no other documentation regarding configuring the cache can be found.

As a newbie to caching I need a cache that expires entries if there hasn't been a get(key) operation for more than 30 Minutes.

Cache<String, MyClass> cache =
  CacheBuilder.newCache(String.class, MyClass.class)
    .source(MyClass::new)
    .name(getClass())
    .expiryDuration(30, TimeUnit.MINUTES)
    .maxSize(10000)
    .backgroundRefresh(false).build();

What is the difference between maxSize and maxSizeBound? I want to limit the cache to a maximum amount of lets say 10k. When reaching the limit, the oldest entries should be evicted from the cache.
Does backgroundRefresh(false) trigger the desired behavior that entries are only refreshed upon access?

Is it possible to register an event handler which is called after an entry expires and is removed from the cache?

Like a method expiryHandler((o) -> enqueueStoringToDatabase(o)) in CacheConfig.

Expiry Listener

In some situations it may be interesting to have a callback fired just before the expiration of a given entry. It would provide a control over this detached entry.

For instance if the expired entry has some allocated resources, it may be required to close the resources before losing a reference to the corresponding object.

Without this feature, it is mandatory to handle a separate cache of the same entries without any expiration and to check manually and periodically if a given entry expired. This is possible but I think that cache2k could provide such a feature.

Thanks !

Cache identity and life time

Current behavior:

If a name is not specified the cache generates a name automatically. The name is
inferred from the call stack trace and contains the simple class name, the method and
the line number of the of the caller to build(). Instead of relying to the
automatic name generation, a name should be chosen carefully.

In case of a name collision the cache is generating a unique name by adding a counter value.

Problem:

With persistence as well as a separate configuration file the lifetime of a cache is actually
not more determined between build() and close(). For persistent caches the cache contains the existing data. If the application does build another cache under the same name, it would currently not contain the existing data, but be empty, since another name was generated.

Solutions:

  • When cache with same name is build, return the existing one. Do check basic parameters for compatibility
  • Refuse operation / produce exception when a cache is build again
  • Act different when in heap only?
  • log warning?

Default Expiry Configuration

By default the expiry duration is 10 minutes. If an ExpiryCalculator is configured in, with a dynamic expiry time per entry, the maximal allowed expiry duration is still 10 minutes, since the default is not changed.

Limiting the expiry time even when the calculator is present is a nice feature, but very surprising.

Idea:

  • Change the default expiry to eternal (no expiry)

But:

  • add a configuration exception if a time value and a calculator is configured in?
  • let the configured duration the maximum time and limit the return values of the expiry calculator?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.