This is an automated email from the ASF dual-hosted git repository.

sdanilov pushed a commit to branch ignite-18882
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 11248f8031bdd4fbe815ce95e7dc7a95c5d214b1
Author: Semyon Danilov <[email protected]>
AuthorDate: Wed Mar 1 20:29:42 2023 +0400

    IGNITE-18882 Don't store tombstones if there is no other version of row
---
 .../storage/AbstractMvPartitionStorageGcTest.java  | 15 ++++++--
 .../storage/impl/TestMvPartitionStorage.java       |  4 +++
 .../mv/AddWriteCommittedInvokeClosure.java         |  6 ++++
 .../pagememory/mv/CommitWriteInvokeClosure.java    |  7 ++++
 .../internal/storage/rocksdb/GarbageCollector.java | 40 +++++++++++++---------
 .../storage/rocksdb/RocksDbMvPartitionStorage.java |  1 +
 6 files changed, 54 insertions(+), 19 deletions(-)

diff --git 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvPartitionStorageGcTest.java
 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvPartitionStorageGcTest.java
index f98e1822f3..9911115db5 100644
--- 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvPartitionStorageGcTest.java
+++ 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvPartitionStorageGcTest.java
@@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
 import org.apache.ignite.internal.hlc.HybridTimestamp;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -132,7 +131,6 @@ public abstract class AbstractMvPartitionStorageGcTest 
extends BaseMvPartitionSt
     }
 
     @Test
-    @Disabled("https://issues.apache.org/jira/browse/IGNITE-18882";)
     void testVacuumsSecondRowIfTombstoneIsFirst() {
         addAndCommit(null);
 
@@ -144,4 +142,17 @@ public abstract class AbstractMvPartitionStorageGcTest 
extends BaseMvPartitionSt
 
         assertRowMatches(row.binaryRow(), TABLE_ROW);
     }
+
+    @Test
+    void testVacuumsSecondRowIfTombstoneIsFirst2() {
+        addWriteCommitted(ROW_ID, null, clock.now());
+
+        addWriteCommitted(ROW_ID, TABLE_ROW, clock.now());
+
+        addWriteCommitted(ROW_ID, TABLE_ROW2, clock.now());
+
+        BinaryRowAndRowId row = pollForVacuum(HybridTimestamp.MAX_VALUE);
+
+        assertRowMatches(row.binaryRow(), TABLE_ROW);
+    }
 }
diff --git 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/impl/TestMvPartitionStorage.java
 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/impl/TestMvPartitionStorage.java
