-
Notifications
You must be signed in to change notification settings - Fork 6
Quick start
- Create In-Memory Cache
- Create Disk-Based Cache
- Create Hybrid Cache
- Create Tandem Cache
- Create cache using cache configuration file
- Load cache from a disk with a configuration file
- Load cache from a disk (programmatically)
- Usage example
import com.carrotdata.cache.*;
import com.carrotdata.cache.controllers.*;
import com.carrotdata.cache.index.*;
protected Cache createInMemoryCache(String cacheName) throws IOException {
// Data directory is needed even for in-memory cache; this is where
// data from memory can be saved to. This example uses temporary data folder just for
// the API demonstration purposes, in a real application you will need to provide
// some persistent folder path or use default one: "./data"
Path dataDirPath = Files.createTempDirectory(null);
String dataDir = dataDirPath.toFile().getAbsolutePath();
Builder builder = new Builder(cacheName);
builder
.withCacheDataSegmentSize(16_777_216) // 16MB
.withCacheMaximumSize(8_589_934_592) // 8GB
.withRecyclingSelector(MinAliveRecyclingSelector.class.getName())
.withDataDir(dataDir)
.withMainQueueIndexFormat(SubCompactBaseWithExpireIndexFormat.class.getName()) // This index format supports cache expiration
.withAdmissionController(ExpirationAwareAdmissionController.class.getName()); // This controller does some smart things :)
return builder.buildMemoryCache();
}import com.carrotdata.cache.*;
import com.carrotdata.cache.controllers.*;
import com.carrotdata.cache.index.*;
protected Cache createDiskCache(String cacheName) throws IOException {
Path dataDirPath = Files.createTempDirectory(null);
String dataDir = dataDirPath.toFile().getAbsolutePath();
Builder builder = new Builder(cacheName);
builder
.withCacheDataSegmentSize(67_108_864) // 64MB
.withCacheMaximumSize(687_194_767_360) // 640GB
.withRecyclingSelector(MinAliveRecyclingSelector.class.getName()) // Specify recycling selector type
.withDataDir(dataDir)
.withMainQueueIndexFormat(SubCompactBaseWithExpireIndexFormat.class.getName()) // This index format supports cache expiration
.withAdmissionController(ExpirationAwareAdmissionController.class.getName()); // Specify cache admission controller
return builder.buildDiskCache();
}protected Cache createHybridCache(String ramCacheName, String diskCacheName) throws IOException {
Cache ramCache = createInMemoryCache(ramCacheName);
Cache diskCache = createDiskCache(diskCacheName);
ramCache.setVictimCache(diskCache);
return ramCache;
}import com.carrotdata.cache.*;
import com.carrotdata.cache.controllers.*;
import com.carrotdata.cache.index.*;
protected Cache createInMemoryCompressedCache(String cacheName) throws IOException {
// Data directory is needed even for in-memory cache; this is where
// data from memory can be saved to
Path dataDirPath = Files.createTempDirectory(null);
String dataDir = dataDirPath.toFile().getAbsolutePath();
Builder builder = new Builder(cacheName);
builder
.withCacheDataSegmentSize(16_777_216) // 16MB
.withCacheMaximumSize(34_359_738_368) // 32GB
.withCacheCompressionEnabled(true) // Enable compression
.withRecyclingSelector(MinAliveRecyclingSelector.class.getName())
.withDataDir(dataDir)
.withMainQueueIndexFormat(SubCompactBaseWithExpireIndexFormat.class.getName()) // This index format supports cache expiration
.withAdmissionController(ExpirationAwareAdmissionController.class.getName()); // This controller does some smart things :)
return builder.buildMemoryCache();
}
protected Cache createTandemCache(String ramCacheName, String ramCompressedCacheName) throws IOException {
Cache ramCache = createInMemoryCache(ramCacheName);
Cache compCache = createInMemoryCompressedCache(ramCompressedCacheName);
ramCache.setVictimCache(compCache);
return ramCache;
}The code below demonstrates a generic method for creating caches, including multi-level configurations, from a supplied configuration file. This approach allows you to easily define and manage cache settings, enabling flexible customization to suit various application requirements. By leveraging a configuration file, you can efficiently set up complex cache structures without hardcoding parameters, ensuring that your cache setup remains adaptable and scalable as your needs evolve.
import com.carrotdata.cache.*;
protected Cache createCacheFromConfigFile(String pathToFile) throws IOException {
// Load configuration from a file
CacheConfig conf = CacheConfig.getInstance(pathToFile);
String[] cacheNames = conf.getCacheNames();
Cache cache = null, mainCache = null;
for (String name : cacheNames) {
Cache c = new Cache(name);
if (mainCache == null) {
mainCache = c;
}
if (cache != null) {
cache.setVictimCache(c);
}
cache = c;
}
return mainCache;
}The code below demonstrates a generic method for loading caches from a saved snapshot on disk. This approach ensures that your cache can be quickly restored to its previous state, minimizing downtime and eliminating the need to rebuild the cache from scratch. By leveraging saved snapshots, you can preserve important cached data across sessions, enhancing both the reliability and performance of your application.
import com.carrotdata.cache.*;
protected Cache loadCache(String pathToFile) throws IOException {
// Load configuration from a file
CacheConfig conf = CacheConfig.getInstance(pathToFile);
String[] cacheNames = conf.getCacheNames();
if (cacheNames == null || cacheNames.length == 0) {
throw new IOException("No cache(s) were defined in the configuration file");
}
String mainCache = cacheNames[0];
Cache c = null;
if (conf.isSaveOnShutdown(mainCache)) {
for (int i = 0; i < cacheNames.length; i++) {
Cache cc = Cache.loadCache(cacheNames[i]);
if (cc == null) {
LOG.error("Failed to load cache '{}', will initialize cache from configuration file instead.", cacheNames[i]);
cache = null;
// TODO: dispose?
break;
}
if (c != null) {
c.setVictimCache(cc);
} else {
cache = cc;
}
c = cc;
}
}
if (cache == null) {
// Nothing to load, so create new ones
return createCacheFromConfigFile(pathToFile);
}
}The code below demonstrates a generic method for loading caches from a saved snapshot on disk without relying on a configuration file, allowing the cache to be created purely programmatically. This approach is useful when flexibility and dynamic cache creation are required, as it enables you to restore the cache state directly in your code. By bypassing the need for a configuration file, you can tailor the cache initialization process to specific runtime conditions or application logic, ensuring a seamless and efficient recovery of cached data.
import com.carrotdata.cache.*;
protected Cache loadOrCreateMemoryCache(String cacheName) throws IOException {
Cache cache = null;
if (conf.isSaveOnShutdown(mainCache)) {
cache = Cache.loadCache(cacheName);
}
if (cache == null) {
// Nothing to load, so create new ones
return createInMemoryCache(cacheName);
}
}
protected Cache loadOrCreateDiskCache(String cacheName) throws IOException {
Cache cache = null;
if (conf.isSaveOnShutdown(mainCache)) {
cache = Cache.loadCache(cacheNames[i]);
}
if (cache == null) {
// Nothing to load, so create new ones
return createDiskCache(cacheName);
}
}
protected Cache createCache() throws IOException {
String mainCacheName = "L1Cache";
String diskCacheName = "L2Cache";
Cache mainCache = loadOrCreateMemoryCache(mainCacheName);
Cache diskCache = loadOrCreateDiskCache(diskCacheName);
mainCache.setVictimCache(diskCache);
return mainCache;
}Cache cache = createInMemoryCache("ram1");
byte[] key1 = "key1".getBytes(StandardCharsets.UTF_8);
byte[] value1 = "value1".getBytes(StandardCharsets.UTF_8);
// Put key-value without expiration time
cache.put(key1, value1, 0);
byte[] key2 = "key2".getBytes(StandardCharsets.UTF_8);
byte[] value2 = "value2".getBytes(StandardCharsets.UTF_8);
// Put key-value with expiration time of 1 minute
cache.put(key2, value2, System.currentTimeMillis() + 60 * 1000);
byte[] buffer = new byte[value2.length];
int size = cache.get(key2, 0, key2.length, buffer, 0);
String result = new String(buffer, 0, size, StandardCharsets.UTF_8);
System.out.printf("Value for key %s is %s", key2, result);Note: Carrot Cache, by default, disables both: compression and expiration support. You have to enable them either through configuration file if you use it to create a cache or programmatically.
- Config file
cache.names=L1
cache.types=memory
# Enable compression for cache 'L1'
L1.compression.enabled=true
# Set index format which supports object's expiration
L1.index.format.impl=com.carrotdata.cache.index.SubCompactBaseNoSizeWithExpireIndexFormat
# Set recycling selector to MinAlive
L1.recycling.selector.impl=com.carrotdata.cache.controllers.MinAliveRecyclingSelector
# Set optimized admission controller
L1.admission.controller.impl=com.carrotcache.cache.controllers.ExpirationAwareAdmissionController- Java API
import com.carrotdata.cache.*;
import com.carrotcache.cache.controllers.*;
import com.carrotdata.cache.index.*;
protected createCache(String cacheName) {
Builder b = new Builder(cacheName);
b.withCacheCompressionEnabled(true).
b.withRecyclingSelector(MinAliveRecyclingSelector.class.getName()) // Specify recycling selector type
b.withMainQueueIndexFormat(SubCompactBaseNoSizeWithExpireIndexFormat.class.getName()) // This index format supports cache expiration
b.withAdmissionController(ExpirationAwareAdmissionController.class.getName()); // Specify cache admission controller
// ...
return b.buildMemoryCache();
}Copyright (c) Carrot Data, Inc. 2024