java - org - ehcache persistir en problemas de disco
org springframework cache ehcache (8)
Quiero hacer algo con ehcache en Java que creo que debería ser extremadamente simple, pero he pasado suficiente tiempo frustrándome con los documentos ...
Escribir un valor en un caché persistente de disco. Apagar.
Vuelva a arrancar y lea ese valor.
Aquí está mi función de Java:
private static void testCacheWrite() {
// create the cache manager from our configuration
URL url = TestBed.class.getClass().getResource("/resource/ehcache.xml");
CacheManager manager = CacheManager.create(url);
// check to see if our cache exits, if it doesn''t create it
Cache testCache = null;
if (!manager.cacheExists("test")) {
System.out.println("No cache found. Creating cache...");
int maxElements = 50000;
testCache = new Cache("test", maxElements,
MemoryStoreEvictionPolicy.LFU, true, null, true, 60, 30,
true, Cache.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null);
manager.addCache(testCache);
// add an element to persist
Element el = new Element("key", "value");
testCache.put(el);
testCache.flush();
System.out.println("Cache to disk. Cache size on disk: " +
testCache.getDiskStoreSize());
} else {
// cache exists so load it
testCache = manager.getCache("test");
Element el = testCache.get("key");
if (null == el) {
System.out.print("Value was null");
return;
}
String value = (String) el.getObjectValue();
System.out.println("Value is: " + value);
}
manager.shutdown();
}
Y aquí está mi configuración de caché (ehcache.xml):
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="C:/mycache"/><!-- java.io.tmpdir -->
<defaultCache
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
Aunque veo los archivos test.index y test.data en el disco después de la primera ejecución, la salida de esta función siempre es la siguiente (parece que nunca se carga el caché del disco):
No se encontró ningún caché. Creando caché ...
Caché al disco. Tamaño de caché en disco: 2
Debo estar haciendo algo tonto aquí, ¡pero no estoy seguro de qué!
Supongo que esto funcionará, pero aún me pregunto por qué las memorias caché definidas programáticamente no pueden persistir en el disco (¡especialmente porque aún están escritas en el disco!)
ehcache.xml
entendido, una memoria caché creada mediante programación (es decir, no declarada en ehcache.xml
) puede usar un DiskStore
que puede ser persistente, pero esto no significa que la memoria caché se cargará automáticamente mediante el CacheManager
CacheManager. En realidad, no creo que los archivos mencionados anteriormente contengan los parámetros de caché.
Pero, si "vuelve a crear" el caché mediante programación con los mismos parámetros, encontrará las entradas previamente almacenadas en caché desde el DiskStore
.
Bueno, bueno, lo que hice para solucionar esto fue configurar mi caché usando el archivo de configuración. Aquí está la configuración actualizada:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="C:/mycache" />
<defaultCache
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
<cache
name="test"
maxElementsInMemory="500"
eternal="true"
overflowToDisk="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="1"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
Así que básicamente no usé el constructor para definir el caché.
Supongo que esto funcionará, pero aún me pregunto por qué las memorias caché definidas programáticamente no pueden persistir en el disco (¡especialmente porque aún están escritas en el disco!).
Gracias por los comentarios chicos.
Creo que debería eliminar la prueba manager.cacheExists(..)
y simplemente crear su caché usando testCache = manager.getCache("test");
En lugar de usar el new Cache(..)
. Incluso si su caché es diskPersistent, no existirá hasta que lo obtenga por primera vez. (Al menos eso es lo que pienso, ya que solo uso getCache(..)
y hace exactamente lo que estás buscando)
Nota:
También puede agregar algo como esto para asegurarse de que existe el caché:
Cache cache = manager.getCache(name);
if (cache == null) {
throw new NullPointerException(String.format("no cache with name %s defined, please configure it in %s", name, url));
}
Nota 2:
Si su archivo de configuración se llama ehcache.xml, no debe usar CacheManager.create(url)
.En su lugar, use el CacheManager singleton: Creo que he confundido el uso de CacheManager.create(url)
con el uso de un new CacheManager(url)
. Aún así, debe usar el singleton para ehcache.xml
y el new CacheManager(url)
para cualquier otra cosa.
// ehcache.xml - shared between different invocations
CacheManager defaultManager = CacheManager.getInstance();
// others - avoid calling twice with same argument
CacheManager manager = CacheManager.create(url);
Usar CacheManager.create(..)
es problemático ya que puede ignorar completamente la URL pasada si se ha llamado antes a alguno de los métodos create(..)
o getInstance()
:
public static CacheManager create(URL configurationFileURL) throws CacheException {
synchronized (CacheManager.class) {
if (singleton == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Creating new CacheManager with config URL: " + configurationFileURL);
}
singleton = new CacheManager(configurationFileURL);
}
return singleton;
}
}
Por eso no recomendaría el uso de ninguno de los CacheManager.create(..)
. Utilice CacheManager.getInstance()
o new CacheManager(url)
.
Después de pasar un tiempo de calidad con el depurador, creo que tengo una respuesta para el OP.
El problema (al menos por lo que he visto) se centra en los archivos de caché de disco no agrupados y en cómo se vuelven a leer. En el archivo net.sf.ehcache.store.compound.factories.DiskPersistentStorageFactory.java, el método:
public DiskPersistentStorageFactory(Ehcache cache, String diskPath) {
super(getDataFile(diskPath, cache), cache.getCacheConfiguration().getDiskExpiryThreadIntervalSeconds(),
cache.getCacheConfiguration().getDiskSpoolBufferSizeMB(), cache.getCacheEventNotificationService(), false);
indexFile = new File(getDataFile().getParentFile(), getIndexFileName(cache));
flushTask = new IndexWriteTask(indexFile, cache.getCacheConfiguration().isClearOnFlush());
if (!getDataFile().exists() || (getDataFile().length() == 0)) {
LOG.debug("Matching data file missing (or empty) for index file. Deleting index file " + indexFile);
indexFile.delete();
} else if (getDataFile().exists() && indexFile.exists()) {
if (getDataFile().lastModified() > (indexFile.lastModified() + TimeUnit.SECONDS.toMillis(1))) {
LOG.warn("The index for data file {} is out of date, probably due to an unclean shutdown. "
+ "Deleting index file {}", getDataFile(), indexFile);
indexFile.delete();
}
}
diskCapacity = cache.getCacheConfiguration().getMaxElementsOnDisk();
memoryCapacity = cache.getCacheConfiguration().getMaxElementsInMemory();
memoryPolicy = determineEvictionPolicy(cache.getCacheConfiguration());
}
comprueba las marcas de tiempo en los archivos de datos. El problema que estoy viendo es que no importa cómo termine cerrando el caché / administrador, los archivos nunca se sincronizan correctamente. Mi solución rápida y sucia era ajustar la hora del archivo de datos para que pasara la marca de tiempo en el archivo de índice:
File index = new File( path, name + ".index" );
File data = new File( path, name + ".data" );
data.setLastModified( index.lastModified() + 1 );
Por supuesto, esto no es elegante, pero satisface mis necesidades, ya que nuestro proyecto utiliza cachés agrupados, y esto me permite depurar de forma independiente con un caché persistente ... y sin tener que ejecutar Terracotta localmente.
Una advertencia es que para las memorias caché no agrupadas, tengo que vaciar () después de cada acción () y eliminar () para mantener la imagen del disco fresca, especialmente cuando la depuración se debe a la falta de soporte de apagado cuando simplemente "tira" el enchufe".
Esto me tomó un tiempo descubrirlo, pero básicamente lo que se necesita hacer aquí es crear el CacheManager en consecuencia.
Si crea el administrador de caché y los cachés de la misma manera en que lo creó en el xml, funcionará.
net.sf.ehcache.CacheManager manager = net.sf.ehcache.CacheManager
.create(new Configuration().diskStore(
new DiskStoreConfiguration().path("C:/mycache")
)
.cache(new CacheConfiguration()
.name(testName)
.eternal(true)
.maxBytesLocalHeap(10000, MemoryUnit.BYTES)
.maxBytesLocalDisk(1000000, MemoryUnit.BYTES)
.diskExpiryThreadIntervalSeconds(0)
.diskPersistent(true)));
Esto podría ser un poco tarde, pero tuve el mismo problema: lo que ayudó fue cerrar el administrador de caché.
(de docu: http://ehcache.org/documentation/code-samples#ways-of-loading-cache-configuration )
Apague el administrador de caché de singleton:
CacheManager.getInstance().shutdown();
Cierre una instancia de CacheManager, suponiendo que tiene una referencia al CacheManager llamado:
manager.shutdown();
Tuve y resolví un problema similar.
Quiero configurar ehcache para que tenga un caché determinado de elementos persistentes en el disco. Pero quiero hacerlo solo en el entorno local (el entorno de producción funciona con una persistencia distributed
), así que cambio la configuración mediante programación cuando se inicia la aplicación (una aplicación web en mi caso)
File configurationFile = new File(event.getServletContext().getRealPath(EHCACHE_CONFIG_PATH));
Configuration configuration = ConfigurationFactory.parseConfiguration(configurationFile);
//...doing other stuff here...
CacheConfiguration cacheConfiguration = configuration.getCacheConfigurations().get("mycachename");
if(localEnvironment){
cacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.DISTRIBUTED));
}else{
//siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE));
//deprecated lines..
siteCacheConfiguration.setDiskPersistent(true);
siteCacheConfiguration.setOverflowToDisk(true);
}
Tuve un problema con la línea comentada siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE))
, de hecho, el código Ehcache (estoy usando ehcache-2.6.11
) lanza una Excepción si usas Strategy.LOCALRESTARTABLE
sin una Versión empresarial del jarro:
CacheException: You must use an enterprise version of Ehcache to successfully enable enterprise persistence.
Al indagar en el código, me di cuenta de que estas dos líneas (en desuso) hacen lo mismo eludiendo la versión empresarial Excepción
siteCacheConfiguration.setDiskPersistent(true);
siteCacheConfiguration.setOverflowToDisk(true);
¡Recuerde agregar CacheManager.getInstance().shutdown()
en el cierre de la aplicación!
Espero que esto ayude.
Una pequeña pista si su caché en el disco permanece vacío: asegúrese de que sus elementos en el caché sean serializables. ehcache registra si ese no es el caso, pero mi configuración de registro no imprimió estas entradas de registro.