• Bug
  • Status: Closed
  • 2 Major
  • Resolution: Fixed
  • ehcache-core
  • cdennis
  • Reporter: mads1980
  • May 26, 2010
  • 0
  • Watchers: 1
  • July 15, 2010
  • June 17, 2010

Description

The current code assumes that iterator.next() will always return a DiskElement, however this is not guaranteed if the diskElements ConcurrentHashMap abruptly reduces its size (for instance, because of a high number of concurrent remove() operations, or clear()). As per CHM’s docs, the iterator may or may not reflect changes to the underlying map after it has been obtained.

Apparently this would not happen with previous DiskStore incarnations since most operations where synchronized with instance-wide locks. However, recent concurrency performance improvements now allow get() concurrent with remove() as well as expiration / eviction via background thread.

This border case is assumed to be relatively rare (at least I can’t find any bug reports related to NoSuchElementException), however it doesn’t mean that it cannot happen.

The proposed solution is to truncate the sample array up to the last element obtained by a successful iterator.next()

    private DiskElement[] sampleElements(Map map) {
        int[] offsets = AbstractPolicy.generateRandomSample(map.size());
        DiskElement[] elements = new DiskElement[offsets.length];
        Iterator iterator = map.values().iterator();
        for (int i = 0; i < offsets.length; i++) {
            try {
                for (int j = 0; j < offsets[i]; j++) {
                    iterator.next();
                }
                elements[i] = (DiskElement) iterator.next();
            } catch (NoSuchElementException e) {
                return Arrays.copyOfRange(elements, 0, i);
            }
        }
        return elements;
    }

Comments

Fiona OShea 2010-06-01

Is there anything that we need to do here?