blob: 48f6e87f08ae7962598b413b74f42f78c44b474d [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.offheap;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.COMPRESSED_BIT;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.DESTROYED_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.END_OF_STREAM_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.INVALID_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.LOCAL_INVALID_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.NOT_AVAILABLE_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.NULL_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.REMOVED_PHASE1_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.REMOVED_PHASE2_ADDRESS;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.SERIALIZED_BIT;
import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.TOMBSTONE_ADDRESS;
import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import java.nio.ByteBuffer;
import java.util.function.Function;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.apache.geode.compression.Compressor;
import org.apache.geode.internal.cache.CachePerfStats;
import org.apache.geode.internal.cache.CachedDeserializable;
import org.apache.geode.internal.cache.DiskId;
import org.apache.geode.internal.cache.EntryEventImpl;
import org.apache.geode.internal.cache.RegionEntryContext;
import org.apache.geode.internal.cache.Token;
import org.apache.geode.internal.cache.VMCachedDeserializable;
import org.apache.geode.internal.cache.entries.DiskEntry;
import org.apache.geode.internal.cache.entries.OffHeapRegionEntry;
import org.apache.geode.internal.cache.entries.VersionedStatsDiskRegionEntryOffHeap;
import org.apache.geode.internal.offheap.MemoryAllocatorImpl.DummyNonRealTimeStatsUpdater;
import org.apache.geode.internal.serialization.DSCODE;
public class OffHeapRegionEntryHelperInstanceTest {
private static final long VALUE_IS_NOT_ENCODABLE = 0L;
private MemoryAllocator memoryAllocator;
private ReferenceCounterInstance referenceCounter;
private OffHeapStoredObject offHeapStoredObject;
private OffHeapRegionEntryHelperInstance offHeapRegionEntryHelperInstance;
@Before
public void setUp() {
OutOfOffHeapMemoryListener listener = mock(OutOfOffHeapMemoryListener.class);
OffHeapMemoryStats stats = mock(OffHeapMemoryStats.class);
Function<Long, OffHeapStoredObject> offHeapStoredObjectFactory =
uncheckedCast(mock(Function.class));
offHeapStoredObject = mock(OffHeapStoredObject.class);
referenceCounter = mock(ReferenceCounterInstance.class);
when(offHeapStoredObjectFactory.apply(anyLong()))
.thenReturn(offHeapStoredObject);
memoryAllocator =
MemoryAllocatorImpl.create(listener, stats, 1, OffHeapStorage.MIN_SLAB_SIZE,
OffHeapStorage.MIN_SLAB_SIZE, null, () -> new DummyNonRealTimeStatsUpdater());
offHeapRegionEntryHelperInstance =
spy(new OffHeapRegionEntryHelperInstance(ohAddress -> offHeapStoredObject,
referenceCounter));
}
@After
public void tearDown() {
MemoryAllocatorImpl.freeOffHeapMemory();
}
@Test
public void encodeDataAsAddressShouldReturnZeroIfValueIsGreaterThanSevenBytes() {
byte[] valueInBytes =
ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(Long.MAX_VALUE).array();
assertThat(valueInBytes.length)
.isGreaterThanOrEqualTo(OffHeapRegionEntryHelperInstance.MAX_LENGTH_FOR_DATA_AS_ADDRESS);
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, false, false);
assertThat(encodedAddress)
.isEqualTo(VALUE_IS_NOT_ENCODABLE);
}
@Test
public void encodeDataAsAddressShouldEncodeLongIfItsSerializedAndIfItsNotTooBig() {
byte[] valueInBytes = EntryEventImpl.serialize(0L);
boolean isSerialized = true;
boolean isCompressed = false;
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
isCompressed);
long expectedEncodedAddress = 123L;
assertThat(encodedAddress)
.isEqualTo(expectedEncodedAddress);
assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
}
@Test
public void encodeDataAsAddressShouldReturnZeroIfValueIsLongAndItIsSerializedAndBig() {
byte[] valueInBytes = EntryEventImpl.serialize(Long.MAX_VALUE);
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, true,
false);
assertThat(encodedAddress)
.isEqualTo(VALUE_IS_NOT_ENCODABLE);
}
@Test
public void encodeDataAsAddressShouldReturnZeroIfValueIsLargerThanEightBytesAndNotLong() {
byte[] someValue = new byte[8];
someValue[0] = DSCODE.CLASS.toByte();
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(someValue, true, false);
assertThat(encodedAddress)
.isEqualTo(VALUE_IS_NOT_ENCODABLE);
}
@Test
public void encodeDataAsAddressShouldReturnValidAddressIfValueIsLesserThanSevenBytes() {
byte[] valueInBytes =
ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
boolean isSerialized = false;
boolean isCompressed = false;
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
isCompressed);
long expectedAddress = 549755813697L;
assertThat(encodedAddress)
.isEqualTo(expectedAddress);
assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
}
@Test
public void encodeDataAsAddressShouldSetSerializedBitIfSerialized() {
byte[] valueInBytes = EntryEventImpl.serialize(Integer.MAX_VALUE);
boolean isSerialized = true;
boolean isCompressed = false;
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
isCompressed);
long expectedAddress = 63221918596947L;
assertThat(expectedAddress)
.isEqualTo(encodedAddress);
assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
}
@Test
public void encodeDataAsAddressShouldSetSerializedBitIfCompressed() {
byte[] valueInBytes =
ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
boolean isSerialized = false;
boolean isCompressed = true;
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
isCompressed);
long expectedAddress = 549755813701L;
assertThat(encodedAddress)
.isEqualTo(expectedAddress);
assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
}
@Test
public void encodeDataAsAddressShouldSetBothSerializedAndCompressedBitsIfSerializedAndCompressed() {
byte[] valueInBytes = EntryEventImpl.serialize(Integer.MAX_VALUE);
boolean isSerialized = true;
boolean isCompressed = true;
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
isCompressed);
long expectedAddress = 63221918596951L;
assertThat(expectedAddress)
.isEqualTo(encodedAddress);
assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
}
@Test
public void decodeUncompressedAddressToBytesShouldReturnActualBytes() {
long encodedAddress = 549755813697L;
int value = Integer.MAX_VALUE;
byte[] actual =
offHeapRegionEntryHelperInstance.decodeUncompressedAddressToBytes(encodedAddress);
byte[] expectedValue = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
assertThat(actual)
.isEqualTo(expectedValue);
}
@Test
public void decodeUncompressedAddressToBytesShouldDecodeLongIfItsSerializedAndIfItsNotTooBig() {
byte[] actual = offHeapRegionEntryHelperInstance.decodeUncompressedAddressToBytes(123L);
byte[] expectedValue = EntryEventImpl.serialize(0L);
assertThat(actual)
.isEqualTo(expectedValue);
}
@Test
public void decodeUncompressedAddressToBytesWithCompressedAddressShouldThrowException() {
long encodedAddress = 549755813703L;
Throwable thrown = catchThrowable(() -> {
offHeapRegionEntryHelperInstance.decodeUncompressedAddressToBytes(encodedAddress);
});
assertThat(thrown)
.isInstanceOf(AssertionError.class);
}
@Test
public void decodeCompressedDataAsAddressToRawBytes() {
long encodedAddress = 549755813703L;
byte[] expected = new byte[] {127, -1, -1, -1};
byte[] bytes = offHeapRegionEntryHelperInstance.decodeAddressToRawBytes(encodedAddress);
assertThat(bytes)
.isEqualTo(expected);
}
@Test
public void encodedAddressShouldBeDecodableEvenIfValueIsSerialized() {
int value = Integer.MAX_VALUE;
byte[] serializedValue = EntryEventImpl.serialize(value);
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(serializedValue, true, false);
int actualValue =
(int) offHeapRegionEntryHelperInstance.decodeAddressToObject(encodedAddress);
assertThat(actualValue)
.isEqualTo(value);
}
@Test
public void encodedAddressShouldBeDecodableEvenIfValueIsUnserialized() {
int value = Integer.MAX_VALUE;
byte[] unSerializedValue = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
long encodedAddress =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(unSerializedValue, false, false);
byte[] actualValue =
(byte[]) offHeapRegionEntryHelperInstance.decodeAddressToObject(encodedAddress);
assertThat(actualValue)
.isEqualTo(unSerializedValue);
}
@Test
public void isSerializedShouldReturnTrueIfSerialized() {
assertThat(offHeapRegionEntryHelperInstance.isSerialized(1000010L)).isTrue();
}
@Test
public void isSerializedShouldReturnFalseIfNotSerialized() {
assertThat(offHeapRegionEntryHelperInstance.isSerialized(1000000L)).isFalse();
}
@Test
public void isCompressedShouldReturnTrueIfCompressed() {
assertThat(offHeapRegionEntryHelperInstance.isCompressed(1000100L)).isTrue();
}
@Test
public void isCompressedShouldReturnFalseIfNotCompressed() {
assertThat(offHeapRegionEntryHelperInstance.isCompressed(1000000L)).isFalse();
}
@Test
public void isOffHeapShouldReturnTrueIfAddressIsOnOffHeap() {
OffHeapStoredObject value = createChunk(Long.MAX_VALUE);
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(value.getAddress())).isTrue();
}
@Test
public void isOffHeapShouldReturnFalseIfAddressIsAnEncodedAddress() {
byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
long address = offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, false);
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(address)).isFalse();
}
@Test
public void isOffHeapShouldReturnFalseForAnyTokenAddress() {
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(NULL_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(INVALID_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(LOCAL_INVALID_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(DESTROYED_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(REMOVED_PHASE1_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(REMOVED_PHASE2_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(END_OF_STREAM_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(NOT_AVAILABLE_ADDRESS)).isFalse();
assertThat(offHeapRegionEntryHelperInstance.isOffHeap(TOMBSTONE_ADDRESS)).isFalse();
}
@Test
public void setValueShouldChangeTheRegionEntryAddressToNewAddress() {
// mock region entry
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
// some old address
long oldAddress = 1L;
// testing when the newValue is a chunk
OffHeapStoredObject newValue = createChunk(Long.MAX_VALUE);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, newValue.getAddress())).thenReturn(Boolean.TRUE);
// invoke the method under test
offHeapRegionEntryHelperInstance.setValue(regionEntry, newValue);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, newValue.getAddress());
// resetting the spy in-order to re-use
reset(regionEntry);
// testing when the newValue is DataAsAddress
TinyStoredObject newAddress1 = new TinyStoredObject(2L);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, newAddress1.getAddress())).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, newAddress1);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, newAddress1.getAddress());
reset(regionEntry);
// Testing when newValue is Token Objects
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, NULL_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, null);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, NULL_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, INVALID_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.INVALID);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, INVALID_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, LOCAL_INVALID_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.LOCAL_INVALID);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, LOCAL_INVALID_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, DESTROYED_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.DESTROYED);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, DESTROYED_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, REMOVED_PHASE1_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.REMOVED_PHASE1);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, REMOVED_PHASE1_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, REMOVED_PHASE2_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.REMOVED_PHASE2);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, REMOVED_PHASE2_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, END_OF_STREAM_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.END_OF_STREAM);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, END_OF_STREAM_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, NOT_AVAILABLE_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.NOT_AVAILABLE);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, NOT_AVAILABLE_ADDRESS);
reset(regionEntry);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, TOMBSTONE_ADDRESS)).thenReturn(true);
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.TOMBSTONE);
// verify oldAddress is replaced with newAddress
verify(regionEntry).setAddress(oldAddress, TOMBSTONE_ADDRESS);
}
@Test
public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndReleaseOldValueIfItsOnOffHeap() {
OffHeapStoredObject oldValue = createChunk(Long.MAX_VALUE);
OffHeapStoredObject newValue = createChunk(Long.MAX_VALUE - 1);
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
// mock Chunk static methods - in-order to verify that release is called
doNothing().when(offHeapStoredObject).release();
// mock region entry methods required for test
when(regionEntry.getAddress())
.thenReturn(oldValue.getAddress());
when(regionEntry.setAddress(oldValue.getAddress(), newValue.getAddress()))
.thenReturn(Boolean.TRUE);
// invoke the method under test
offHeapRegionEntryHelperInstance.setValue(regionEntry, newValue);
// verify oldAddress is changed to newAddress
verify(regionEntry)
.setAddress(oldValue.getAddress(), newValue.getAddress());
// verify oldAddress is released
verify(referenceCounter)
.release(oldValue.getAddress());
}
@Test
public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndDoesNothingIfOldAddressIsAnEncodedAddress() {
byte[] oldData =
ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
byte[] newData =
ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE - 1).array();
long oldAddress = offHeapRegionEntryHelperInstance.encodeDataAsAddress(oldData, false, false);
StoredObject newAddress = new TinyStoredObject(
offHeapRegionEntryHelperInstance.encodeDataAsAddress(newData, false, false));
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
// mock region entry methods required for test
when(regionEntry.getAddress())
.thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, newAddress.getAddress()))
.thenReturn(true);
// invoke the method under test
offHeapRegionEntryHelperInstance.setValue(regionEntry, newAddress);
// verify oldAddress is changed to newAddress
verify(regionEntry)
.setAddress(oldAddress, newAddress.getAddress());
// verify that release is never called as the old address is not on offheap
verifyNoInteractions(offHeapStoredObject);
}
@Test
public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndDoesNothingIfOldAddressIsATokenAddress() {
long oldAddress = REMOVED_PHASE1_ADDRESS;
long newAddress = REMOVED_PHASE2_ADDRESS;
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
when(regionEntry.getAddress())
.thenReturn(oldAddress);
when(regionEntry.setAddress(oldAddress, newAddress))
.thenReturn(true);
// invoke the method under test
offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.REMOVED_PHASE2);
// verify oldAddress is changed to newAddress
verify(regionEntry)
.setAddress(oldAddress, newAddress);
}
@Test
public void setValueShouldThrowIllegalExceptionIfNewValueCannotBeConvertedToAddress() {
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
when(regionEntry.getAddress())
.thenReturn(1L);
// invoke the method under test with some object other than Chunk/DataAsAddress/Token
Throwable thrown = catchThrowable(() -> {
offHeapRegionEntryHelperInstance.setValue(regionEntry, new Object());
});
assertThat(thrown)
.isInstanceOf(IllegalStateException.class);
}
@Test
public void getValueAsTokenShouldReturnNotATokenIfValueIsOnOffHeap() {
OffHeapStoredObject chunk = createChunk(Long.MAX_VALUE);
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
when(regionEntry.getAddress())
.thenReturn(chunk.getAddress());
Token token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token)
.isEqualTo(Token.NOT_A_TOKEN);
}
@Test
public void getValueAsTokenShouldReturnNotATokenIfValueIsEncoded() {
byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
long address = offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, false);
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
when(regionEntry.getAddress())
.thenReturn(address);
Token token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token)
.isEqualTo(Token.NOT_A_TOKEN);
}
@Test
public void getValueAsTokenShouldReturnAValidToken() {
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
when(regionEntry.getAddress()).thenReturn(NULL_ADDRESS);
Token token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isNull();
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(INVALID_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.INVALID);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(LOCAL_INVALID_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.LOCAL_INVALID);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(DESTROYED_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.DESTROYED);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(REMOVED_PHASE1_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.REMOVED_PHASE1);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(REMOVED_PHASE2_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.REMOVED_PHASE2);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(END_OF_STREAM_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.END_OF_STREAM);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(NOT_AVAILABLE_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.NOT_AVAILABLE);
// mock region entry methods required for test
when(regionEntry.getAddress()).thenReturn(TOMBSTONE_ADDRESS);
token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
assertThat(token).isEqualTo(Token.TOMBSTONE);
}
@Test
public void addressToObjectShouldReturnValueFromChunk() {
OffHeapRegionEntryHelperInstance offHeapRegionEntryHelperInstance =
new OffHeapRegionEntryHelperInstance();
OffHeapStoredObject expected = createChunk(Long.MAX_VALUE);
Object actual =
offHeapRegionEntryHelperInstance.addressToObject(expected.getAddress(), false, null);
assertThat(actual)
.isInstanceOf(OffHeapStoredObject.class)
.isEqualTo(expected);
}
@Test
public void addressToObjectShouldReturnCachedDeserializableFromChunkIfAskedToDecompress() {
byte[] data = EntryEventImpl.serialize(Long.MAX_VALUE);
RegionEntryContext regionContext = mock(RegionEntryContext.class);
CachePerfStats cacheStats = mock(CachePerfStats.class);
Compressor compressor = mock(Compressor.class);
when(regionContext.getCompressor())
.thenReturn(compressor);
when(compressor.decompress(data))
.thenReturn(data);
when(regionContext.getCachePerfStats())
.thenReturn(cacheStats);
when(cacheStats.startDecompression())
.thenReturn(10000L);
MemoryBlock chunk =
(MemoryBlock) memoryAllocator.allocateAndInitialize(data, true, true);
offHeapRegionEntryHelperInstance =
spy(new OffHeapRegionEntryHelperInstance(OffHeapStoredObject::new, referenceCounter));
Object actual =
offHeapRegionEntryHelperInstance.addressToObject(chunk.getAddress(), true, regionContext);
assertThat(actual)
.isInstanceOf(VMCachedDeserializable.class);
long actualValue = (long) ((CachedDeserializable) actual).getDeserializedForReading();
assertThat(actualValue)
.isEqualTo(Long.MAX_VALUE);
}
@Test
public void addressToObjectShouldReturnDecompressedValueFromChunkIfAskedToDecompress() {
byte[] data = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(Long.MAX_VALUE).array();
RegionEntryContext regionContext = mock(RegionEntryContext.class);
CachePerfStats cacheStats = mock(CachePerfStats.class);
Compressor compressor = mock(Compressor.class);
when(regionContext.getCompressor())
.thenReturn(compressor);
when(compressor.decompress(data))
.thenReturn(data);
when(regionContext.getCachePerfStats())
.thenReturn(cacheStats);
when(cacheStats.startDecompression())
.thenReturn(10000L);
MemoryBlock chunk = (MemoryBlock) memoryAllocator.allocateAndInitialize(data, false, true);
offHeapRegionEntryHelperInstance =
spy(new OffHeapRegionEntryHelperInstance(OffHeapStoredObject::new, referenceCounter));
Object actual =
offHeapRegionEntryHelperInstance.addressToObject(chunk.getAddress(), true, regionContext);
assertThat(actual)
.isInstanceOf(byte[].class)
.isEqualTo(data);
}
@Test
public void addressToObjectShouldReturnValueFromDataAsAddress() {
byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
long address = offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, false);
Object actual = offHeapRegionEntryHelperInstance.addressToObject(address, false, null);
TinyStoredObject expected = new TinyStoredObject(address);
assertThat(actual)
.isInstanceOf(TinyStoredObject.class)
.isEqualTo(expected);
}
@Test
public void addressToObjectShouldReturnCachedDeserializableFromSerializedDataAsAddressIfAskedToDecompress() {
byte[] data = EntryEventImpl.serialize(Integer.MAX_VALUE);
RegionEntryContext regionContext = mock(RegionEntryContext.class);
CachePerfStats cacheStats = mock(CachePerfStats.class);
Compressor compressor = mock(Compressor.class);
when(regionContext.getCompressor())
.thenReturn(compressor);
when(compressor.decompress(data))
.thenReturn(data);
when(regionContext.getCachePerfStats())
.thenReturn(cacheStats);
when(cacheStats.startDecompression())
.thenReturn(10000L);
long address =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, true, true);
Object actual = offHeapRegionEntryHelperInstance.addressToObject(address, true, regionContext);
assertThat(actual)
.isInstanceOf(VMCachedDeserializable.class);
int actualValue = (int) ((CachedDeserializable) actual).getDeserializedForReading();
assertThat(actualValue)
.isEqualTo(Integer.MAX_VALUE);
}
@Test
public void addressToObjectShouldReturnDecompressedValueFromDataAsAddressIfAskedToDecompress() {
byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
RegionEntryContext regionContext = mock(RegionEntryContext.class);
CachePerfStats cacheStats = mock(CachePerfStats.class);
Compressor compressor = mock(Compressor.class);
when(regionContext.getCompressor())
.thenReturn(compressor);
when(compressor.decompress(data))
.thenReturn(data);
when(regionContext.getCachePerfStats())
.thenReturn(cacheStats);
when(cacheStats.startDecompression())
.thenReturn(10000L);
long address =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, true);
Object actual = offHeapRegionEntryHelperInstance.addressToObject(address, true, regionContext);
assertThat(actual)
.isInstanceOf(byte[].class)
.isEqualTo(data);
}
@Test
public void addressToObjectShouldReturnToken() {
Token token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(NULL_ADDRESS, false, null);
assertThat(token).isNull();
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(INVALID_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.INVALID);
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(LOCAL_INVALID_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.LOCAL_INVALID);
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(DESTROYED_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.DESTROYED);
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(REMOVED_PHASE1_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.REMOVED_PHASE1);
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(REMOVED_PHASE2_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.REMOVED_PHASE2);
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(END_OF_STREAM_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.END_OF_STREAM);
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(NOT_AVAILABLE_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.NOT_AVAILABLE);
token = (Token) offHeapRegionEntryHelperInstance
.addressToObject(TOMBSTONE_ADDRESS, false, null);
assertThat(token).isEqualTo(Token.TOMBSTONE);
}
@Test
public void getSerializedLengthFromDataAsAddressShouldReturnValidLength() {
byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
long address =
offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, true);
TinyStoredObject tinyStoredObject = new TinyStoredObject(address);
int actualLength = offHeapRegionEntryHelperInstance.getSerializedLength(tinyStoredObject);
assertThat(actualLength)
.isEqualTo(data.length);
}
@Test
public void getSerializedLengthFromDataAsAddressShouldReturnZeroForNonEncodedAddress() {
TinyStoredObject nonEncodedAddress = new TinyStoredObject(100000L);
int actualLength = offHeapRegionEntryHelperInstance.getSerializedLength(nonEncodedAddress);
assertThat(actualLength)
.isZero();
}
@Test
public void releaseEntryShouldSetValueToRemovePhase2() {
OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
when(regionEntry.getAddress())
.thenReturn(1L);
when(regionEntry.setAddress(1L, REMOVED_PHASE2_ADDRESS))
.thenReturn(Boolean.TRUE);
offHeapRegionEntryHelperInstance.releaseEntry(regionEntry);
verify(offHeapRegionEntryHelperInstance)
.setValue(regionEntry, Token.REMOVED_PHASE2);
}
@Test
public void releaseEntryShouldSetValueToRemovePhase2AndSetsAsyncToFalseForDiskEntry() {
OffHeapRegionEntry regionEntry = mock(VersionedStatsDiskRegionEntryOffHeap.class);
DiskId diskId = spy(DiskId.class);
when(regionEntry.getAddress())
.thenReturn(1L);
when(regionEntry.setAddress(1L, REMOVED_PHASE2_ADDRESS))
.thenReturn(Boolean.TRUE);
when(((DiskEntry) regionEntry).getDiskId())
.thenReturn(diskId);
when(diskId.isPendingAsync())
.thenReturn(Boolean.TRUE);
offHeapRegionEntryHelperInstance.releaseEntry(regionEntry);
verify(offHeapRegionEntryHelperInstance)
.setValue(regionEntry, Token.REMOVED_PHASE2);
verify(diskId)
.setPendingAsync(Boolean.FALSE);
}
@Test
public void doWithOffHeapClearShouldSetTheThreadLocalToTrue() {
// verify that threadlocal is not set
assertThat(OffHeapClearRequired.doesClearNeedToCheckForOffHeap()).isFalse();
OffHeapClearRequired.doWithOffHeapClear(() -> {
// verify that threadlocal is set when offheap is cleared
assertThat(OffHeapClearRequired.doesClearNeedToCheckForOffHeap()).isTrue();
});
// verify that threadlocal is reset after offheap is cleared
assertThat(OffHeapClearRequired.doesClearNeedToCheckForOffHeap()).isFalse();
}
private OffHeapStoredObject createChunk(Object value) {
byte[] bytes = EntryEventImpl.serialize(value);
StoredObject chunk = memoryAllocator.allocateAndInitialize(bytes, true, false);
return (OffHeapStoredObject) chunk;
}
private static void assertSerializedAndCompressedBits(long encodedAddress,
boolean shouldSerializedBitBeSet, boolean shouldCompressedBitBeSet) {
boolean isSerializedBitSet = (encodedAddress & SERIALIZED_BIT) == SERIALIZED_BIT;
assertThat(isSerializedBitSet)
.isEqualTo(shouldSerializedBitBeSet);
boolean isCompressedBitSet = (encodedAddress & COMPRESSED_BIT) == COMPRESSED_BIT;
assertThat(isCompressedBitSet)
.isEqualTo(shouldCompressedBitBeSet);
}
}