| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.internal.cache; |
| |
| import static org.assertj.core.api.Assertions.assertThat; |
| import static org.assertj.core.api.Assertions.assertThatThrownBy; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.RETURNS_DEEP_STUBS; |
| import static org.mockito.Mockito.doThrow; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.junit.After; |
| import org.junit.Test; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.stubbing.Answer; |
| |
| import org.apache.geode.cache.DataPolicy; |
| import org.apache.geode.cache.EntryNotFoundException; |
| import org.apache.geode.cache.EvictionAttributes; |
| import org.apache.geode.cache.Operation; |
| import org.apache.geode.cache.Scope; |
| import org.apache.geode.cache.TransactionId; |
| import org.apache.geode.cache.client.internal.ServerRegionProxy; |
| import org.apache.geode.distributed.internal.InternalDistributedSystem; |
| import org.apache.geode.distributed.internal.membership.InternalDistributedMember; |
| import org.apache.geode.internal.cache.entries.DiskEntry.RecoveredEntry; |
| import org.apache.geode.internal.cache.eviction.EvictableEntry; |
| import org.apache.geode.internal.cache.eviction.EvictionController; |
| import org.apache.geode.internal.cache.eviction.EvictionCounters; |
| import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID; |
| import org.apache.geode.internal.cache.versions.ConcurrentCacheModificationException; |
| import org.apache.geode.internal.cache.versions.RegionVersionVector; |
| import org.apache.geode.internal.cache.versions.VersionHolder; |
| import org.apache.geode.internal.cache.versions.VersionSource; |
| import org.apache.geode.internal.cache.versions.VersionStamp; |
| import org.apache.geode.internal.cache.versions.VersionTag; |
| import org.apache.geode.internal.serialization.KnownVersion; |
| import org.apache.geode.internal.util.concurrent.ConcurrentMapWithReusableEntries; |
| import org.apache.geode.internal.util.concurrent.CustomEntryConcurrentHashMap; |
| |
| public class AbstractRegionMapTest { |
| |
| private static final Object KEY = "key"; |
| private static final EntryEventImpl UPDATEEVENT = mock(EntryEventImpl.class); |
| |
| @After |
| public void tearDown() { |
| AbstractRegionMap.FORCE_INVALIDATE_EVENT = false; |
| } |
| |
| @Test |
| public void shouldBeMockable() throws Exception { |
| AbstractRegionMap mockAbstractRegionMap = mock(AbstractRegionMap.class); |
| RegionEntry mockRegionEntry = mock(RegionEntry.class); |
| VersionHolder mockVersionHolder = mock(VersionHolder.class); |
| |
| when(mockAbstractRegionMap.removeTombstone(eq(mockRegionEntry), eq(mockVersionHolder), |
| anyBoolean(), anyBoolean())).thenReturn(true); |
| |
| assertThat( |
| mockAbstractRegionMap.removeTombstone(mockRegionEntry, mockVersionHolder, true, true)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void invalidateOfNonExistentRegionThrowsEntryNotFound() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| when(arm._getOwner().isInitialized()).thenReturn(true); |
| |
| assertThatThrownBy(() -> arm.invalidate(event, true, false, false)) |
| .isInstanceOf(EntryNotFoundException.class); |
| verify(arm._getOwner(), never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).invokeInvalidateCallbacks(any(), any(), anyBoolean()); |
| } |
| |
| @Test |
| public void invalidateOfNonExistentRegionThrowsEntryNotFoundWithForce() { |
| AbstractRegionMap.FORCE_INVALIDATE_EVENT = true; |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| when(arm._getOwner().isInitialized()).thenReturn(true); |
| |
| assertThatThrownBy(() -> arm.invalidate(event, true, false, false)) |
| .isInstanceOf(EntryNotFoundException.class); |
| verify(arm._getOwner(), never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), times(1)).invokeInvalidateCallbacks(any(), any(), anyBoolean()); |
| } |
| |
| @Test |
| public void invalidateOfAlreadyInvalidEntryReturnsFalse() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| |
| // invalidate on region that is not initialized should create |
| // entry in map as invalid. |
| when(arm._getOwner().isInitialized()).thenReturn(false); |
| assertThatThrownBy(() -> arm.invalidate(event, true, false, false)) |
| .isInstanceOf(EntryNotFoundException.class); |
| |
| when(arm._getOwner().isInitialized()).thenReturn(true); |
| assertFalse(arm.invalidate(event, true, false, false)); |
| verify(arm._getOwner(), never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).invokeInvalidateCallbacks(any(), any(), anyBoolean()); |
| } |
| |
| @Test |
| public void invalidateOfAlreadyInvalidEntryReturnsFalseWithForce() { |
| AbstractRegionMap.FORCE_INVALIDATE_EVENT = true; |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| |
| // invalidate on region that is not initialized should create |
| // entry in map as invalid. |
| when(arm._getOwner().isInitialized()).thenReturn(false); |
| assertThatThrownBy(() -> arm.invalidate(event, true, false, false)) |
| .isInstanceOf(EntryNotFoundException.class); |
| |
| when(arm._getOwner().isInitialized()).thenReturn(true); |
| assertFalse(arm.invalidate(event, true, false, false)); |
| verify(arm._getOwner(), never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), times(1)).invokeInvalidateCallbacks(any(), any(), anyBoolean()); |
| } |
| |
| @Test |
| public void invalidateForceNewEntryOfAlreadyInvalidEntryReturnsFalse() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| |
| // invalidate on region that is not initialized should create |
| // entry in map as invalid. |
| assertTrue(arm.invalidate(event, true, true, false)); |
| verify(arm._getOwner(), times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), |
| anyBoolean()); |
| |
| when(arm._getOwner().isInitialized()).thenReturn(true); |
| assertFalse(arm.invalidate(event, true, true, false)); |
| verify(arm._getOwner(), times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), |
| anyBoolean()); |
| verify(arm._getOwner(), never()).invokeInvalidateCallbacks(any(), any(), anyBoolean()); |
| } |
| |
| @Test |
| public void invalidateForceNewEntryOfAlreadyInvalidEntryReturnsFalseWithForce() { |
| AbstractRegionMap.FORCE_INVALIDATE_EVENT = true; |
| try { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| |
| // invalidate on region that is not initialized should create |
| // entry in map as invalid. |
| when(arm._getOwner().isInitialized()).thenReturn(false); |
| assertTrue(arm.invalidate(event, true, true, false)); |
| verify(arm._getOwner(), times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), |
| anyBoolean()); |
| verify(arm._getOwner(), never()).invokeInvalidateCallbacks(any(), any(), anyBoolean()); |
| |
| when(arm._getOwner().isInitialized()).thenReturn(true); |
| assertFalse(arm.invalidate(event, true, true, false)); |
| verify(arm._getOwner(), times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), |
| anyBoolean()); |
| verify(arm._getOwner(), times(1)).invokeInvalidateCallbacks(any(), any(), anyBoolean()); |
| } finally { |
| AbstractRegionMap.FORCE_INVALIDATE_EVENT = false; |
| } |
| } |
| |
| @Test |
| public void destroyWithEmptyRegionThrowsException() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| assertThatThrownBy(() -> arm.destroy(event, false, false, false, false, null, false)) |
| .isInstanceOf(EntryNotFoundException.class); |
| } |
| |
| @Test |
| public void destroyWithEmptyRegionInTokenModeAddsAToken() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| assertThat(re.getValueAsToken()).isEqualTo(Token.DESTROYED); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyWithEmptyRegionInTokenModeWithRegionClearedExceptionDoesDestroy() |
| throws Exception { |
| CustomEntryConcurrentHashMap<String, EvictableEntry> map = |
| mock(CustomEntryConcurrentHashMap.class); |
| EvictableEntry entry = mock(EvictableEntry.class); |
| when(entry.destroy(any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean(), anyBoolean())) |
| .thenThrow(RegionClearedException.class); |
| when(map.get(KEY)).thenReturn(null); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| when(factory.createEntry(any(), any(), any())).thenReturn(entry); |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), eq(true), |
| eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void evictDestroyWithEmptyRegionInTokenModeDoesNothing() { |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| final boolean evict = true; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isFalse(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isFalse(); |
| verify(arm._getOwner(), never()).basicDestroyPart2(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).basicDestroyPart3(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any()); |
| } |
| |
| @Test |
| public void evictDestroyWithExistingTombstoneInTokenModeChangesToDestroyToken() { |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true); |
| addEntry(arm, Token.TOMBSTONE); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| final boolean evict = true; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| assertThat(re.getValueAsToken()).isEqualTo(Token.DESTROYED); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void evictDestroyWithExistingTombstoneInUseByTransactionInTokenModeDoesNothing() |
| throws RegionClearedException { |
| CustomEntryConcurrentHashMap<String, EvictableEntry> map = |
| mock(CustomEntryConcurrentHashMap.class); |
| EvictableEntry entry = mock(EvictableEntry.class); |
| when(entry.isInUseByTransaction()).thenReturn(true); |
| when(entry.getValue()).thenReturn(Token.TOMBSTONE); |
| when(map.get(KEY)).thenReturn(entry); |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true, map); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| final boolean evict = true; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isFalse(); |
| verify(entry, never()).destroy(any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean(), |
| anyBoolean()); |
| verify(arm._getOwner(), never()).basicDestroyPart2(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).basicDestroyPart3(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any()); |
| } |
| |
| @Test |
| public void evictDestroyWithConcurrentChangeFromNullToInUseByTransactionInTokenModeDoesNothing() |
| throws RegionClearedException { |
| CustomEntryConcurrentHashMap<Object, EvictableEntry> map = |
| mock(CustomEntryConcurrentHashMap.class); |
| EvictableEntry entry = mock(EvictableEntry.class); |
| when(entry.isInUseByTransaction()).thenReturn(true); |
| when(map.get(KEY)).thenReturn(null); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry); |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true, map); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| final boolean evict = true; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isFalse(); |
| verify(entry, never()).destroy(any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean(), |
| anyBoolean()); |
| verify(arm._getOwner(), never()).basicDestroyPart2(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).basicDestroyPart3(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any()); |
| } |
| |
| @Test |
| public void destroyWithConcurrentChangeFromNullToValidRetriesAndDoesDestroy() |
| throws RegionClearedException { |
| CustomEntryConcurrentHashMap<Object, EvictableEntry> map = |
| mock(CustomEntryConcurrentHashMap.class); |
| EvictableEntry entry = mock(EvictableEntry.class); |
| when(entry.getValue()).thenReturn("value"); |
| when(entry.destroy(any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| when(map.get(KEY)).thenReturn(null).thenReturn(entry); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry); |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true, map); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| final boolean evict = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isTrue(); |
| verify(entry, times(1)).destroy(eq(arm._getOwner()), eq(event), eq(false), anyBoolean(), |
| eq(expectedOldValue), anyBoolean(), anyBoolean()); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyInTokenModeWithConcurrentChangeFromNullToRemovePhase2RetriesAndDoesDestroy() |
| throws RegionClearedException { |
| CustomEntryConcurrentHashMap<Object, EvictableEntry> map = |
| mock(CustomEntryConcurrentHashMap.class); |
| EvictableEntry entry = mock(EvictableEntry.class); |
| when(entry.isRemovedPhase2()).thenReturn(true); |
| when(entry.destroy(any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| when(map.get(KEY)).thenReturn(null); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry).thenReturn(null); |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true, map); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| final boolean evict = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isTrue(); |
| verify(map).remove(eq(KEY), eq(entry)); |
| verify(map, times(2)).putIfAbsent(eq(KEY), any()); |
| verify(entry, never()).destroy(eq(arm._getOwner()), eq(event), eq(false), anyBoolean(), |
| eq(expectedOldValue), anyBoolean(), anyBoolean()); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyWithConcurrentChangeFromTombstoneToValidRetriesAndDoesDestroy() |
| throws RegionClearedException { |
| CustomEntryConcurrentHashMap<Object, EvictableEntry> map = |
| mock(CustomEntryConcurrentHashMap.class); |
| EvictableEntry entry = mock(EvictableEntry.class); |
| when(entry.getValue()).thenReturn("value"); |
| when(entry.isTombstone()).thenReturn(true).thenReturn(false); |
| when(entry.destroy(any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| when(map.get(KEY)).thenReturn(entry); |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true, map); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| final boolean evict = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isTrue(); |
| verify(entry, times(1)).destroy(eq(arm._getOwner()), eq(event), eq(false), anyBoolean(), |
| eq(expectedOldValue), anyBoolean(), anyBoolean()); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyOfExistingEntryInTokenModeAddsAToken() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| addEntry(arm); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| assertThat(re.getValueAsToken()).isEqualTo(Token.DESTROYED); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyOfExistingTombstoneInTokenModeWithConcurrencyChecksDoesNothing() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| RegionVersionVector<?> versionVector = mock(RegionVersionVector.class); |
| when(arm._getOwner().getVersionVector()).thenReturn(versionVector); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| VersionTag<?> versionTag = mock(VersionTag.class); |
| when(versionTag.hasValidVersion()).thenReturn(true); |
| event.setVersionTag(versionTag); |
| addEntry(arm, Token.TOMBSTONE); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = true; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| // why not DESTROY token? |
| assertThat(re.getValueAsToken()).isEqualTo(Token.TOMBSTONE); |
| // since it was already destroyed why do we do the parts? |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyOfExistingTombstoneWithConcurrencyChecksThrowsEntryNotFound() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| RegionVersionVector<?> versionVector = mock(RegionVersionVector.class); |
| when(arm._getOwner().getVersionVector()).thenReturn(versionVector); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| VersionTag<?> versionTag = mock(VersionTag.class); |
| when(versionTag.hasValidVersion()).thenReturn(true); |
| event.setVersionTag(versionTag); |
| addEntry(arm, Token.TOMBSTONE); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| assertThatThrownBy( |
| () -> arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isInstanceOf(EntryNotFoundException.class); |
| } |
| |
| @Test |
| public void destroyOfExistingTombstoneWithConcurrencyChecksAndRemoveRecoveredEntryDoesRemove() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| RegionVersionVector<?> versionVector = mock(RegionVersionVector.class); |
| when(arm._getOwner().getVersionVector()).thenReturn(versionVector); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| VersionTag<?> versionTag = mock(VersionTag.class); |
| when(versionTag.hasValidVersion()).thenReturn(true); |
| event.setVersionTag(versionTag); |
| addEntry(arm, Token.TOMBSTONE); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| final boolean removeRecoveredEntry = true; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, |
| removeRecoveredEntry)).isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isFalse(); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyOfExistingRemovePhase2WithConcurrencyChecksAndRemoveRecoveredEntryDoesRetryAndThrowsEntryNotFound() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| RegionVersionVector<?> versionVector = mock(RegionVersionVector.class); |
| when(arm._getOwner().getVersionVector()).thenReturn(versionVector); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| VersionTag<?> versionTag = mock(VersionTag.class); |
| when(versionTag.hasValidVersion()).thenReturn(true); |
| event.setVersionTag(versionTag); |
| addEntry(arm, Token.REMOVED_PHASE2); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| final boolean removeRecoveredEntry = true; |
| assertThatThrownBy(() -> arm.destroy(event, inTokenMode, duringRI, false, false, |
| expectedOldValue, removeRecoveredEntry)).isInstanceOf(EntryNotFoundException.class); |
| } |
| |
| @Test |
| public void destroyOfExistingEntryRemovesEntryFromMapAndDoesNotifications() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(); |
| addEntry(arm); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isFalse(); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyOfExistingEntryWithConcurrencyChecksAndNoVersionTagDestroysWithoutTombstone() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| addEntry(arm); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| // This might be a bug. It seems like we should have created a tombstone but we have no |
| // version tag so that might be the cause of this bug. |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isFalse(); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyOfExistingEntryWithConcurrencyChecksAddsTombstone() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| RegionVersionVector versionVector = mock(RegionVersionVector.class); |
| when(arm._getOwner().getVersionVector()).thenReturn(versionVector); |
| addEntry(arm); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.hasValidVersion()).thenReturn(true); |
| event.setVersionTag(versionTag); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| assertThat(re.getValueAsToken()).isEqualTo(Token.TOMBSTONE); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void evictDestroyOfExistingEntryWithConcurrencyChecksAddsTombstone() { |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true); |
| RegionVersionVector<?> versionVector = mock(RegionVersionVector.class); |
| when(arm._getOwner().getVersionVector()).thenReturn(versionVector); |
| addEntry(arm); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| VersionTag<?> versionTag = mock(VersionTag.class); |
| when(versionTag.hasValidVersion()).thenReturn(true); |
| event.setVersionTag(versionTag); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| final boolean evict = true; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, evict, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| assertThat(re.getValueAsToken()).isEqualTo(Token.TOMBSTONE); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyWithEmptyRegionWithConcurrencyChecksThrowsException() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| assertThatThrownBy(() -> arm.destroy(event, false, false, false, false, null, false)) |
| .isInstanceOf(EntryNotFoundException.class); |
| } |
| |
| @Test |
| public void evictDestroyWithEmptyRegionWithConcurrencyChecksDoesNothing() { |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(true); |
| EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| assertThat(arm.destroy(event, false, false, false, true, null, false)).isFalse(); |
| verify(arm._getOwner(), never()).basicDestroyPart2(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).basicDestroyPart3(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any()); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isFalse(); |
| } |
| |
| @Test |
| public void evictDestroyWithEmptyRegionDoesNothing() { |
| final TestableVMLRURegionMap arm = new TestableVMLRURegionMap(false); |
| EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| assertThat(arm.destroy(event, false, false, false, true, null, false)).isFalse(); |
| verify(arm._getOwner(), never()).basicDestroyPart2(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).basicDestroyPart3(any(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean(), any()); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isFalse(); |
| } |
| |
| @Test |
| public void destroyWithEmptyRegionWithConcurrencyChecksAddsATombstone() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| RegionVersionVector versionVector = mock(RegionVersionVector.class); |
| when(arm._getOwner().getVersionVector()).thenReturn(versionVector); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.hasValidVersion()).thenReturn(true); |
| event.setVersionTag(versionTag); |
| event.setOriginRemote(true); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| assertThat(re.getValueAsToken()).isEqualTo(Token.TOMBSTONE); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| } |
| |
| @Test |
| public void destroyWithEmptyRegionWithConcurrencyChecksAndNullVersionTagAddsATombstone() { |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true); |
| final EntryEventImpl event = createEventForDestroy(arm._getOwner()); |
| event.setOriginRemote(true); |
| final Object expectedOldValue = null; |
| final boolean inTokenMode = false; |
| final boolean duringRI = false; |
| assertThat(arm.destroy(event, inTokenMode, duringRI, false, false, expectedOldValue, false)) |
| .isTrue(); |
| assertThat(arm.getEntryMap().containsKey(event.getKey())).isTrue(); |
| boolean invokeCallbacks = true; |
| verify(arm._getOwner(), times(1)).basicDestroyPart2(any(), eq(event), eq(inTokenMode), |
| eq(false), eq(duringRI), eq(invokeCallbacks)); |
| verify(arm._getOwner(), times(1)).basicDestroyPart3(any(), eq(event), eq(inTokenMode), |
| eq(duringRI), eq(invokeCallbacks), eq(expectedOldValue)); |
| // instead of a TOMBSTONE we leave an entry whose value is REMOVE_PHASE1 |
| // this looks like a bug. It is caused by some code in: AbstractRegionEntry.destroy() |
| // that calls removePhase1 when the versionTag is null. |
| // It seems like this code path needs to tell the higher levels |
| // to call removeEntry |
| RegionEntry re = (RegionEntry) arm.getEntryMap().get(event.getKey()); |
| assertThat(re.getValueAsToken()).isEqualTo(Token.REMOVED_PHASE1); |
| } |
| |
| @Test |
| public void txApplyInvalidateDoesNotInvalidateRemovedToken() throws RegionClearedException { |
| TxTestableAbstractRegionMap arm = new TxTestableAbstractRegionMap(); |
| |
| Object newValue = "value"; |
| arm.txApplyPut(Operation.CREATE, KEY, newValue, false, |
| new TXId(mock(InternalDistributedMember.class), 1), mock(TXRmtEvent.class), |
| mock(EventID.class), null, new ArrayList<EntryEventImpl>(), null, null, null, null, 1); |
| RegionEntry re = arm.getEntry(KEY); |
| assertNotNull(re); |
| |
| Token[] removedTokens = |
| {Token.REMOVED_PHASE2, Token.REMOVED_PHASE1, Token.DESTROYED, Token.TOMBSTONE}; |
| |
| for (Token token : removedTokens) { |
| verifyTxApplyInvalidate(arm, KEY, re, token); |
| } |
| } |
| |
| @Test |
| public void updateRecoveredEntry_givenExistingDestroyedOrRemovedAndSettingToTombstone_neverCallsUpdateSizeOnRemove() { |
| RecoveredEntry recoveredEntry = mock(RecoveredEntry.class); |
| RegionEntry regionEntry = mock(RegionEntry.class); |
| when(regionEntry.isTombstone()).thenReturn(false).thenReturn(true); |
| when(regionEntry.isDestroyedOrRemoved()).thenReturn(true); |
| when(regionEntry.getVersionStamp()).thenReturn(mock(VersionStamp.class)); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, null, null, regionEntry); |
| |
| arm.updateRecoveredEntry(KEY, recoveredEntry); |
| |
| verify(arm._getOwner(), never()).updateSizeOnRemove(any(), anyInt()); |
| } |
| |
| @Test |
| public void updateRecoveredEntry_givenExistingRemovedNonTombstone_neverCallsUpdateSizeOnRemove() { |
| RecoveredEntry recoveredEntry = mock(RecoveredEntry.class); |
| RegionEntry regionEntry = mock(RegionEntry.class); |
| when(regionEntry.isRemoved()).thenReturn(true); |
| when(regionEntry.isTombstone()).thenReturn(false); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, null, null, regionEntry); |
| |
| arm.updateRecoveredEntry(KEY, recoveredEntry); |
| |
| verify(arm._getOwner(), never()).updateSizeOnRemove(any(), anyInt()); |
| } |
| |
| @Test |
| public void updateRecoveredEntry_givenNoExistingEntry_neverCallsUpdateSizeOnRemove() { |
| RecoveredEntry recoveredEntry = mock(RecoveredEntry.class); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, null, null, null); |
| |
| arm.updateRecoveredEntry(KEY, recoveredEntry); |
| |
| verify(arm._getOwner(), never()).updateSizeOnRemove(any(), anyInt()); |
| } |
| |
| @Test |
| public void updateRecoveredEntry_givenExistingNonTombstoneAndSettingToTombstone_callsUpdateSizeOnRemove() { |
| RecoveredEntry recoveredEntry = mock(RecoveredEntry.class); |
| RegionEntry regionEntry = mock(RegionEntry.class); |
| when(regionEntry.isTombstone()).thenReturn(false).thenReturn(true); |
| when(regionEntry.getVersionStamp()).thenReturn(mock(VersionStamp.class)); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, null, null, regionEntry); |
| |
| arm.updateRecoveredEntry(KEY, recoveredEntry); |
| |
| verify(arm._getOwner(), times(1)).updateSizeOnRemove(eq(KEY), anyInt()); |
| } |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturningDestroyedOrRemovedEntry_neverCallsUpdateSizeOnRemove() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isDestroyedOrRemoved()).thenReturn(true); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(versionStamp.asVersionTag()).thenReturn(mock(VersionTag.class)); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry).thenReturn(null); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, null); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| |
| verify(arm._getOwner(), never()).updateSizeOnRemove(any(), anyInt()); |
| } |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturningNonTombstone_callsUpdateSizeOnRemove() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(false); |
| when(entry.isDestroyedOrRemoved()).thenReturn(false); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(versionStamp.asVersionTag()).thenReturn(mock(VersionTag.class)); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry).thenReturn(null); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, null); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| |
| verify(arm._getOwner(), times(1)).updateSizeOnRemove(eq(KEY), anyInt()); |
| } |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturningRemoveTokenOnFirstTryWillTryUntilRegionEntryIsPut() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(false); |
| when(entry.isDestroyedOrRemoved()).thenReturn(false); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| RegionEntry removedTokenEntry = mock(RegionEntry.class); |
| when(removedTokenEntry.isRemovedPhase2()).thenReturn(true); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(versionStamp.asVersionTag()).thenReturn(mock(VersionTag.class)); |
| |
| Answer returnRemovedTokenAnswer = new Answer() { |
| private int putTimes = 0; |
| |
| @Override |
| public Object answer(InvocationOnMock invocation) throws Throwable { |
| if (putTimes++ == 0) { |
| return removedTokenEntry; |
| } |
| return entry; |
| } |
| }; |
| when(map.putIfAbsent(eq(KEY), any())).thenAnswer(returnRemovedTokenAnswer); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, null); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| |
| verify(map, times(2)).putIfAbsent(eq(KEY), any()); |
| } |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturningRegionEntryAndProcessVersionTagThrowsConcurrentCacheModificationException_createdEntryRemovedFromMapAndNotInitialImageInit() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(false); |
| when(entry.isDestroyedOrRemoved()).thenReturn(false); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(versionStamp.asVersionTag()).thenReturn(mock(VersionTag.class)); |
| doThrow(new ConcurrentCacheModificationException()).when(versionStamp).processVersionTag(any(), |
| any(), anyBoolean(), anyBoolean(), any(), any(), anyBoolean()); |
| RegionEntry createdEntry = mock(RegionEntry.class); |
| when(createdEntry.getVersionStamp()).thenReturn(versionStamp); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| when(factory.createEntry(any(), any(), any())).thenReturn(createdEntry); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| |
| verify(map, times(1)).remove(eq(KEY), eq(createdEntry)); |
| verify(entry, never()).initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean()); |
| } |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturningRegionEntryAndSameTombstoneWillAttemptToRemoveREAndInvokeNothingElse() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(false); |
| when(entry.isDestroyedOrRemoved()).thenReturn(false); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(entry.isTombstone()).thenReturn(true); |
| TestableVersionTag versionTag = new TestableVersionTag(); |
| versionTag.setVersionSource(mock(VersionSource.class)); |
| when(versionStamp.asVersionTag()).thenReturn(versionTag); |
| RegionEntry createdEntry = mock(RegionEntry.class); |
| when(createdEntry.getVersionStamp()).thenReturn(versionStamp); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| when(factory.createEntry(any(), any(), any())).thenReturn(createdEntry); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(map.putIfAbsent(eq(KEY), eq(createdEntry))).thenReturn(entry); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| |
| verify(map, times(1)).remove(eq(KEY), eq(createdEntry)); |
| verify(entry, never()).initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean()); |
| } |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturningRegionEntryOldIsTombstone_callUnscheduleTombstone() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(true); |
| when(entry.isDestroyedOrRemoved()).thenReturn(false); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(versionStamp.asVersionTag()).thenReturn(mock(VersionTag.class)); |
| RegionEntry createdEntry = mock(RegionEntry.class); |
| when(createdEntry.getVersionStamp()).thenReturn(versionStamp); |
| when(createdEntry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| when(factory.createEntry(any(), any(), any())).thenReturn(createdEntry); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| |
| verify(arm._getOwner(), times(1)).unscheduleTombstone(entry); |
| } |
| |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturnsNullAndProcessVersionTagThrowsConcurrentCacheModificationException_createdEntryRemovedFromMapAndNotInitialImageInit() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(null); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| VersionStamp mockVersionStamp = mock(VersionStamp.class); |
| doThrow(new ConcurrentCacheModificationException()).when(mockVersionStamp) |
| .processVersionTag(any(), any(), anyBoolean(), anyBoolean(), any(), any(), anyBoolean()); |
| RegionEntry createdEntry = mock(RegionEntry.class); |
| when(createdEntry.getVersionStamp()).thenReturn(mockVersionStamp); |
| when(factory.createEntry(any(), any(), any())).thenReturn(createdEntry); |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| verify(map, times(1)).remove(eq(KEY), eq(createdEntry)); |
| verify(createdEntry, never()).initialImageInit(any(), anyLong(), any(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| } |
| |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturnsNullAndValueIsTombstone_callToScheduleTombstone() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(null); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| VersionStamp mockVersionStamp = mock(VersionStamp.class); |
| RegionEntry createdEntry = mock(RegionEntry.class); |
| when(createdEntry.getVersionStamp()).thenReturn(mockVersionStamp); |
| when(createdEntry.initialImageInit(any(), anyLong(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean())).thenReturn(true); |
| when(factory.createEntry(any(), any(), any())).thenReturn(createdEntry); |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, Token.TOMBSTONE, false, false, versionTag, null, false); |
| verify(arm._getOwner(), times(1)).scheduleTombstone(any(), any()); |
| } |
| |
| |
| @Test |
| public void initialImagePut_givenPutIfAbsentReturnsNullAndValueIsNotTombstone_callUpdateSizeOnCreate() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(null); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| VersionStamp mockVersionStamp = mock(VersionStamp.class); |
| RegionEntry createdEntry = mock(RegionEntry.class); |
| when(createdEntry.getVersionStamp()).thenReturn(mockVersionStamp); |
| when(createdEntry.initialImageInit(any(), anyLong(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean())).thenReturn(true); |
| when(factory.createEntry(any(), any(), any())).thenReturn(createdEntry); |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| arm.initialImagePut(KEY, 0, "", false, false, versionTag, null, false); |
| verify(arm._getOwner(), times(1)).updateSizeOnCreate(any(), anyInt()); |
| } |
| |
| |
| @Test |
| public void initialImagePut_ExceptionThrownWhenCreatingNewRegionEntry_removeDoesNotGetCalled() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(null); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| VersionStamp mockVersionStamp = mock(VersionStamp.class); |
| RegionEntry createdEntry = mock(RegionEntry.class); |
| when(createdEntry.getVersionStamp()).thenReturn(mockVersionStamp); |
| when(createdEntry.initialImageInit(any(), anyLong(), any(), anyBoolean(), anyBoolean(), |
| anyBoolean())).thenReturn(true); |
| when(factory.createEntry(any(), any(), any())).thenThrow(new RuntimeException()); |
| final TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| when(arm._getOwner().getServerProxy()).thenReturn(mock(ServerRegionProxy.class)); |
| VersionTag versionTag = mock(VersionTag.class); |
| when(versionTag.getMemberID()).thenReturn(mock(VersionSource.class)); |
| |
| try { |
| arm.initialImagePut(KEY, 0, "", false, false, versionTag, null, false); |
| } catch (RuntimeException e) { |
| // expected to be thrown, we set up the test this way |
| } |
| verify(map, never()).remove(eq(KEY), eq(createdEntry)); |
| } |
| |
| |
| @Test |
| public void txApplyDestroy_givenExistingDestroyedOrRemovedEntry_neverCallsUpdateSizeOnRemove() { |
| RegionEntry regionEntry = mock(RegionEntry.class); |
| when(regionEntry.isTombstone()).thenReturn(false); |
| when(regionEntry.isDestroyedOrRemoved()).thenReturn(true); |
| when(regionEntry.getVersionStamp()).thenReturn(mock(VersionStamp.class)); |
| TXId txId = mock(TXId.class); |
| when(txId.getMemberId()).thenReturn(mock(InternalDistributedMember.class)); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, null, null, regionEntry); |
| |
| arm.txApplyDestroy(KEY, txId, null, false, false, null, null, null, new ArrayList<>(), null, |
| null, false, null, null, 0); |
| |
| verify(arm._getOwner(), never()).updateSizeOnRemove(any(), anyInt()); |
| } |
| |
| @Test |
| public void txApplyDestroy_givenExistingNonTombstone_callsUpdateSizeOnRemove() { |
| RegionEntry regionEntry = mock(RegionEntry.class); |
| when(regionEntry.isTombstone()).thenReturn(false); |
| when(regionEntry.getVersionStamp()).thenReturn(mock(VersionStamp.class)); |
| TXId txId = mock(TXId.class); |
| when(txId.getMemberId()).thenReturn(mock(InternalDistributedMember.class)); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, null, null, regionEntry); |
| |
| arm.txApplyDestroy(KEY, txId, null, false, false, null, null, null, new ArrayList<>(), null, |
| null, false, null, null, 0); |
| |
| verify(arm._getOwner(), times(1)).updateSizeOnRemove(eq(KEY), anyInt()); |
| } |
| |
| @Test |
| public void txApplyDestroy_givenPutIfAbsentReturningDestroyedOrRemovedEntry_neverCallsUpdateSizeOnRemove() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(false); |
| when(entry.isDestroyedOrRemoved()).thenReturn(true); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(versionStamp.asVersionTag()).thenReturn(mock(VersionTag.class)); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry).thenReturn(null); |
| TXId txId = mock(TXId.class); |
| when(txId.getMemberId()).thenReturn(mock(InternalDistributedMember.class)); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, null); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| |
| arm.txApplyDestroy(KEY, txId, null, false, false, null, null, null, new ArrayList<>(), null, |
| null, false, null, null, 0); |
| |
| verify(arm._getOwner(), never()).updateSizeOnRemove(any(), anyInt()); |
| } |
| |
| @Test |
| public void txApplyDestroy_givenPutIfAbsentReturningNonTombstone_callsUpdateSizeOnRemove() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.getKey()).thenReturn(KEY); |
| when(entry.isTombstone()).thenReturn(false); |
| VersionStamp versionStamp = mock(VersionStamp.class); |
| when(entry.getVersionStamp()).thenReturn(versionStamp); |
| when(versionStamp.asVersionTag()).thenReturn(mock(VersionTag.class)); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry).thenReturn(null); |
| TXId txId = mock(TXId.class); |
| when(txId.getMemberId()).thenReturn(mock(InternalDistributedMember.class)); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, null); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| |
| arm.txApplyDestroy(KEY, txId, null, false, false, null, null, null, new ArrayList<>(), null, |
| null, false, null, null, 0); |
| |
| verify(arm._getOwner(), times(1)).updateSizeOnRemove(eq(KEY), anyInt()); |
| } |
| |
| @Test |
| public void txApplyDestroy_givenFactory_neverCallsUpdateSizeOnRemove() |
| throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| TXId txId = mock(TXId.class); |
| when(txId.getMemberId()).thenReturn(mock(InternalDistributedMember.class)); |
| RegionEntryFactory factory = mock(RegionEntryFactory.class); |
| when(factory.createEntry(any(), any(), any())).thenReturn(entry); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, factory); |
| when(arm._getOwner().getConcurrencyChecksEnabled()).thenReturn(true); |
| |
| arm.txApplyDestroy(KEY, txId, null, false, false, null, null, null, new ArrayList<>(), null, |
| null, false, null, null, 0); |
| |
| verify(arm._getOwner(), never()).updateSizeOnCreate(any(), anyInt()); |
| } |
| |
| private EntryEventImpl createEventForInvalidate(LocalRegion lr) { |
| when(lr.getKeyInfo(KEY)).thenReturn(new KeyInfo(KEY, null, null)); |
| return EntryEventImpl.create(lr, Operation.INVALIDATE, KEY, false, null, true, false); |
| } |
| |
| private EntryEventImpl createEventForDestroy(LocalRegion lr) { |
| when(lr.getKeyInfo(KEY)).thenReturn(new KeyInfo(KEY, null, null)); |
| return EntryEventImpl.create(lr, Operation.DESTROY, KEY, false, null, true, false); |
| } |
| |
| private void addEntry(AbstractRegionMap arm) { |
| addEntry(arm, "value"); |
| } |
| |
| private void addEntry(AbstractRegionMap arm, Object value) { |
| RegionEntry entry = arm.getEntryFactory().createEntry(arm._getOwner(), KEY, value); |
| arm.getEntryMap().put(KEY, entry); |
| } |
| |
| private void verifyTxApplyInvalidate(TxTestableAbstractRegionMap arm, Object key, RegionEntry re, |
| Token token) throws RegionClearedException { |
| re.setValue(arm._getOwner(), token); |
| arm.txApplyInvalidate(key, Token.INVALID, false, |
| new TXId(mock(InternalDistributedMember.class), 1), mock(TXRmtEvent.class), false, |
| mock(EventID.class), null, new ArrayList<EntryEventImpl>(), null, null, null, null, 1); |
| assertEquals(re.getValueAsToken(), token); |
| } |
| |
| /** |
| * TestableAbstractRegionMap |
| */ |
| private static class TestableAbstractRegionMap extends AbstractRegionMap { |
| private final RegionEntry regionEntryForGetEntry; |
| |
| protected TestableAbstractRegionMap() { |
| this(false); |
| } |
| |
| protected TestableAbstractRegionMap(boolean withConcurrencyChecks) { |
| this(withConcurrencyChecks, null, null); |
| } |
| |
| protected TestableAbstractRegionMap(boolean withConcurrencyChecks, |
| ConcurrentMapWithReusableEntries map, RegionEntryFactory factory) { |
| this(withConcurrencyChecks, false, map, factory, null); |
| } |
| |
| protected TestableAbstractRegionMap(boolean withConcurrencyChecks, |
| ConcurrentMapWithReusableEntries map, RegionEntryFactory factory, |
| RegionEntry regionEntryForGetEntry) { |
| this(withConcurrencyChecks, false, map, factory, regionEntryForGetEntry); |
| } |
| |
| protected TestableAbstractRegionMap(boolean withConcurrencyChecks, boolean isDistributedRegion, |
| ConcurrentMapWithReusableEntries map, RegionEntryFactory factory, |
| RegionEntry regionEntryForGetEntry) { |
| super(null); |
| this.regionEntryForGetEntry = regionEntryForGetEntry; |
| LocalRegion owner = isDistributedRegion ? mock(DistributedRegion.class, RETURNS_DEEP_STUBS) |
| : mock(LocalRegion.class); |
| CachePerfStats cachePerfStats = mock(CachePerfStats.class); |
| when(owner.getCachePerfStats()).thenReturn(cachePerfStats); |
| when(owner.getConcurrencyChecksEnabled()).thenReturn(withConcurrencyChecks); |
| when(owner.getDataPolicy()).thenReturn(DataPolicy.REPLICATE); |
| when(owner.getScope()).thenReturn(Scope.LOCAL); |
| when(owner.isInitialized()).thenReturn(true); |
| |
| InternalCache cache = mock(InternalCache.class); |
| InternalDistributedSystem ids = mock(InternalDistributedSystem.class); |
| when(owner.getCache()).thenReturn(cache); |
| when(cache.getDistributedSystem()).thenReturn(ids); |
| when(ids.getOffHeapStore()).thenReturn(null); |
| |
| doThrow(EntryNotFoundException.class).when(owner).checkEntryNotFound(any()); |
| initialize(owner, new Attributes(), null, false); |
| if (map != null) { |
| setEntryMap(map); |
| } |
| if (factory != null) { |
| setEntryFactory(factory); |
| } |
| } |
| |
| @Override |
| public RegionEntry getEntry(Object key) { |
| if (this.regionEntryForGetEntry != null) { |
| return this.regionEntryForGetEntry; |
| } else { |
| return super.getEntry(key); |
| } |
| } |
| } |
| |
| |
| /** |
| * TestableVMLRURegionMap |
| */ |
| private static class TestableVMLRURegionMap extends VMLRURegionMap { |
| private static EvictionAttributes evictionAttributes = |
| EvictionAttributes.createLRUEntryAttributes(); |
| |
| protected TestableVMLRURegionMap() { |
| this(false); |
| } |
| |
| private static LocalRegion createOwner(boolean withConcurrencyChecks) { |
| LocalRegion owner = mock(LocalRegion.class); |
| |
| InternalCache cache = mock(InternalCache.class); |
| InternalDistributedSystem ids = mock(InternalDistributedSystem.class); |
| when(owner.getCache()).thenReturn(cache); |
| when(cache.getDistributedSystem()).thenReturn(ids); |
| when(ids.getOffHeapStore()).thenReturn(null); |
| |
| CachePerfStats cachePerfStats = mock(CachePerfStats.class); |
| when(owner.getCachePerfStats()).thenReturn(cachePerfStats); |
| when(owner.getEvictionAttributes()).thenReturn(evictionAttributes); |
| when(owner.getConcurrencyChecksEnabled()).thenReturn(withConcurrencyChecks); |
| when(owner.getDataPolicy()).thenReturn(DataPolicy.REPLICATE); |
| doThrow(EntryNotFoundException.class).when(owner).checkEntryNotFound(any()); |
| return owner; |
| } |
| |
| private static EvictionController createEvictionController() { |
| EvictionController result = mock(EvictionController.class); |
| when(result.getEvictionAlgorithm()).thenReturn(evictionAttributes.getAlgorithm()); |
| EvictionCounters evictionCounters = mock(EvictionCounters.class); |
| when(result.getCounters()).thenReturn(evictionCounters); |
| return result; |
| } |
| |
| protected TestableVMLRURegionMap(boolean withConcurrencyChecks) { |
| super(createOwner(withConcurrencyChecks), new Attributes(), null, createEvictionController()); |
| } |
| |
| protected TestableVMLRURegionMap(boolean withConcurrencyChecks, |
| ConcurrentMapWithReusableEntries hashMap) { |
| this(withConcurrencyChecks); |
| setEntryMap(hashMap); |
| } |
| } |
| |
| /** |
| * TxTestableAbstractRegionMap |
| */ |
| private static class TxTestableAbstractRegionMap extends AbstractRegionMap { |
| |
| protected TxTestableAbstractRegionMap(boolean isInitialized) { |
| super(null); |
| InternalRegion owner; |
| if (isInitialized) { |
| owner = mock(LocalRegion.class); |
| when(owner.isInitialized()).thenReturn(true); |
| } else { |
| owner = mock(DistributedRegion.class); |
| when(owner.isInitialized()).thenReturn(false); |
| } |
| |
| InternalCache cache = mock(InternalCache.class); |
| InternalDistributedSystem ids = mock(InternalDistributedSystem.class); |
| |
| KeyInfo keyInfo = mock(KeyInfo.class); |
| when(keyInfo.getKey()).thenReturn(KEY); |
| when(owner.getKeyInfo(eq(KEY), any(), any())).thenReturn(keyInfo); |
| when(owner.getMyId()).thenReturn(mock(InternalDistributedMember.class)); |
| when(owner.getCache()).thenReturn(cache); |
| when(owner.isAllEvents()).thenReturn(true); |
| when(owner.shouldNotifyBridgeClients()).thenReturn(true); |
| when(owner.lockWhenRegionIsInitializing()).thenCallRealMethod(); |
| |
| when(cache.getDistributedSystem()).thenReturn(ids); |
| when(ids.getOffHeapStore()).thenReturn(null); |
| initialize(owner, new Attributes(), null, false); |
| } |
| |
| protected TxTestableAbstractRegionMap() { |
| this(true); |
| } |
| } |
| |
| @Test |
| public void txApplyPutOnSecondaryConstructsPendingCallbacksWhenRegionEntryExists() |
| throws Exception { |
| AbstractRegionMap arm = new TxRegionEntryTestableAbstractRegionMap(); |
| List<EntryEventImpl> pendingCallbacks = new ArrayList<>(); |
| TXId txId = new TXId(mock(InternalDistributedMember.class), 1); |
| TXRmtEvent txRmtEvent = mock(TXRmtEvent.class); |
| EventID eventId = mock(EventID.class); |
| Object newValue = "value"; |
| |
| InternalCache cache = mock(InternalCache.class); |
| InternalDistributedSystem ids = mock(InternalDistributedSystem.class); |
| when(arm._getOwner().getCache()).thenReturn(cache); |
| when(cache.getDistributedSystem()).thenReturn(ids); |
| when(ids.getOffHeapStore()).thenReturn(null); |
| |
| arm.txApplyPut(Operation.UPDATE, KEY, newValue, false, txId, txRmtEvent, eventId, null, |
| pendingCallbacks, null, null, null, null, 1); |
| |
| assertEquals(1, pendingCallbacks.size()); |
| verify(arm._getOwner(), times(1)).txApplyPutPart2(any(), any(), anyLong(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).invokeTXCallbacks(EnumListenerEvent.AFTER_UPDATE, UPDATEEVENT, |
| false); |
| } |
| |
| @Test |
| public void txApplyPutOnPrimaryConstructsPendingCallbacksWhenPutIfAbsentReturnsExistingEntry() |
| throws Exception { |
| AbstractRegionMap arm = new TxPutIfAbsentTestableAbstractRegionMap(); |
| List<EntryEventImpl> pendingCallbacks = new ArrayList<>(); |
| TXId txId = new TXId(arm._getOwner().getMyId(), 1); |
| EventID eventId = mock(EventID.class); |
| TXEntryState txEntryState = mock(TXEntryState.class); |
| Object newValue = "value"; |
| |
| InternalCache cache = mock(InternalCache.class); |
| InternalDistributedSystem ids = mock(InternalDistributedSystem.class); |
| when(arm._getOwner().getCache()).thenReturn(cache); |
| when(cache.getDistributedSystem()).thenReturn(ids); |
| when(ids.getOffHeapStore()).thenReturn(null); |
| |
| arm.txApplyPut(Operation.UPDATE, KEY, newValue, false, txId, null, eventId, null, |
| pendingCallbacks, null, null, txEntryState, null, 1); |
| |
| assertEquals(1, pendingCallbacks.size()); |
| verify(arm._getOwner(), times(1)).txApplyPutPart2(any(), any(), anyLong(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), never()).invokeTXCallbacks(EnumListenerEvent.AFTER_UPDATE, UPDATEEVENT, |
| false); |
| } |
| |
| @Test |
| public void txApplyPutOnSecondaryNotifiesClientsWhenRegionEntryIsRemoved() throws Exception { |
| AbstractRegionMap arm = new TxRemovedRegionEntryTestableAbstractRegionMap(); |
| List<EntryEventImpl> pendingCallbacks = new ArrayList<>(); |
| TXId txId = new TXId(mock(InternalDistributedMember.class), 1); |
| TXRmtEvent txRmtEvent = mock(TXRmtEvent.class); |
| EventID eventId = mock(EventID.class); |
| Object newValue = "value"; |
| |
| arm.txApplyPut(Operation.UPDATE, KEY, newValue, false, txId, txRmtEvent, eventId, null, |
| pendingCallbacks, null, null, null, null, 1); |
| |
| assertEquals(0, pendingCallbacks.size()); |
| verify(arm._getOwner(), never()).txApplyPutPart2(any(), any(), anyLong(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), times(1)).invokeTXCallbacks(EnumListenerEvent.AFTER_UPDATE, UPDATEEVENT, |
| false); |
| } |
| |
| @Test |
| public void txApplyPutOnSecondaryNotifiesClientsWhenRegionEntryIsNull() throws Exception { |
| AbstractRegionMap arm = new TxNoRegionEntryTestableAbstractRegionMap(); |
| List<EntryEventImpl> pendingCallbacks = new ArrayList<>(); |
| TXId txId = new TXId(mock(InternalDistributedMember.class), 1); |
| TXRmtEvent txRmtEvent = mock(TXRmtEvent.class); |
| EventID eventId = mock(EventID.class); |
| Object newValue = "value"; |
| |
| arm.txApplyPut(Operation.UPDATE, KEY, newValue, false, txId, txRmtEvent, eventId, null, |
| pendingCallbacks, null, null, null, null, 1); |
| |
| assertEquals(0, pendingCallbacks.size()); |
| verify(arm._getOwner(), never()).txApplyPutPart2(any(), any(), anyLong(), anyBoolean(), |
| anyBoolean(), anyBoolean()); |
| verify(arm._getOwner(), times(1)).invokeTXCallbacks(EnumListenerEvent.AFTER_UPDATE, UPDATEEVENT, |
| false); |
| } |
| |
| @Test |
| public void txApplyPutDoesNotLockWhenRegionIsInitialized() { |
| AbstractRegionMap arm = new TxTestableAbstractRegionMap(); |
| TXId txId = mock(TXId.class, RETURNS_DEEP_STUBS); |
| EventID eventId = mock(EventID.class); |
| TXRmtEvent txRmtEvent = mock(TXRmtEvent.class); |
| |
| InternalCache cache = mock(InternalCache.class); |
| InternalDistributedSystem ids = mock(InternalDistributedSystem.class); |
| when(arm._getOwner().getCache()).thenReturn(cache); |
| when(cache.getDistributedSystem()).thenReturn(ids); |
| when(ids.getOffHeapStore()).thenReturn(null); |
| |
| arm.txApplyPut(Operation.UPDATE, KEY, "", false, txId, txRmtEvent, eventId, null, |
| new ArrayList<>(), null, null, null, null, 1); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isFalse(); |
| verify(arm._getOwner(), never()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void txApplyPutLockWhenRegionIsInitializing() { |
| AbstractRegionMap arm = new TxTestableAbstractRegionMap(false); |
| TXId txId = mock(TXId.class, RETURNS_DEEP_STUBS); |
| EventID eventId = mock(EventID.class); |
| TXRmtEvent txRmtEvent = mock(TXRmtEvent.class); |
| |
| arm.txApplyPut(Operation.UPDATE, KEY, "", false, txId, txRmtEvent, eventId, null, |
| new ArrayList<>(), null, null, null, null, 1); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isTrue(); |
| verify(arm._getOwner()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void txApplyDestroyDoesNotLockWhenRegionIsInitialized() { |
| AbstractRegionMap arm = new TxTestableAbstractRegionMap(); |
| TXId txId = mock(TXId.class, RETURNS_DEEP_STUBS); |
| |
| arm.txApplyDestroy(KEY, txId, null, false, false, null, null, null, new ArrayList<>(), null, |
| null, true, null, null, 0); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isFalse(); |
| verify(arm._getOwner(), never()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void txApplyDestroyLockWhenRegionIsInitializing() { |
| AbstractRegionMap arm = new TxTestableAbstractRegionMap(false); |
| TXId txId = mock(TXId.class, RETURNS_DEEP_STUBS); |
| |
| arm.txApplyDestroy(KEY, txId, null, false, false, null, null, null, new ArrayList<>(), null, |
| null, true, null, null, 0); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isTrue(); |
| verify(arm._getOwner()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void txApplyInvalidateDoesNotLockWhenRegionIsInitialized() { |
| AbstractRegionMap arm = new TxTestableAbstractRegionMap(); |
| TXId txId = mock(TXId.class, RETURNS_DEEP_STUBS); |
| |
| arm.txApplyInvalidate(new Object(), Token.INVALID, false, |
| txId, mock(TXRmtEvent.class), false, |
| mock(EventID.class), null, new ArrayList<EntryEventImpl>(), null, null, null, null, 1); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isFalse(); |
| verify(arm._getOwner(), never()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void txApplyInvalidateLockWhenRegionIsInitializing() { |
| AbstractRegionMap arm = new TxTestableAbstractRegionMap(false); |
| TXId txId = mock(TXId.class, RETURNS_DEEP_STUBS); |
| |
| arm.txApplyInvalidate(new Object(), Token.INVALID, false, |
| txId, mock(TXRmtEvent.class), false, |
| mock(EventID.class), null, new ArrayList<EntryEventImpl>(), null, null, null, null, 1); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isTrue(); |
| verify(arm._getOwner()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void invalidateDoesNotLockWhenRegionIsInitialized() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, true, |
| mock(ConcurrentMapWithReusableEntries.class), mock(RegionEntryFactory.class), |
| mock(RegionEntry.class)); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| when(arm._getOwner().isInitialized()).thenReturn(true); |
| when(arm._getOwner().lockWhenRegionIsInitializing()).thenCallRealMethod(); |
| arm.invalidate(event, false, false, false); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isFalse(); |
| verify(arm._getOwner(), never()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void invalidateLocksWhenRegionIsInitializing() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, true, |
| mock(ConcurrentMapWithReusableEntries.class), mock(RegionEntryFactory.class), |
| mock(RegionEntry.class)); |
| EntryEventImpl event = createEventForInvalidate(arm._getOwner()); |
| when(arm._getOwner().isInitialized()).thenReturn(false); |
| when(arm._getOwner().lockWhenRegionIsInitializing()).thenCallRealMethod(); |
| arm.invalidate(event, false, false, false); |
| |
| verify(arm._getOwner()).lockWhenRegionIsInitializing(); |
| assertThat(arm._getOwner().lockWhenRegionIsInitializing()).isTrue(); |
| verify(arm._getOwner()).unlockWhenRegionIsInitializing(); |
| } |
| |
| @Test |
| public void isInTokenModeNeededReturnsFalseIfConcurrencyChecksEnabled() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(true, true, |
| mock(ConcurrentMapWithReusableEntries.class), mock(RegionEntryFactory.class), |
| mock(RegionEntry.class)); |
| |
| assertThat(arm.isInTokenModeNeeded(arm._getOwner(), true)).isFalse(); |
| } |
| |
| @Test |
| public void isInTokenModeNeededReturnsFalseIfInTokenModeIsFalse() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, true, |
| mock(ConcurrentMapWithReusableEntries.class), mock(RegionEntryFactory.class), |
| mock(RegionEntry.class)); |
| |
| assertThat(arm.isInTokenModeNeeded(arm._getOwner(), false)).isFalse(); |
| } |
| |
| @Test |
| public void isInTokenModeNeededReturnsTrueIfConcurrencyChecksNotEnabledAndInTokenMode() { |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, true, |
| mock(ConcurrentMapWithReusableEntries.class), mock(RegionEntryFactory.class), |
| mock(RegionEntry.class)); |
| |
| assertThat(arm.isInTokenModeNeeded(arm._getOwner(), true)).isTrue(); |
| } |
| |
| @Test |
| public void initialImagePut_lruEntryCreateInvoked() throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(true); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, null); |
| TestableAbstractRegionMap armSpy = spy(arm); |
| armSpy.initialImagePut(KEY, 0, "value", true, true, null, null, false); |
| verify(armSpy).lruEntryCreate(entry); |
| } |
| |
| @Test |
| public void initialImagePut_lruEntryUpdateInvoked() throws RegionClearedException { |
| ConcurrentMapWithReusableEntries map = mock(ConcurrentMapWithReusableEntries.class); |
| RegionEntry entry = mock(RegionEntry.class); |
| when(entry.isTombstone()).thenReturn(false); |
| when(entry.initialImagePut(any(), anyLong(), any(), anyBoolean(), anyBoolean())) |
| .thenReturn(true); |
| when(map.putIfAbsent(eq(KEY), any())).thenReturn(entry); |
| TestableAbstractRegionMap arm = new TestableAbstractRegionMap(false, map, null); |
| TestableAbstractRegionMap armSpy = spy(arm); |
| armSpy.initialImagePut(KEY, 0, "value", true, true, null, null, false); |
| verify(armSpy).lruEntryUpdate(entry); |
| } |
| |
| private static class TxNoRegionEntryTestableAbstractRegionMap |
| extends TxTestableAbstractRegionMap { |
| @Override |
| public RegionEntry getEntry(Object key) { |
| return null; |
| } |
| |
| @Override |
| EntryEventImpl createTransactionCallbackEvent(final LocalRegion re, Operation op, Object key, |
| Object newValue, TransactionId txId, TXRmtEvent txEvent, EventID eventId, |
| Object aCallbackArgument, FilterRoutingInfo filterRoutingInfo, |
| ClientProxyMembershipID bridgeContext, TXEntryState txEntryState, VersionTag versionTag, |
| long tailKey) { |
| return UPDATEEVENT; |
| } |
| } |
| |
| private static class TxRegionEntryTestableAbstractRegionMap extends TxTestableAbstractRegionMap { |
| @Override |
| public RegionEntry getEntry(Object key) { |
| return mock(RegionEntry.class); |
| } |
| } |
| |
| private static class TxPutIfAbsentTestableAbstractRegionMap extends TxTestableAbstractRegionMap { |
| @Override |
| public RegionEntry putEntryIfAbsent(Object key, RegionEntry newRe) { |
| return mock(RegionEntry.class); |
| } |
| } |
| |
| private static class TxRemovedRegionEntryTestableAbstractRegionMap |
| extends TxTestableAbstractRegionMap { |
| @Override |
| public RegionEntry getEntry(Object key) { |
| RegionEntry regionEntry = mock(RegionEntry.class); |
| when(regionEntry.isDestroyedOrRemoved()).thenReturn(true); |
| return regionEntry; |
| } |
| |
| @Override |
| EntryEventImpl createTransactionCallbackEvent(final LocalRegion re, Operation op, Object key, |
| Object newValue, TransactionId txId, TXRmtEvent txEvent, EventID eventId, |
| Object aCallbackArgument, FilterRoutingInfo filterRoutingInfo, |
| ClientProxyMembershipID bridgeContext, TXEntryState txEntryState, VersionTag versionTag, |
| long tailKey) { |
| return UPDATEEVENT; |
| } |
| } |
| |
| private static class TestableVersionTag extends VersionTag { |
| |
| private VersionSource versionSource; |
| |
| @Override |
| public boolean equals(Object o) { |
| return true; |
| } |
| |
| @Override |
| public KnownVersion[] getSerializationVersions() { |
| return new KnownVersion[0]; |
| } |
| |
| @Override |
| public VersionSource readMember(DataInput in) throws IOException, ClassNotFoundException { |
| return null; |
| } |
| |
| @Override |
| public void writeMember(VersionSource memberID, DataOutput out) throws IOException { |
| |
| } |
| |
| @Override |
| public int getDSFID() { |
| return 0; |
| } |
| |
| @Override |
| public VersionSource getMemberID() { |
| return versionSource; |
| } |
| |
| public void setVersionSource(VersionSource versionSource) { |
| this.versionSource = versionSource; |
| } |
| } |
| } |