9 Replies Latest reply on Apr 18, 2012 4:18 PM by dudalov

    How to exclude some objects from eviction ?

    dudalov

      I'm looking for cache configuration that would allow to keep some objects in cache for the entire cache lifecycle. These keep-forever objects represent some internal entries that are frequently used and not known during compile-time. All other objects should be subject to the selected eviction policy, which in my case could be LRU.

       

      According to the documentation https://docs.jboss.org/author/display/ISPN/Eviction mortal entries were not evicted prior 4.2, they were removed based on expiration settings. That seems allowed to keep mortal objects longer than immortal ones. But now all entries are treated equally and I wonder how to configure cache for my needs. I could probably achieve the same goal by creating two cache instances: one for mortal and another for immortal objects with different eviction policies (LRU and NONE). Does it sound like a proper way to do it? Is there any better option?

       

      Thanks

        • 1. Re: How to exclude some objects from eviction ?
          galder.zamarreno

          It's easy, you can set default eviction/expiration settings in the config, and for the keep-forever objects, call put passing the right parameters for expiration (a negative value) via: http://goo.gl/Y8Ogc

          • 2. Re: How to exclude some objects from eviction ?
            dudalov

            Sigh. No luck. Here’s what I tried in a unit test, but it didn’t work as expected. First I added two "immortal" Objects using

                Cache<K,V>.put(id, object)

            Then I added "mortal" objects to exceed cache size

                long MORTAL_LIFESPAN = 10 * 365L;

                TimeUnit MORTAL_UNIT = TimeUnit.DAYS;

                Cache<K,V>.put(id, object, MORTAL_LIFESPAN, MORTAL_UNIT, MORTAL_LIFESPAN, MORTAL_UNIT)

            I also added a listeneter to see when "immortal" objects are evicted. They were removed as soon as cache reached the configured capacity. It doesn't look like these objects were treated differently.

             

            It was tested with the settings from the "replication" example with only one cache node. Specifically,

            _manager = new DefaultCacheManager(

               GlobalConfigurationBuilder.defaultClusteredBuilder()

                .transport().addProperty("configurationFile", "jgroups.xml").build(),

                    new ConfigurationBuilder()

                        .clustering().cacheMode(CacheMode.REPL_SYNC).build()

               );

             

            Configuration config = new ConfigurationBuilder().eviction()

                .maxEntries(cacheSize).strategy(evictionStrategy)

                .expiration().wakeUpInterval(5000L).maxIdle(120000L)

                .clustering().cacheMode(CacheMode.REPL_SYNC)

                .build();

            _manager.defineConfiguration(cacheName, config);

            Cache<String, String> _infinispanCache = _manager.getCache(cacheName);

             

            I also ran this test with explicit -1 for lifespan and idleTime, but the result was the same. Do I need to specify any other settings for cache or manager to make it work? Did I miss any?

             

            Thanks

            • 3. Re: How to exclude some objects from eviction ?
              galder.zamarreno

              I think you're confusing the concepts of eviction of expiration here.

               

              Expiration is about deleting stuff forever (maxIdle and lifespan params). This can certainly be overriden.

               

              Eviction is another thing, is about maintaining the cache under certain size. This cannot be overriden. Even if data must live forever, if the capacity is exceeded, data will be deleted according to the algorithm chosen, LRU/LIRS. This is irrespective of the lifespan of the data... see https://docs.jboss.org/author/x/MwY5 for more info

              • 4. Re: How to exclude some objects from eviction ?
                dudalov

                I understand that these settings are different and that they are used for different purposes, but it’s what was suggested in the response #1 and what according to the documentation was a solution prior 4.2 release. I tried the suggested way but it didn’t work. Did I misread the directions? Is it supported configuration? Again, here’s what I’m looking for:

                     a1, a2, …. aN            a limited set of keep-forever objects (e.g. often used system entries)

                     b1, b2, ………… bM   less important Objects of the same type

                I want to keep both As and Bs in the same cache, but evict only Bs when the cache is full.

                I can tell A from B when I add them to the cache, so I can call “put” with different parameters if needed. Is it achievable? If “yes” then “how”?

                 

                Thank you

                • 5. Re: How to exclude some objects from eviction ?
                  galder.zamarreno

                  Hmmm, I don't see what's wrong here exactly. What about you provide a unit test with the code that you had and the assertions that you expect?

                  • 6. Re: How to exclude some objects from eviction ?
                    dudalov

                    Sorry, I don't see any "attach" option, so dumping the whole test here:

                     

                    package dev.cache.infinispan;

                     

                    import java.util.Date;

                    import java.util.Map;

                    import java.util.Map.Entry;

                    import java.util.concurrent.TimeUnit;

                     

                    import org.infinispan.Cache;

                    import org.infinispan.eviction.EvictionStrategy;

                    import org.infinispan.manager.DefaultCacheManager;

                    import org.infinispan.notifications.Listener;

                    import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;

                    import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;

                    import org.infinispan.configuration.cache.Configuration;

                    import org.infinispan.configuration.cache.ConfigurationBuilder;

                     

                    import junit.framework.TestCase;

                    import junit.framework.TestSuite;

                    import junit.textui.TestRunner;

                     

                    public class KeepForeverTest extends TestCase  {

                     

                        private static String CLASSNAME = KeepForeverTest.class.getCanonicalName();

                     

                        private DefaultCacheManager _manager;

                        private static final long FOREVER_LIFESPAN = -1; // 

                        private static final TimeUnit FOREVER_UNIT = TimeUnit.DAYS;

                        private static final long MORTAL_LIFESPAN = 10 * 365L; // 

                        private static final TimeUnit MORTAL_UNIT = TimeUnit.DAYS;

                     

                        private static void debug(String method, String msg) {

                            System.out.format("[%s] %s : %s : %s\n", CLASSNAME, new Date(), method, msg);

                        }

                     

                        public KeepForeverTest() {

                        }

                     

                        protected void setUp() throws Exception {

                            super.setUp();

                     

                            _manager = new DefaultCacheManager();

                            /*

                                       GlobalConfigurationBuilder.defaultClusteredBuilder()

                                        .transport().addProperty("configurationFile", "jgroups.xml").build(),

                                            new ConfigurationBuilder()

                                                .clustering().cacheMode(CacheMode.REPL_SYNC).build()

                                       );

                            */                  

                        }

                     

                        protected void tearDown() throws Exception {

                            _manager.stop();

                            super.tearDown();

                        }   

                     

                        private Cache<String, String> getCache(String cacheName, int capacity) {

                            EvictionStrategy evictionStrategy = EvictionStrategy.LRU;

                     

                            Configuration config = new ConfigurationBuilder().eviction()

                                .maxEntries(capacity).strategy(evictionStrategy)

                                .expiration().wakeUpInterval(5000L).maxIdle(120000L)

                                //.clustering().cacheMode(CacheMode.REPL_SYNC)

                                .build();

                     

                            _manager.defineConfiguration(cacheName, config);

                            Cache<String, String> infinispanCache = _manager.getCache(cacheName);

                            return infinispanCache;

                        }

                     

                        @org.junit.Test

                        public void testKeepForever() throws Exception {

                            String cacheName = "foo";

                            int capacity = 5;

                            Cache<String, String> cache = getCache(cacheName, capacity);

                            cache.addListener(new EntryChangeListener());  // to see when the resources are evicted

                     

                            // 1. add two keep-forever objects

                            Resource rs1 = new Resource("immortal_1", "immortal_value1");

                            Resource rs2 = new Resource("immortal_2", "immortal_value2");

                            cache.put(rs1.getID(), rs1.getValue(), FOREVER_LIFESPAN, FOREVER_UNIT, FOREVER_LIFESPAN, FOREVER_UNIT);

                            cache.put(rs2.getID(), rs2.getValue(), FOREVER_LIFESPAN, FOREVER_UNIT, FOREVER_LIFESPAN, FOREVER_UNIT);

                     

                            // 2. verify that resources were added to the cache

                            String expected_rs1 = cache.get(rs1.getID());

                            String expected_rs2 = cache.get(rs2.getID());

                            assertNotNull("Resource 1 should be presented in the cache", expected_rs1);

                            assertNotNull("Resource 2 should be presented in the cache", expected_rs2);

                     

                            // 3. add more resources to make sure that the number exceeds the capacity

                            int numEntries = 10 * capacity;  // 10 times more than cache capacity

                            for (int i=0; i<=numEntries; i++ ) {

                                Resource rs = new Resource("mortal_" + i, "value_" + i);

                                cache.put(rs.getID(), rs.getValue(), MORTAL_LIFESPAN, MORTAL_UNIT, MORTAL_LIFESPAN, MORTAL_UNIT);

                            }

                     

                            // 4. Verify that immortal resources are in the cache

                            expected_rs1 = cache.get(rs1.getID());

                            expected_rs2 = cache.get(rs2.getID());

                            assertNotNull("Resource 1 should be preserved", expected_rs1);

                            assertNotNull("Resource 2 should be preserved", expected_rs2);

                        }   

                     

                        /**

                         *

                         * Inner classes

                         *

                         */

                     

                        private static class Resource {

                            private final String id;

                            private final String value;

                     

                            public Resource(String id, String value) {

                                this.id = id;

                                this.value = value;

                            }

                     

                            public String getID() {

                                return id;

                            }

                     

                            public String getValue() {

                                return value;

                            }

                     

                            @Override

                            public int hashCode() {

                                final int prime = 31;

                                int result = 1;

                                result = prime * result + ((id == null) ? 0 : id.hashCode());

                                return result;

                            }

                     

                            @Override

                            public boolean equals(Object obj) {

                                if (this == obj)

                                    return true;

                                if (obj == null)

                                    return false;

                                if (getClass() != obj.getClass())

                                    return false;

                                Resource other = (Resource) obj;

                                if (id == null) {

                                    if (other.id != null)

                                        return false;

                                } else if (!id.equals(other.id))

                                    return false;

                                return true;

                            }

                        }

                     

                        @Listener

                        public static class EntryChangeListener  {

                     

                            @CacheEntriesEvicted

                            public void observeEvicted(CacheEntriesEvictedEvent<String, String> event) {

                                final String methodName = "observeEvicted";

                                if (!event.isPre()) {

                                    Map<String, String> entries = event.getEntries();

                     

                                    Cache<String, String> cache = event.getCache();

                                    for (Entry<String, String> entry : entries.entrySet()) {

                                        debug(methodName, String.format("  next entry: key=%s value=%s", entry.getKey(), entry.getValue()));

                                    }

                                }

                            }

                        }

                     

                        public static void main(String[] args) {

                            (new TestRunner()).doRun(new TestSuite(KeepForeverTest.class));

                        }

                    }

                    • 7. Re: How to exclude some objects from eviction ?
                      galder.zamarreno

                      Thanks for attaching the test. Just to verify, do you see a failure saying: 'java.lang.AssertionError: Resource 1 should be preserved" ? That's after "// 4. Verify that immortal resources are in the cache"

                      • 8. Re: How to exclude some objects from eviction ?
                        galder.zamarreno

                        The reason the test fails is due to what I said before:

                        Galder Zamarreño wrote:

                         

                        Eviction is another thing, is about maintaining the cache under certain size. This cannot be overriden. Even if data must live forever, if the capacity is exceeded, data will be deleted according to the algorithm chosen, LRU/LIRS. This is irrespective of the lifespan of the data... see https://docs.jboss.org/author/x/MwY5 for more info

                        Even if the data is immortal in theory, if the max capacitiy is exceeded, it might go according to the eviction policy. If you really want an immortal cache, you'll need to disable eviction.

                        • 9. Re: How to exclude some objects from eviction ?
                          dudalov

                          Thanks for the confirmation that what I was looking is not a supported option. I was confused by your first response "It's easy, you can set default eviction/expiration settings in the config ....".

                           

                          Please let me know if you remember another way to achieve the same result (comment #4 above) within a single cache. Otherwise I'll use two different caches for immortal and mortal objects of the same type. That would double objects retrieval, since I would need to check both caches, but probably it could be optimized somehow. Thanks again.

                           

                          Dmitry