• Bug
  • Status: Resolved
  • 2 Major
  • Resolution: Fixed
  • ehcache-core
  • ljacomet
  • Reporter: ianslai
  • May 01, 2013
  • 0
  • Watchers: 8
  • December 23, 2013
  • November 22, 2013

Description

If I create an Element, call {{setTimeToLive(0)}}, and put it in a cache, it seems to expire immediately and is not retrievable in a subsequent {{get()}}.

The motivation for this is that I am going through some legacy code that calls {{Element.setTimeToLive()}} for every single element put in the cache, instead of relying on the configuration set in {{ehcache.xml}}. One of the options in our code base is to set the element to have an infinite TTL. My current workaround for this is to check to see if the TTL is zero, and if so call {{setEternal(true)}}, but that ignores TTI.

I’m not sure if I’m reading the documentation right, but I thought a value of 0 meant the element should never expire; if that is incorrect then I guess the javadoc for Element should be updated.

Example test that illustrates failure:

package testfailure;

import static org.junit.Assert.*;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.PersistenceConfiguration;
import net.sf.ehcache.config.PersistenceConfiguration.Strategy;

import org.junit.Test;

public class TimeToLiveTest {

    @Test public void testTimeToLive() {
        CacheManager cacheManager = new CacheManager();

        Cache cache = new Cache(new CacheConfiguration()
                                 .name("mycache")
                                 .maxEntriesLocalHeap(100)
                                 .eternal(false)
                                 .timeToLiveSeconds(0)
                                 .timeToIdleSeconds(0)
                                 .persistence(new PersistenceConfiguration().strategy(Strategy.NONE)));

        cacheManager.addCache(cache);

        Element e = new Element("somekey", "value");
        e.setTimeToLive(0);

        cache.put(e);
        assertNotNull(cache.get("somekey"));
        assertEquals("value", cache.get("somekey").getObjectValue());
    }
}

From my version.properties: {noformat} product-name = Ehcache Core version = 2.6.5 built-by = cruise build-hostname = kong.terracotta.lan build-jdk = 1.6.0_27 build-time = 20130222-1215 build-revision = 7111 enterprise = false {noformat}

Thank you very much.

Comments

Yi Zhang 2013-05-01

I confirmed his test case. It works if you set TTL to anything non-zero (as expected) And also works if you additionally set TTI to 0.

I believe the problem might lie in a difference between explicitly setting to 0 vs inheriting Int.MIN_VALUE from the cache, and setting TTL explicitly while inheriting TTI in Element.java {noformat} /** * @return true if the element is eternal */ public boolean isEternal() { return (0 == timeToIdle) && (0 == timeToLive); }

/**
 * Sets whether the element is eternal.
 *
 * @param eternal
 */
public void setEternal(final boolean eternal) {
    if (eternal) {
        this.cacheDefaultLifespan = false;
        this.timeToIdle = 0;
        this.timeToLive = 0;
    } else if (isEternal()) {
        this.cacheDefaultLifespan = false;
        this.timeToIdle = Integer.MIN_VALUE;
        this.timeToLive = Integer.MIN_VALUE;
    }
}

/**
 * Whether any combination of eternal, TTL or TTI has been set.
 *
 * @return true if set.
 */
public boolean isLifespanSet() {
    return this.timeToIdle != Integer.MIN_VALUE || this.timeToLive != Integer.MIN_VALUE;
} \{noformat\}

Thus: e.setTimeToLive(0); // isEternal() = false e.setTimeToIdle(0); // now isEternal() = true

note getTimeToLive/Idle returns 0 in any case; we check for Integer.MIN_VALUE and return 0

Yi Zhang 2013-05-01

Hi Ian thanks for the report and the test case. I’ve reproduced your issue in our environment. We will try to resolve this as soon as possible. In the meantime, if you require a workaround, please also explicitly set the TTI in addition to the TTL; e.g.

{noformat} e.setTimeToLive(0); e.setTimeToIdle(0); {noformat}

Ian Lai 2013-05-02

Thanks for the prompt response and the workaround suggestion! I will implement that in our code.

Shelley Baker 2013-09-16

{{e.setTimeToIdle(0)}} has the same problem. Will this also be addressed as part of this JIRA?

James House 2013-09-16

Please evaluate

Alexander Snaps 2013-09-17

Hey Ian, can you please elaborate on why you think it’s a bug and what documentation you are referring to, that seems to imply 0 is eternal (or infinite). I do know that CacheConfiguration has something along those lines (http://ehcache.org/documentation/user-guide/configuration#dynamically-changing-cache-configuration), and while I agree that having the two out of sync is definitively counter intuitive, I’m not sure there is a bug really… Beyond that, this would be a pretty big change in behaviour, that would have people relying on the current behaviour consider a bug… even if it’d fix your issue. As I see it, if you want the per element to have that semantic, you should call into Element.setEternal or, since it does translate to that eventually, have both (0 == timeToIdle) && (0 == timeToLive)…

Ian Lai 2013-09-17

Hi Alex,

Part of the problem was that the guidance on setting time-to-live is spotty in the API, so I was relying on what documentation is available, and on existing behavior to deduce what should happen when {{Element.setTimeToLive(0)}} is called.

The existing behavior seems to be that 0 is equivalent to forever for time-to-live and time-to-idle in either {{ehcache.xml}} or runtime configuration. That matches the User Guide you referenced:

http://ehcache.org/documentation/user-guide/configuration#dynamically-changing-cache-configuration

The documentation for CacheConfiguration says “0 means do not check for idling”, which implies that 0 means forever. However, the documentation for Element does not mention what would happen if it is set to 0:

http://ehcache.org/apidocs/net/sf/ehcache/config/CacheConfiguration.html#timeToLiveSeconds http://ehcache.org/apidocs/net/sf/ehcache/Element.html#setTimeToIdle(int)

I understand that we should not break behavior for existing users, but I would disagree that this is a behavior users would rely on, since having items disappear from the cache immediately is not very useful at all, unless you want to disable the cache, in which case it would not make sense to call {{Element.setTimeToLive()}} for every element.

Also, the same counter-intuitive behavior also occurs when you call {{Element.setTimeToIdle(0)}}; this would mean if someone wants to make sure a particular element gets cleared out past a certain lifetime in the cache, regardless of what its configured idle time is, then the element always disappears as soon as it’s added to the cache.

Nevertheless, if you think it would be disruptive to fix this inconsistency in behavior, I would suggest updating the API and/or adding documentation describing the expected input values and resultant behavior for Element setters, since this might very well trip up some other unsuspecting developer down the road.

Thanks, Ian

Alexander Snaps 2013-09-18

Ian, I totally agree with you on latter part there, we should update the documentation, including javadoc and have it clearly mention that both needs to be set to 0 when wanting to set an element as eternal using these methods. Now, I actually also agree with you on the former part, I don’t see why anyone would rely on the current behaviour, other than maybe disabling the cache on a ‘per element’ basis. Why put to the cache at all though then ? Other than waisting cpu cycle and potentially even evicting something that would have more value than what you are currently putting anyways… Yet, experience has shown, some people just rely on the corner case behaviours more often than one would think… Anyways, we’ll make sure the doc get better and more complete! Thanks for your input. Alex

Louis Jacomet Jacomet 2013-10-17

Linked to API jira created to improve Element javadoc

Louis Jacomet Jacomet 2013-11-22

Improved javadoc for setTimeToLive(int) and setTimeToIdle(int)