From 1a0ccfb7c39af927de369cff689fcba112227f95 Mon Sep 17 00:00:00 2001 From: Chris Dennis Date: Wed, 6 Jul 2016 20:14:13 -0400 Subject: [PATCH] Fixes #1291 : Ensure single element chains are moved correctly during compaction --- .../server/offheap/OffHeapChainMap.java | 8 +++-- .../offheap/OffHeapChainStorageEngine.java | 4 +++ .../offheap/OffHeapServerStoreTest.java | 32 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java b/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java index 1101c147e1..2b5d1bce26 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java +++ b/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainMap.java @@ -53,8 +53,12 @@ public OffHeapChainMap(PageSource source, Portability keyPortability, public void evicting(Callable> callable) { try { Map.Entry entry = callable.call(); - if (evictionListener != null) { - evictionListener.onEviction(entry.getKey()); + try { + if (evictionListener != null) { + evictionListener.onEviction(entry.getKey()); + } + } finally { + entry.getValue().close(); } } catch (Exception e) { throw new AssertionError(e); diff --git a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java b/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java index e9b3c1c998..405f172023 100644 --- a/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java +++ b/clustered/server/src/main/java/org/ehcache/clustered/server/offheap/OffHeapChainStorageEngine.java @@ -538,6 +538,10 @@ public boolean moved(long from, long to) { return false; } else { long tail = storage.readLong(to + CHAIN_HEADER_TAIL_OFFSET); + if (tail == from + CHAIN_HEADER_SIZE) { + tail = to + CHAIN_HEADER_SIZE; + storage.writeLong(to + CHAIN_HEADER_TAIL_OFFSET, tail); + } storage.writeLong(tail + ELEMENT_HEADER_NEXT_OFFSET, to); for (AttachedInternalChain activeChain : activeChains) { activeChain.moved(from, to); diff --git a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java b/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java index 75ec72d83f..a9660438e5 100644 --- a/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java +++ b/clustered/server/src/test/java/org/ehcache/clustered/server/offheap/OffHeapServerStoreTest.java @@ -16,6 +16,7 @@ package org.ehcache.clustered.server.offheap; import java.nio.ByteBuffer; +import java.util.Random; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; @@ -30,6 +31,7 @@ import org.terracotta.offheapstore.buffersource.OffHeapBufferSource; import org.terracotta.offheapstore.exceptions.OversizeMappingException; import org.terracotta.offheapstore.paging.UnlimitedPageSource; +import org.terracotta.offheapstore.paging.UpfrontAllocatingPageSource; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -39,6 +41,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.terracotta.offheapstore.util.MemoryUnit.MEGABYTES; public class OffHeapServerStoreTest extends ServerStoreTest { @@ -161,4 +164,33 @@ public Object answer(InvocationOnMock invocation) throws Throwable { assertThat(payload.remaining(), is(8)); } + @Test + public void testCrossSegmentShrinking() { + long seed = System.nanoTime(); + Random random = new Random(seed); + try { + OffHeapServerStore store = new OffHeapServerStore(new UpfrontAllocatingPageSource(new OffHeapBufferSource(), MEGABYTES.toBytes(1L), MEGABYTES.toBytes(1)), 16); + + ByteBuffer smallValue = ByteBuffer.allocate(1024); + for (int i = 0; i < 10000; i++) { + try { + store.getAndAppend(random.nextInt(500), smallValue.duplicate()); + } catch (OversizeMappingException e) { + //ignore + } + } + + ByteBuffer largeValue = ByteBuffer.allocate(100 * 1024); + for (int i = 0; i < 10000; i++) { + try { + store.getAndAppend(random.nextInt(500), largeValue.duplicate()); + } catch (OversizeMappingException e) { + //ignore + } + } + } catch (Throwable t) { + throw (AssertionError) new AssertionError("Failed with seed " + seed).initCause(t); + } + } + }