index 6afd07d7dd..67f926b54e 100644
--- 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/impl/TestMvPartitionStorage.java
+++ 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/impl/TestMvPartitionStorage.java
@@ -271,6 +271,10 @@ public class TestMvPartitionStorage implements 
MvPartitionStorage {
             // Calling it from the compute is fine. Concurrent writes of the 
same row are impossible, and if we call the compute closure
             // several times, the same tuple will be inserted into the GC 
queue (timestamp and rowId don't change in this case).
             gcQueue.add(committedVersionChain);
+        } else {
+            if (committedVersionChain.row == null) {
+                return null;
+            }
         }
 
         return committedVersionChain;
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AddWriteCommittedInvokeClosure.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AddWriteCommittedInvokeClosure.java
index 3fddbc1249..182699cceb 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AddWriteCommittedInvokeClosure.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AddWriteCommittedInvokeClosure.java
@@ -81,6 +81,12 @@ class AddWriteCommittedInvokeClosure implements 
InvokeClosure<VersionChain> {
             throw new StorageException("Write intent exists: [rowId={}, {}]", 
oldRow.rowId(), storage.createStorageInfo());
         }
 
+        if (row == null && oldRow == null) {
+            operationType = OperationType.NOOP;
+
+            return;
+        }
+
         if (oldRow == null) {
             operationType = OperationType.PUT;
 
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/CommitWriteInvokeClosure.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/CommitWriteInvokeClosure.java
index 3a9b6e28e5..34ad755db5 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/CommitWriteInvokeClosure.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/CommitWriteInvokeClosure.java
@@ -80,6 +80,13 @@ class CommitWriteInvokeClosure implements 
InvokeClosure<VersionChain> {
         RowVersion current = storage.readRowVersion(oldRow.headLink(), 
DONT_LOAD_VALUE);
         RowVersion next = oldRow.hasNextLink() ? 
storage.readRowVersion(oldRow.nextLink(), DONT_LOAD_VALUE) : null;
 
+        if (next == null && current.isTombstone()) {
+            // Previous version doesn't exist and current version is a 
tombstone, delete current version.
+            operationType = OperationType.REMOVE;
+
+            return;
+        }
+
         // If the previous and current version are tombstones, then delete the 
current version.
         if (next != null && current.isTombstone() && next.isTombstone()) {
             toRemove = current;
diff --git 
a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/GarbageCollector.java
 
b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/GarbageCollector.java
index d150deac98..01743cd39d 100644
--- 
a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/GarbageCollector.java
+++ 
b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/GarbageCollector.java
@@ -105,8 +105,6 @@ class GarbageCollector {
             throws RocksDBException {
         ColumnFamilyHandle partCf = helper.partCf;
 
-        boolean newAndPrevTombstones = false;
-
         // Try find previous value for the row id.
         ByteBuffer keyBuffer = MV_KEY_BUFFER.get();
         keyBuffer.clear();
@@ -117,7 +115,7 @@ class GarbageCollector {
             it.seek(keyBuffer);
 
             if (invalid(it)) {
-                return false;
+                return isNewValueTombstone;
             }
 
             keyBuffer.clear();
@@ -126,28 +124,36 @@ class GarbageCollector {
 
             RowId readRowId = helper.getRowId(keyBuffer, ROW_ID_OFFSET);
 
-            if (readRowId.equals(rowId)) {
-                // Found previous value.
-                assert keyLen == MAX_KEY_SIZE; // Can not be write-intent.
+            if (!readRowId.equals(rowId)) {
+                return isNewValueTombstone;
+            }
 
-                if (isNewValueTombstone) {
-                    // If new value is a tombstone, lets check if previous 
value was also a tombstone.
-                    int valueSize = it.value(EMPTY_DIRECT_BUFFER);
+            // Found previous value.
+            assert keyLen == MAX_KEY_SIZE; // Can not be write-intent.
 
-                    newAndPrevTombstones = valueSize == 0;
+            if (isNewValueTombstone) {
+                // If new value is a tombstone, lets check if previous value 
was also a tombstone.
+                int valueSize = it.value(EMPTY_DIRECT_BUFFER);
+
+                if (valueSize == 0) {
+                    return true;
                 }
+            }
 
-                if (!newAndPrevTombstones) {
-                    keyBuffer.clear();
+            keyBuffer.clear();
 
-                    helper.putGcKey(keyBuffer, rowId, timestamp);
+            helper.putGcKey(keyBuffer, rowId, timestamp);
 
-                    writeBatch.put(gcQueueCf, keyBuffer, EMPTY_DIRECT_BUFFER);
-                }
-            }
+            writeBatch.put(gcQueueCf, keyBuffer, EMPTY_DIRECT_BUFFER);
         }
 
-        return newAndPrevTombstones;
+        return false;
+    }
+
+    enum Res {
+        NO_PREVIOUS_VALUE,
+        PREVIOUS_VALUE_TOMBSTONE,
+        HAS_PREVIOUS_VALUE
     }
 
     /**
diff --git 
a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbMvPartitionStorage.java
 
b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbMvPartitionStorage.java
index 31278c1a90..5deab406bf 100644
--- 
a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbMvPartitionStorage.java
+++ 
b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbMvPartitionStorage.java
@@ -67,6 +67,7 @@ import org.apache.ignite.internal.storage.RowId;
 import org.apache.ignite.internal.storage.StorageException;
 import org.apache.ignite.internal.storage.StorageRebalanceException;
 import org.apache.ignite.internal.storage.TxIdMismatchException;
+import org.apache.ignite.internal.storage.rocksdb.GarbageCollector.Res;
 import org.apache.ignite.internal.storage.util.StorageState;
 import org.apache.ignite.internal.util.ArrayUtils;
 import org.apache.ignite.internal.util.Cursor;

Reply via email to