blob: 47904bf214201fefd3ebe377ac721d325f4e2418 [file] [log] [blame]
/*
* 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;
}
}
}