Adrian Crum wrote:
> Adam Heath wrote:
>> Ok, this is a cute bug.
>>
>> Under the hood, UtilCache makes use of java.util.LinkedHashMap when
>> maxMemSize is set.  It configures LinkedHashMap to act like a LRU map;
>> every access reorders the internal linked list.  Access here is
>> defined as a call to get(key).
>>
>> The problem is that to handle element expiration, UtilCache must call
>> get(key) on LinkedHashMap.  This causes the key to be accessed, which
>> pushes it to the front of the linked list, and breaks the LRU contract.
>>
>> The way to fix this is to switch UtilCache away from a polling
>> mechanism for expiration.  Instead, a DelayQueue can be used, where
>> every single CacheLine is registered into a single, static DelayQueue,
>> and a thread is started that is always polling from that.  When an
>> item is returned from the poll, it removes it from it's containing cache.
>>
>> This is the only way I see to get UtilCache make to being a proper LRU
>> system.
> 
> I think it would help if the original cache design decisions were
> available so we can understand why things were set up that way.
> 
> The first question that comes to my mind is: Why does the cache need to
> be polled? It seems to me the max memory size would need to be checked
> only when a new item is about to be placed in the cache.

You misunderstand the problem.

Having a LRU map that auto-removes things as the map gets too large is
fine.  This part works.

What breaks is that the a LRU map's algorithm is only effective when a
get is a get, and a put is a put.  However, UtilCache does a get while
doing a put.  This causes the LRU map to consider this get an access,
and the item moves to the beginning of the internal list.

If I do UtilCache.put(key, value), I don't expect the LRU to change
the order of eviction.  It should only change during a get(key) call.

I discovered this problem while trying to write test cases.

Reply via email to