EhCache Configuration in Replicated Mode

In cluster environments, you can configure EhCache in two different topologies, one is distributed, and the other is replicated. In replicated mode, each EhCache instance in the network acts as a peer. There is no master cache. Instances communicated with each other via multicast network messages through RMI. Configuration of EhCache in replicated mode is easy but involves several steps. Let’s look at them closer.

First, you need to add <cacheManagerPeerProviderFactory> element within <ehcache> element in ehcache.xml file.

<cacheManagerPeerProviderFactory
    class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
        properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,multicastGroupPort=4446, timeToLive=32"/>

EhCache instances discover other peers in the network either automatic, or manual. You can manage this option with peerDiscovery attribute.

Second, you need to add <cacheManagerPeerListenerFactory> element, again within <ehcache> element so that EhCache instance will listen to peer messages in the network.

<cacheManagerPeerListenerFactory
    class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
        properties="hostName=localhost,port=40001,socketTimeoutMillis=2000"/>

hostName property allows entering hostname or IP in order to direct EhCache listen multicast messages through a specific interface if there are several interfaces on the cluster node.

The third addition, <cacheEventListenerFactory> element goes into either <cache> or <defaultCache> elements the configuration file.

<defaultCache
  maxElementsInMemory="10000"
  eternal="false"
  timeToIdleSeconds="0"
  timeToLiveSeconds="120"
  maxElementsOnDisk="10000000"
  diskExpiryThreadIntervalSeconds="120"
  memoryStoreEvictionPolicy="LRU">
    <persistence strategy="localTempSwap"/>
    <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
        properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, 
        replicatePutsViaCopy=true,replicateUpdatesViaCopy=true, replicateRemovals=true "/>
</defaultCache>

This element enables a cache event listener in order to replicate operations performed on this cache region.

You can choose whether replication should go async or sync via replicateAsynchronously attribute. Async mode will be faster, but sync is safer as it will wait to ensure every other peer in the network got updated.

replicatePuts, replicateUpdates and replicateRemovals attributes allow us to manage what operations will be replicated on other peers.

replicatePutsViaCopy, and replicateUpdatesViaCopy attributes changes replication strategy of cache puts and updates either by copying or invalidating data in other peers.

The last important thing about in replicated cache configuration is use of time to idle. As time to idle is incosistent with replicated caching, you should not use it in your cache region configuration. You can set it to 0 in your cache configuration.

EhCache’i Monitor Etmek

EhCache özellikle Hibernate’deki ikincil önbellek kabiliyetini sağlamak için yaygın biçimde kullanılmaktadır. EhCache ile çalışırken “cache region” olarak adlandırılan bölgelerdeki aktiviteleri izlemek için değişik yollara başvurabiliriz.

1. Yol: EhCache Monitor Uygulamasını Kullanabiliriz

 

Öncelikle Terracotta firması tarafından geliştirilen ve geliştirme amaçlı olarak ücretsiz kullanılabilen ehcache-monitor uygulaması Terracotta sitesinden indirilmelidir. Monitor uygulaması web uygulaması olarak çalıştırılarak değişik lokasyonlardan gelecek “cache probe” olarak adlandırılan bilgilendirmeleri dinler. Belirli periyotlarla bu bilgiler güncellenerek uygulamamızdaki cache aktiviteleri monitor uygulaması üzerinden takibe sunulmuş olur.
Monitor uygulamasını çalıştırmak için işletim sistemine göre bin dizini altındaki startup scriptlerinden birisini çalıştırmalıyız. Bu script jetty server çalıştırarak monitor uygulamasını devreye alacaktır. Uygulama ile ilgili herhangi bir konfigürasyon değişikliği yapmak istersek bunu etc dizini altındaki konfigürasyon dosyalarından gerçekleştirebiliriz.

Monitor uygulamasını çalıştırdıktan sonra kendi uygulamamızın bağımlılıkları arasına ehcache-probe.jar‘ı, uygulamamızdaki ehcache.xml dosyası içerisine de aşağıdaki gibi bir tanımı eklemeliyiz.

<cacheManagerPeerListenerFactory
 class="org.terracotta.ehcachedx.monitor.probe.ProbePeerListenerFactory"
 properties="monitorAddress=localhost,monitorPort=9889, memoryMeasurement=true" />

Bu tanımdaki monitorAddress ve monitorPort attribute’ları monitor uygulamasının çalıştığı sistemi göstermelidir. Ehcache.xml dosyasındaki cache region tanımlarında statistics attribute değerini true yaparak ve uygulamamızı çalıştıralım. Artık Ehcache cache bölümleri üzerindeki aktiviteleri monitor uygulaması üzerinden takip edebiliriz.

2. Yol: CacheManager ve İlgili Nesneleri Mbean ile Erişilebilir Kılabiliriz

Diğer bir yol CacheManager ve ilgili nesnelerini MBean olarak dışarıdan erişilebilir kılmaktır. Bu sayede cache ile ilgili verilere herhangi bir JMX console vasıtası ile erişilebilir. Bunun için uygulamamızda değişik yollar izlenebilir. Aşağıda en basit şekli ile bunu nasıl yapabileceğimiz gösterilmektedir.

MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ManagementService.registerMBeans(cacheManager,mBeanServer,true,true,true,true);

Öncelikle JRE’deki MBean sunucusuna erişilmelidir. Ardından EhCache’in ManagementService sınıfı aracılığı ile cacheManager nesnesi mBeanServer’a register edilir. Boolean input argümanları CacheManager, Cache, CacheConfiguration, CacheStatistics yapılarından hangilerinin MBean vasıtası ile dışarıya açılacağını belirler.

Bu kayıt işlemini uygulamamızın başlangıç kısmında yapmamız gerekir. Uygulamamız açık kaldığı müddetçe herhangi bir JMX uyumlu console vasıtası ile cache yapılarının MBean aracılığı ile sundukları attribute ve fonksiyonlara erişebiliriz. JMX console olarak JDK ile beraber gelen jconsole kullanılabilir.

 

 

$jconsole

Yukarıdaki komut ile JConsole açıldıktan sonra uygulamanın MBean sunucusuna bağlanarak kayıtlı Mbean nesnelerine erişebiliriz.

3. Yol: Kendi EhCache Debugger’ımızı Yazabiliriz

Eğer yukarıdaki yöntemler ile elde ettiğimiz veri bizi tatmin etmez ise CacheManager nesnesi üzerinden doğrudan istediğimiz bilgiye erişmemiz de mümkündür. Bunun için uygulama içerisinde bir miktar kod yazmamız yeterli olacaktır.

String[] cacheNames = cacheManager.getCacheNames();
Cache cache = cacheManager.getCache(cacheName);

Öncelikle getCacheNames() metodu ile tanımlı cache bölgelerinin isimlerini elde etmeliyiz. Ardından her bir cache nesnesine bu isimler vasıtası ile ulaşabiliriz.

for(Object key:cache.getKeys()) {
 Element element = cache.getQuiet(key);
}

Cache nesnesinin getKeys() metodu ile o anda cache’e eklenmiş kayıtların anahtar kelimelerine erişerek Cache.getQuiet() metodu ile her bir key’e karşılık gelen değeri Element nesnesi olarak alırız. Burada dikkat edilmesi gereken nokta eğer Cache.get() metodu ile bu değerlere erişecek olursak CacheManager bizim erişimlerimizi de cache istatistiklerine dahil edecektir. Bu nedenle debug işlemi sırasında getQuiet() metodunun kullanılması önemlidir.

element.getHitCount();
element.getCreationTime();

Object entry = element.getObjectValue();

Element nesnesi üzerinden değerin eklenme, erişim, güncellenme zamanlarını, kaç defa erişildiğini vs. öğrenmek mümkündür. Cache’de tutulan değerimiz Element nesnesi içerisindedir. Cache’i kullanan uygulamaya göre değerin tipi ve içeriği farklı farklı olacaktır.

((CacheEntry)entry).getDisassembledState();

Hibernate için bu genellikle CacheEntry nesnesidir. Örneğin, CacheEntry.getValue() metodu ile, eğer değer entity ise entity nesnenin disassembled haline erişmek mümkündür.

Yukarıda sözünü ettiğimiz bu üç yöntemden uygun olan herhangi birisini kullanarak EhCache’in devrede olduğu uygulamalarımızdaki veri erişim işlemlerinin davranışını daha kolay biçimde takip etmemiz mümkündür. Özellikle Hibernate’in ikincil önbellek kabiliyetinin devrede olduğu senaryolarda bu tür bir monitör işleminin geliştirme ve test süreçlerimize önemli katkısı olacaktır.

Hibernate ve İkincil ÖnBellek Yazı Dizisi

Hibernate ve Second Level Cache’in anlatıldığı bir yazı dizisini yayımladık. Yazılarda Hibernate’in çok sık kullanılmayan, kullanıldığında da zaman zaman saç baş yolduran bu özelliği hakkında dikkat edilmesi gereken noktalar, eşzamanlı erişim stratejilerinin davranışları, önbelleğin konfigürasyonu ve kullanımı hakkında işinize yarayabilecek konulara değindik.

Hibernate İkincil Önbellek Eşzamanlı Erişim Stratejileri

Hibernate ikincil önbellek yapısı, değişik eşzamanlı erişim stratejilerini desteklemektedir. Bunlar daha önce de belirttiğimiz gibi transaction isolation düzeylerine benzemektedir. Bu stratejilerin kısıtları azaldıkça performansları artar, ancak uygulamanın stale veri ile karşılaşma ihtimali de aynı şekilde fazlalaşır. Dört adet eşzamanlı erişim stratejisi vardır.

Transactional

  • Senkron bir eşzamanlı erişim stratejisidir. Başka bir deyişle önbellek üzerindeki veri güncellemeleri transaction ile birlikte gerçekleştirilir.
  • JBoss TreeCache gibi çözümler bu stratejiyi desteklemektedir.
  • Repeatable read izolasyon düzeyine karşılık gelmektedir.
  • Eğer entity önbellekte yoksa öncelikle ön belleğe yerleştirilir. Eğer önbellekte mevcut ise buradan dönülür.
  • Önbellekte tutulan entity ile ilgili yapılan insert veya update, önbelleğe transaction sırasında yansıtılır.

 

