Regarding Topic 1:
Just a sidenote: Please do not confuse expiry and eviction. Expiry means the entry may not returned by the cache any more and may happen at a specified point in time or after a duration. Eviction is the action to free resources, the entry is removed from the cache. After expiry, eviction may happen at the same time or later.
All common cache products do not have support for exact, aka "point in time", expiry. We need that usecase very often in our applications so I spent some effort with cache2k to support this.
Here is a blueprint for cache2k:
static class MetricsEntry {
long nextUpdate;
List<Metrics> metrics;
}
static class MyEntryExpiryCalculator implements EntryExpiryCalculator<Integer, MetricsEntry> {
@Override
public long calculateExpiryTime(Integer _key, MetricsEntry _value, long _fetchTime, CacheEntry _oldEntry) {
return _value.nextUpdate;
}
}
Cache createTheCache() {
Cache<Integer, MetricsEntry> cache =
CacheBuilder.newCache(Integer.class, MetricsEntry.class)
.sharpExpiry(true)
.entryExpiryCalculator(new MyEntryExpiryCalculator())
.source(new MySource())
.build();
return cache;
}
If you have a time reference in the metrics objects, you can use that and you can omit the additional entry class. sharpExpiry(true)
instructs cache2k for exact expiry. If you leave this out, the expiry may be a few milliseconds off, but the access time is slightly faster.
Regarding Topic 2:
The straight forward approach would be to use the interval minutes as cache key.
Here is a cache source (aka cache loader) that strictly returns the metrics of the previous interval:
static class MySource implements CacheSource<Integer, MetricsEntry> {
@Override
public MetricsEntry get(Integer interval) {
MetricsEntry e = new MetricsEntry();
boolean crossedIntervalEnd;
do {
long now = System.currentTimeMillis();
long intervalMillis = interval * 1000 * 60;
long startOfInterval = now % (intervalMillis);
e.metrics = calculateMetrics(startOfInterval, interval);
e.nextUpdate = startOfInterval + intervalMillis;
now = System.currentTimeMillis();
crossedIntervalEnd = now >= e.nextUpdate;
} while (crossedIntervalEnd);
return e;
}
}
That would return the metrics for 10:00-10:05 if you do the request on lets say 10:07.
If you just want to calculate instantly the metrics of the past interval, then it is simpler:
static class MySource implements CacheSource<Integer, MetricsEntry> {
@Override
public MetricsEntry get(Integer interval) {
MetricsEntry e = new MetricsEntry();
long intervalMillis = interval * 1000 * 60;
long startOfInterval = System.currentTimeMillis();
e.metrics = calculateMetrics(startOfInterval, interval);
e.nextUpdate = startOfInterval + intervalMillis;
return e;
}
}
The use of the cache source has an advantage over put()
. cache2k is blocking, so if multiple requests come in for one metric, only one metric calculation is started.
If you don't need exact expiry to the millisecond, you can use other caches, too. The thing you need to do is to store the time it takes to calculate the metrics within your cache value and then correct the expiry duration accordingly.
Have a good one!