tkalkirill commented on code in PR #2019:
URL: https://github.com/apache/ignite-3/pull/2019#discussion_r1187105941
##########
modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksStorageUtils.java:
##########
@@ -188,4 +210,25 @@ static long[] getAsLongs(byte[] bytes) {
return result;
}
+
+ /**
+ * Converts an array of {@code long} values to an array of bytes.
+ *
+ * @param values Array of values.
+ * @param valuesOffset Offset in the array of values to start from.
+ * @return Array of bytes.
+ */
+ static byte[] longsToBytes(long[] values, int valuesOffset) {
+ assert valuesOffset < values.length;
Review Comment:
```suggestion
assert valuesOffset < values.length: "off=" + valuesOffset + ",
arr.len=" + values.length;
```
##########
modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksStorageUtils.java:
##########
@@ -64,7 +76,18 @@ static byte[] longToBytes(long value) {
static long bytesToLong(byte[] array) {
assert array.length == Long.BYTES;
- return (long) LONG_ARRAY_HANDLE.get(array, 0);
+ return bytesToLong(array, 0);
+ }
+
+ /**
+ * Gets a {@code long} value from a byte array starting from the specified
position.
+ *
+ * @param array Byte array.
+ * @param offset Offset to read a value from.
+ * @return {@code long} value.
+ */
+ static long bytesToLong(byte[] array, int offset) {
+ return (long) LONG_ARRAY_HANDLE.get(array, offset);
Review Comment:
```suggestion
static long bytesToLong(byte[] array, int offset) {
assert offset + Long.BYTES <= array.length: "off=" + offset + ",
arr.len=" + array.length;
return (long) LONG_ARRAY_HANDLE.get(array, offset);
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
Review Comment:
```suggestion
// Previous value, must be removed due to compaction with last
tombstone.
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
Review Comment:
```suggestion
HybridTimestamp compactTs = clock.now();
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value2, clock.now());
+
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ // Last operation was remove, so this is a tombstone.
+ Entry entry3 = storage.get(key, lastRevision);
+ assertTrue(entry3.tombstone());
+
+ Entry entry2 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 2);
+ assertTrue(entry1.empty());
+ }
Review Comment:
Please add `lastRevision - 3`
##########
modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksDbKeyValueStorage.java:
##########
@@ -975,30 +1020,104 @@ private boolean addToBatchForRemoval(WriteBatch batch,
byte[] key, long curRev,
}
/**
- * Compacts all entries by the given key, removing all previous revisions
and deleting the last entry if it is a tombstone.
+ * Compacts all entries by the given key, removing revision that are no
longer needed.
+ * Last entry with a revision lesser or equal to the {@code
minRevisionToKeep} and all consecutive entries will be preserved.
+ * If the first entry to keep is a tombstone, it will be removed.
+ * Example:
+ * <pre>
+ * Example 1:
+ * put entry1: revision 5
+ * put entry2: revision 7
+ *
+ * do compaction: revision 6
+ *
+ * entry1: exists
+ * entry2: exists
+ *
+ * Example 2:
+ * put entry1: revision 5
+ * put entry2: revision 7
+ *
+ * do compaction: revision 7
+ *
+ * entry1: doesn't exist
+ * entry2: exists
+ * </pre>
*
* @param batch Write batch.
* @param key Target key.
* @param revs Revisions.
+ * @param minRevisionToKeep Minimum revision that should be kept.
* @throws RocksDBException If failed.
*/
- private void compactForKey(WriteBatch batch, byte[] key, long[] revs)
throws RocksDBException {
- long lastRev = lastRevision(revs);
+ private void compactForKey(WriteBatch batch, byte[] key, long[] revs, long
minRevisionToKeep) throws RocksDBException {
+ if (revs.length < 2) {
+ // If we have less than two revisions, there is no point in
compaction.
+ return;
+ }
+
+ // Index of the first revision we will be keeping in the array of
revisions.
+ int idxToKeepFrom = 0;
+
+ // Whether there is an entry with the minRevisionToKeep.
+ boolean hasMinRevision = false;
+
+ // Traverse revisions, looking for the first revision that needs to be
kept.
+ for (long rev : revs) {
+ if (rev >= minRevisionToKeep) {
+ if (rev == minRevisionToKeep) {
+ hasMinRevision = true;
+ }
+ break;
+ }
+
+ idxToKeepFrom++;
+ }
Review Comment:
I saw similar code in the test implementation, can we transfer it to a
separate method?
##########
modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageWriteHandler.java:
##########
@@ -71,74 +72,78 @@ class MetaStorageWriteHandler {
void handleWriteCommand(CommandClosure<WriteCommand> clo) {
WriteCommand command = clo.command();
- if (command instanceof PutCommand) {
- PutCommand putCmd = (PutCommand) command;
+ HybridTimestamp safeTime;
- storage.put(putCmd.key(), putCmd.value());
+ if (command instanceof MetaStorageWriteCommand) {
+ safeTime = ((MetaStorageWriteCommand) command).safeTime();
- clo.result(null);
- } else if (command instanceof GetAndPutCommand) {
- GetAndPutCommand getAndPutCmd = (GetAndPutCommand) command;
+ if (command instanceof PutCommand) {
Review Comment:
How about moving the processing of these commands into a separate private
method with the safe time passed as an argument?
Will there be any problems with exceptions?
For example, in
`org.apache.ignite.internal.table.distributed.raft.PartitionListener#onWrite`
there is processing and if you remove it, then there will be freezes.
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value2, clock.now());
+
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ // Last operation was remove, so this is a tombstone.
+ Entry entry3 = storage.get(key, lastRevision);
+ assertTrue(entry3.tombstone());
+
+ Entry entry2 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 2);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactEmptyStorage() {
+ storage.compact(clock.now());
Review Comment:
What is being tested here?
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value2, clock.now());
+
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ // Last operation was remove, so this is a tombstone.
+ Entry entry3 = storage.get(key, lastRevision);
+ assertTrue(entry3.tombstone());
+
+ Entry entry2 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 2);
Review Comment:
```suggestion
Entry entry2 = storage.get(key, lastRevision - 2);
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value2, clock.now());
+
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ // Last operation was remove, so this is a tombstone.
+ Entry entry3 = storage.get(key, lastRevision);
+ assertTrue(entry3.tombstone());
+
+ Entry entry2 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 2);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactEmptyStorage() {
+ storage.compact(clock.now());
+ }
+
+ @Test
+ public void testCompactionBetweenRevisionsOfOneKey() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.put(key(1), keyValue(1, 0), clock.now());
+
+ HybridTimestamp ts = clock.now();
Review Comment:
```suggestion
HybridTimestamp compactTs = clock.now();
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value2, clock.now());
+
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ // Last operation was remove, so this is a tombstone.
+ Entry entry3 = storage.get(key, lastRevision);
+ assertTrue(entry3.tombstone());
+
+ Entry entry2 = storage.get(key, lastRevision - 1);
Review Comment:
```suggestion
Entry entry3 = storage.get(key, lastRevision - 1);
```
##########
modules/metastorage/src/main/java/org/apache/ignite/internal/metastorage/server/persistence/RocksStorageUtils.java:
##########
@@ -50,11 +49,24 @@ class RocksStorageUtils {
static byte[] longToBytes(long value) {
var buffer = new byte[Long.BYTES];
- LONG_ARRAY_HANDLE.set(buffer, 0, value);
+ putLongToBytes(value, buffer, 0);
return buffer;
}
+ /**
+ * Puts a {@code long} value to the byte array at specified position.
+ *
+ * @param value Value.
+ * @param array Byte array.
+ * @param position Position to put long at.
+ */
+ static void putLongToBytes(long value, byte[] array, int position) {
+ assert position + Long.BYTES <= array.length;
Review Comment:
```suggestion
assert position + Long.BYTES <= array.length: "pos=" + position + ",
arr.len=" + array.length;
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value2, clock.now());
+
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ // Last operation was remove, so this is a tombstone.
+ Entry entry3 = storage.get(key, lastRevision);
+ assertTrue(entry3.tombstone());
+
+ Entry entry2 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 2);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactEmptyStorage() {
+ storage.compact(clock.now());
+ }
+
+ @Test
+ public void testCompactionBetweenRevisionsOfOneKey() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.put(key(1), keyValue(1, 0), clock.now());
Review Comment:
Please move `key(1)` to var, and check it.
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
Review Comment:
```suggestion
HybridTimestamp comactTs = clock.now();
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
Review Comment:
```suggestion
byte[] value = keyValue(0, 0);
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
+ private final HybridClock clock = new HybridClockImpl();
+
+ @Test
+ public void testCompactionAfterLastRevision() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Latest value, must exist.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertEquals(lastRevision, entry2.revision());
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+
+ storage.put(key, value1, clock.now());
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(clock.now());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry2 = storage.get(key, lastRevision);
+ assertTrue(entry2.empty());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 1);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionBetweenMultipleWrites() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+ byte[] value3 = keyValue(0, 2);
+ byte[] value4 = keyValue(0, 3);
+
+ storage.put(key, value1, clock.now());
+ storage.put(key, value2, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value3, clock.now());
+ storage.put(key, value4, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ Entry entry4 = storage.get(key, lastRevision);
+ assertArrayEquals(value4, entry4.value());
+
+ Entry entry3 = storage.get(key, lastRevision - 1);
+ assertArrayEquals(value3, entry3.value());
+
+ Entry entry2 = storage.get(key, lastRevision - 2);
+ assertArrayEquals(value2, entry2.value());
+
+ // Previous value, must be removed due to compaction.
+ Entry entry1 = storage.get(key, lastRevision - 3);
+ assertTrue(entry1.empty());
+ }
+
+ @Test
+ public void testCompactionAfterTombstoneRemovesTombstone() {
+ byte[] key = key(0);
+ byte[] value1 = keyValue(0, 0);
+ byte[] value2 = keyValue(0, 1);
+
+ storage.put(key, value1, clock.now());
+
+ storage.remove(key, clock.now());
+
+ HybridTimestamp ts = clock.now();
+
+ storage.put(key, value2, clock.now());
+
+ storage.remove(key, clock.now());
+
+ long lastRevision = storage.revision();
+
+ storage.compact(ts);
+
+ // Last operation was remove, so this is a tombstone.
+ Entry entry3 = storage.get(key, lastRevision);
Review Comment:
```suggestion
Entry entry4 = storage.get(key, lastRevision);
```
##########
modules/metastorage/src/test/java/org/apache/ignite/internal/metastorage/server/CompactionKeyValueStorageTest.java:
##########
@@ -0,0 +1,194 @@
+/*
+ * 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.metastorage.server;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.hlc.HybridClock;
+import org.apache.ignite.internal.hlc.HybridClockImpl;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.metastorage.Entry;
+import org.junit.jupiter.api.Test;
+
+/** Compaction tests. */
+public abstract class CompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
Review Comment:
```suggestion
public abstract class AbstractCompactionKeyValueStorageTest extends
AbstractKeyValueStorageTest {
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]