Read write

  • Asenkron bir eşzamanlı erişim stratejisidir. Yani önbellek üzerindeki veri güncelleme işlemleri transaction sonlandıktan sonra yürütülür.
  • Read committed izolasyon düzeyine karşılık gelir.
  • Cache erişimi synchronized metotlar ile gerçekleştirilir ve cache lock’lanır.
  • Güncelleme yapılan entity için transaction sırasında soft lock oluşturulur. Her transaction, entity üzerindeki lock count değerini bir artırır.
  • Entity ile ilgili insert ve update işlemleri önbellek tarafına, transaction sonunda yansıtılır. Bu aşamadan sonra soft-lock salıverilir. Entity üzerindeki değişikliklerin önbelleğe yansıtılabilmesi için o anda tek bir soft lock’un olması gerekir.
  • Eğer transaction’ın timestamp’i (Session’ın yaratılma anına denk gelmektedir) entity’nin önbellekteki timestamp değerinden küçük ise, yani Session entity önbelleğe konmadan evvel oluşturulmuş ise entity’ye önbellek yerine veritabanından erişilir.
  • Yine entity üzerinde herhangi bir soft lock söz konusu ise entity’ye erişim yine önbellek yerine veritabanından gerçekleşir.
  • Entity önbellekte mevcut değilse doğrudan önbelleğe konur. Ancak önbellekte mevcut ise, entity’nin önbellekte güncellenebilmesi için de transaction timestamp’in entity’nin önbellekteki timestamp değerinden, ya da yeni version değerinin eskisinden büyük olması gerekir.

 

Nonstrict read write

  • Read-write erişim stratejisinin benzeridir. Ancak read-write gibi önbellek-db senkronizasyonu sağlamayı hedeflemez.
  • Önbellek erişim metotları synchronized değildir, önbelleğe erişimin lock’lanması veya soft lock gibi bir mekanizmada yoktur. Bu nedenle read-write’dan daha hızlıdır.
  • Bu erişim stratejisinde “stale veri” ile karşılaşmak ihtimal dahilindedir.
  • Eğer entity önbellekte ise herhangi bir kontrole tabi tutulmadan dönülür.
  • Eğer entity önbellekte mevcut değil ise DB’den okunur, ardından herhangi bir kontrole tabi tutmadan önbelleğe yerleştirilir.
  • Entity üzerinde güncelleme söz konusu ise transaction sonunda, entity önbellekten çıkarılır.
  • Insert edilen entity, önbelleğe konmaz.

 

Read only

  • Bu erişim stratejisi sadece immutable entity’ler için uygundur. Entity üzerinde değişiklik yapmaya izin vermez.
  • Eğer entity cache’de mevcut ise dönülür, yoksa DB’den yüklenir ve önbelleğe yerleştirilir.
  • Entity, insert işlemi sırasında önbelleğe de konur.

 

Bu erişim stratejileri sadece entity ve collection’lar içindir. Sorgularda ise sonuç eğer sorgu önbelleği aktif ise ve sorgunun kendisi cache’lenmiş ise doğrudan önbellekteki sorgu bölümü üzerinden gelir. Eğer sorgu önbelleği aktif değilse veya sorgu üzerinde cache tanımlı değilse bu durumda sorgu mutlaka DB’ye gider ve sorgu sonucu DB’den dönen içerik olur. Sorgu entity sorgusu ise dönen entity içeriği DB’den dönen veri ile oluşacaktır. Ayrıca collection’lar üzerinde yapılan değişiklikler de collection önbelleğinin doğrudan invalidate edilmesine neden olur.

Bir sonraki konumuz ikincil önbelleğin konfigürasyonu ve kullanımı olacak.

First Impression Is Really Important

I have attended a 3 day workshop for Oracle Coherence Product. Altough I am a veteran Eclipse user, we used JDeveloper 11g Technology Preview 3 during our lab sessions. During those lab sessions, I noticed an ugly thing related with JDeveloper. JDeveloper provides some IDE mechanism to generate equals and hashCode methods for your classes. However, it fails to generate hashCode methods correctly when your classes contain primitive fields which need to be included in hashCode generation.

public class Foo {
	private int id;
	public int hashCode() {
		final int PRIME = 37;
		int result = 1;
		return result;
	}
}

Above is the code block I just generated using JDeveloper. Altough I chosed id to include in when generating hashCode, it doesn’t have it. As a result, you have same hashCode value for all your Foo objects. Not good if you are dealing with data structures like Hashtable in your application. In order to generate it correctly, you simply need to convert your primitive type to its Java wrapper equivalent. After this conversion, it looks more appropriate than above;

public int hashCode() {
	final int PRIME = 37;
	int result = 1;
	result = PRIME * result + id == null) ? 0 : id.hashCode(?;
	return result;
}

In summary, I think, it is really important for a new product to handle some fundamental tasks appropriately in order to get acceptance by its users.