blob: 8b518f63e676fa530c10d8f75b74777613fb0b42 [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.ignite.internal.table;
import static org.apache.ignite.internal.schema.NativeTypes.BYTES;
import static org.apache.ignite.internal.schema.NativeTypes.DATE;
import static org.apache.ignite.internal.schema.NativeTypes.DOUBLE;
import static org.apache.ignite.internal.schema.NativeTypes.FLOAT;
import static org.apache.ignite.internal.schema.NativeTypes.INT16;
import static org.apache.ignite.internal.schema.NativeTypes.INT32;
import static org.apache.ignite.internal.schema.NativeTypes.INT64;
import static org.apache.ignite.internal.schema.NativeTypes.INT8;
import static org.apache.ignite.internal.schema.NativeTypes.STRING;
import static org.apache.ignite.internal.schema.NativeTypes.datetime;
import static org.apache.ignite.internal.schema.NativeTypes.time;
import static org.apache.ignite.internal.schema.NativeTypes.timestamp;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.NativeTypeSpec;
import org.apache.ignite.internal.schema.NativeTypes;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.testobjects.TestObjectWithAllTypes;
import org.apache.ignite.internal.storage.basic.ConcurrentHashMapPartitionStorage;
import org.apache.ignite.internal.table.distributed.storage.VersionedRowStore;
import org.apache.ignite.internal.table.impl.DummyInternalTableImpl;
import org.apache.ignite.internal.table.impl.DummySchemaManagerImpl;
import org.apache.ignite.internal.tx.TxManager;
import org.apache.ignite.internal.tx.impl.HeapLockManager;
import org.apache.ignite.internal.tx.impl.TxManagerImpl;
import org.apache.ignite.network.ClusterService;
import org.apache.ignite.table.KeyValueView;
import org.apache.ignite.table.mapper.Mapper;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
/**
* Basic table operations test.
*/
//TODO: IGNITE-14487 Add bulk operations tests.
//TODO: IGNITE-14487 Add async operations tests.
public class KeyValueViewOperationsTest {
private final Random rnd = new Random();
@Test
public void put() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj3 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
assertNull(tbl.get(key));
// Put KV pair.
tbl.put(key, obj);
assertEquals(obj, tbl.get(key));
assertEquals(obj, tbl.get(key));
// Update KV pair.
tbl.put(key, obj2);
assertEquals(obj2, tbl.get(key));
assertEquals(obj2, tbl.get(key));
// Remove KV pair.
tbl.remove(key);
assertNull(tbl.get(key));
// Put KV pair.
tbl.put(key, obj3);
assertEquals(obj3, tbl.get(key));
}
@Test
public void putIfAbsent() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
assertNull(tbl.get(key));
// Insert new KV pair.
assertTrue(tbl.putIfAbsent(key, obj));
assertEquals(obj, tbl.get(key));
// Update KV pair.
assertFalse(tbl.putIfAbsent(key, obj2));
assertEquals(obj, tbl.get(key));
}
@Test
public void getAndPut() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj3 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
assertNull(tbl.get(key));
// Insert new KV pair.
assertNull(tbl.getAndPut(key, obj));
assertEquals(obj, tbl.get(key));
// Update KV pair.
assertEquals(obj, tbl.getAndPut(key, obj2));
assertEquals(obj2, tbl.getAndPut(key, obj3));
assertEquals(obj3, tbl.get(key));
}
@Test
public void contains() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
// Not-existed value.
assertFalse(tbl.contains(key));
// Put KV pair.
tbl.put(key, obj);
assertTrue(tbl.contains(key));
// Delete key.
assertTrue(tbl.remove(key));
assertFalse(tbl.contains(key));
// Put KV pair.
tbl.put(key, obj2);
assertTrue(tbl.contains(key));
// Delete key.
tbl.remove(key2);
assertFalse(tbl.contains(key2));
}
@Test
public void remove() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
// Put KV pair.
tbl.put(key, obj);
// Delete existed key.
assertEquals(obj, tbl.get(key));
assertTrue(tbl.remove(key));
assertNull(tbl.get(key));
// Delete already deleted key.
assertFalse(tbl.remove(key));
// Put KV pair.
tbl.put(key, obj2);
assertEquals(obj2, tbl.get(key));
// Delete existed key.
assertTrue(tbl.remove(key));
assertNull(tbl.get(key));
// Delete not existed key.
assertNull(tbl.get(key2));
assertFalse(tbl.remove(key2));
}
@Test
public void removeExact() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
// Put KV pair.
tbl.put(key, obj);
assertEquals(obj, tbl.get(key));
// Fails to delete KV pair with unexpected value.
assertFalse(tbl.remove(key, obj2));
assertEquals(obj, tbl.get(key));
// Delete KV pair with expected value.
assertTrue(tbl.remove(key, obj));
assertNull(tbl.get(key));
// Once again.
assertFalse(tbl.remove(key, obj));
assertNull(tbl.get(key));
// Try to remove non-existed key.
assertFalse(tbl.remove(key, obj));
assertNull(tbl.get(key));
// Put KV pair.
tbl.put(key, obj2);
assertEquals(obj2, tbl.get(key));
// Check null value ignored.
assertThrows(Throwable.class, () -> tbl.remove(key, null));
assertEquals(obj2, tbl.get(key));
// Delete KV pair with expected value.
assertTrue(tbl.remove(key, obj2));
assertNull(tbl.get(key));
assertFalse(tbl.remove(key2, obj2));
assertNull(tbl.get(key2));
}
@Test
public void replace() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj3 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
// Ignore replace operation for non-existed KV pair.
assertFalse(tbl.replace(key, obj));
assertNull(tbl.get(key));
tbl.put(key, obj);
// Replace existed KV pair.
assertTrue(tbl.replace(key, obj2));
assertEquals(obj2, tbl.get(key));
// Try remove existed KV pair.
assertThrows(Throwable.class, () -> tbl.replace(key, null));
assertNotNull(tbl.get(key));
tbl.remove(key);
// Ignore replace operation for non-existed KV pair.
assertFalse(tbl.replace(key, obj3));
assertNull(tbl.get(key));
tbl.put(key, obj3);
assertEquals(obj3, tbl.get(key));
// Trye remove non-existed KV pair.
assertThrows(Throwable.class, () -> tbl.replace(key2, null));
assertNull(tbl.get(key2));
}
@Test
public void replaceExact() {
final TestKeyObject key = TestKeyObject.randomObject(rnd);
final TestKeyObject key2 = TestKeyObject.randomObject(rnd);
final TestObjectWithAllTypes obj = TestObjectWithAllTypes.randomObject(rnd);
final TestObjectWithAllTypes obj2 = TestObjectWithAllTypes.randomObject(rnd);
KeyValueView<TestKeyObject, TestObjectWithAllTypes> tbl = kvView();
// Insert KV pair.
assertThrows(Throwable.class, () -> tbl.replace(key, null, obj));
tbl.put(key, obj);
assertEquals(obj, tbl.get(key));
assertNull(tbl.get(key2));
// Ignore replace operation for non-existed KV pair.
assertFalse(tbl.replace(key2, obj, obj2));
assertNull(tbl.get(key2));
// Replace existed KV pair.
assertTrue(tbl.replace(key, obj, obj2));
assertEquals(obj2, tbl.get(key));
// try remove existed KV pair.
assertThrows(Throwable.class, () -> tbl.replace(key, obj2, null));
assertNotNull(tbl.get(key));
}
/**
* Creates key-value view.
*/
private KeyValueViewImpl<TestKeyObject, TestObjectWithAllTypes> kvView() {
ClusterService clusterService = Mockito.mock(ClusterService.class, RETURNS_DEEP_STUBS);
Mockito.when(clusterService.topologyService().localMember().address())
.thenReturn(DummyInternalTableImpl.ADDR);
TxManager txManager = new TxManagerImpl(clusterService, new HeapLockManager());
DummyInternalTableImpl table = new DummyInternalTableImpl(
new VersionedRowStore(new ConcurrentHashMapPartitionStorage(), txManager), txManager);
Mapper<TestKeyObject> keyMapper = Mapper.of(TestKeyObject.class);
Mapper<TestObjectWithAllTypes> valMapper = Mapper.of(TestObjectWithAllTypes.class);
Column[] valCols = {
new Column("primitiveByteCol", INT8, false),
new Column("primitiveShortCol", INT16, false),
new Column("primitiveIntCol", INT32, false),
new Column("primitiveLongCol", INT64, false),
new Column("primitiveFloatCol", FLOAT, false),
new Column("primitiveDoubleCol", DOUBLE, false),
new Column("byteCol", INT8, true),
new Column("shortCol", INT16, true),
new Column("intCol", INT32, true),
new Column("longCol", INT64, true),
new Column("nullLongCol", INT64, true),
new Column("floatCol", FLOAT, true),
new Column("doubleCol", DOUBLE, true),
new Column("dateCol", DATE, true),
new Column("timeCol", time(), true),
new Column("dateTimeCol", datetime(), true),
new Column("timestampCol", timestamp(), true),
new Column("uuidCol", NativeTypes.UUID, true),
new Column("bitmaskCol", NativeTypes.bitmaskOf(42), true),
new Column("stringCol", STRING, true),
new Column("nullBytesCol", BYTES, true),
new Column("bytesCol", BYTES, true),
new Column("numberCol", NativeTypes.numberOf(12), true),
new Column("decimalCol", NativeTypes.decimalOf(19, 3), true),
};
SchemaDescriptor schema = new SchemaDescriptor(
1,
new Column[]{new Column("id", NativeTypes.INT64, false)},
valCols
);
// Validate all types are tested.
Set<NativeTypeSpec> testedTypes = Arrays.stream(valCols).map(c -> c.type().spec())
.collect(Collectors.toSet());
Set<NativeTypeSpec> missedTypes = Arrays.stream(NativeTypeSpec.values())
.filter(t -> !testedTypes.contains(t)).collect(Collectors.toSet());
assertEquals(Collections.emptySet(), missedTypes);
return new KeyValueViewImpl<>(
table,
new DummySchemaManagerImpl(schema),
keyMapper,
valMapper,
null
);
}
/**
* Test object.
*/
@SuppressWarnings({"InstanceVariableMayNotBeInitialized", "unused"})
public static class TestKeyObject {
public static TestKeyObject randomObject(Random rnd) {
return new TestKeyObject(rnd.nextLong());
}
private long id;
private TestKeyObject() {
}
public TestKeyObject(long id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestKeyObject that = (TestKeyObject) o;
return id == that.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
}