You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2021/07/19 15:40:13 UTC

[ignite-3] branch ignite-15120 created (now 704b176)

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

agura pushed a change to branch ignite-15120
in repository https://gitbox.apache.org/repos/asf/ignite-3.git.


      at 704b176  IGNITE-15120 Tombstone condition added to meta storage invoke operation

This branch includes the following new commits:

     new 704b176  IGNITE-15120 Tombstone condition added to meta storage invoke operation

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[ignite-3] 01/01: IGNITE-15120 Tombstone condition added to meta storage invoke operation

Posted by ag...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 704b176af351da5a9b9999bd7f0107c61a4da1d4
Author: Andrey Gura <ag...@apache.org>
AuthorDate: Mon Jul 19 18:39:48 2021 +0300

    IGNITE-15120 Tombstone condition added to meta storage invoke operation
---
 .../internal/metastorage/client/Condition.java     |  31 ++++++
 .../internal/metastorage/client/Conditions.java    |  10 ++
 .../metastorage/client/MetaStorageServiceImpl.java |   5 +
 .../internal/metastorage/common/ConditionType.java |   5 +-
 .../metastorage/server/TombstoneCondition.java     |  40 ++++++++
 .../server/raft/MetaStorageListener.java           |   3 +
 .../server/SimpleInMemoryKeyValueStorageTest.java  | 105 +++++++++++++++++++++
 .../metastorage/server/TombstoneConditionTest.java |  57 +++++++++++
 8 files changed, 255 insertions(+), 1 deletion(-)

diff --git a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Condition.java b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Condition.java
index 45c41c3..08d859c 100644
--- a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Condition.java
+++ b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Condition.java
@@ -283,6 +283,37 @@ public final class Condition {
     }
 
     /**
+     * Represents condition on an entry's value which checks whether value is tombstone or not. Only one type of
+     * condition could be applied to the one instance of condition. Subsequent invocations of any method which produces
+     * condition will throw {@link IllegalStateException}.
+     */
+    public static final class TombstoneCondition extends AbstractCondition {
+        /**
+         * Constructs a condition on an entry, identified by the given key, is tombstone.
+         *
+         * @param key Identifies an entry which condition will be applied to.
+         */
+        TombstoneCondition(byte[] key) {
+            super(key);
+        }
+
+        /**
+         * Produces the condition of type {@link ConditionType#TOMBSTONE}. This condition tests that an entry's value,
+         * identified by the given key, is tombstone.
+         *
+         * @return The condition of type {@link ConditionType#TOMBSTONE}.
+         * @throws IllegalStateException In case when the condition is already defined.
+         */
+        public Condition tombstone() {
+            validate(type());
+
+            type(ConditionType.TOMBSTONE);
+
+            return new Condition(this);
+        }
+    }
+
+    /**
      * Checks that condition is not defined yet. If the condition is already defined then exception will be thrown.
      *
      * @throws IllegalStateException In case when the condition is already defined.
diff --git a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Conditions.java b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Conditions.java
index 60b5576..e326cc6 100644
--- a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Conditions.java
+++ b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/Conditions.java
@@ -70,6 +70,16 @@ public final class Conditions {
     }
 
     /**
+     * Creates condition on an entry's value which checks whether value is tombstone or not.
+     *
+     * @param key Identifies an entry which condition will be applied to. Can't be {@code null}.
+     * @return Condition on entry's value is tombstone.
+     */
+    public static Condition tombstone(@NotNull ByteArray key) {
+        return new Condition.TombstoneCondition(key.bytes()).tombstone();
+    }
+
+    /**
      * Default no-op constructor.
      */
     private Conditions() {
diff --git a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java
index c8202f3..63c48d3 100644
--- a/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java
+++ b/modules/metastorage-client/src/main/java/org/apache/ignite/internal/metastorage/client/MetaStorageServiceImpl.java
@@ -281,6 +281,11 @@ public class MetaStorageServiceImpl implements MetaStorageService {
 
             cnd = new ConditionInfo(inner.key(), inner.type(), null, 0);
         }
+        else if (obj instanceof Condition.TombstoneCondition) {
+            Condition.TombstoneCondition inner = (Condition.TombstoneCondition)obj;
+
+            cnd = new ConditionInfo(inner.key(), inner.type(), null, 0);
+        }
         else if (obj instanceof Condition.RevisionCondition) {
             Condition.RevisionCondition inner = (Condition.RevisionCondition)obj;
 
diff --git a/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/ConditionType.java b/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/ConditionType.java
index 522bd8f..28d1770 100644
--- a/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/ConditionType.java
+++ b/modules/metastorage-common/src/main/java/org/apache/ignite/internal/metastorage/common/ConditionType.java
@@ -49,5 +49,8 @@ public enum ConditionType {
     KEY_EXISTS,
 
     /** Non-existence condition type for key. */
-    KEY_NOT_EXISTS
+    KEY_NOT_EXISTS,
+
+    /** Tombstone condition type for key. */
+    TOMBSTONE
 }
diff --git a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/TombstoneCondition.java b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/TombstoneCondition.java
new file mode 100644
index 0000000..d6895ea
--- /dev/null
+++ b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/TombstoneCondition.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.jetbrains.annotations.NotNull;
+
+/**
+ * Condition tests an entry's value is tombstone in meta storage.
+ * Entry is tombstone if it is not empty and doesn't exists.
+ */
+public class TombstoneCondition extends AbstractCondition {
+    /**
+     * Constructs a condition with the given entry key.
+     *
+     * @param key Key identifies an entry which the condition will applied to.
+     */
+    public TombstoneCondition(@NotNull byte[] key) {
+        super(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean test(@NotNull Entry e) {
+        return e.tombstone();
+    }
+}
diff --git a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java
index 937ec49..38f76da 100644
--- a/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java
+++ b/modules/metastorage-server/src/main/java/org/apache/ignite/internal/metastorage/server/raft/MetaStorageListener.java
@@ -54,6 +54,7 @@ import org.apache.ignite.internal.metastorage.server.ExistenceCondition;
 import org.apache.ignite.internal.metastorage.server.KeyValueStorage;
 import org.apache.ignite.internal.metastorage.server.Operation;
 import org.apache.ignite.internal.metastorage.server.RevisionCondition;
+import org.apache.ignite.internal.metastorage.server.TombstoneCondition;
 import org.apache.ignite.internal.metastorage.server.ValueCondition;
 import org.apache.ignite.internal.metastorage.server.WatchEvent;
 import org.apache.ignite.internal.util.Cursor;
@@ -333,6 +334,8 @@ public class MetaStorageListener implements RaftGroupListener {
             return new ExistenceCondition(ExistenceCondition.Type.EXISTS, key);
         else if (type == ConditionType.KEY_NOT_EXISTS)
             return new ExistenceCondition(ExistenceCondition.Type.NOT_EXISTS, key);
+        else if (type == ConditionType.TOMBSTONE)
+            return new TombstoneCondition(key);
         else if (type == ConditionType.VAL_EQUAL)
             return new ValueCondition(ValueCondition.Type.EQUAL, key, info.value());
         else if (type == ConditionType.VAL_NOT_EQUAL)
diff --git a/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorageTest.java b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorageTest.java
index 342b4ba..508424f 100644
--- a/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorageTest.java
+++ b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/SimpleInMemoryKeyValueStorageTest.java
@@ -1283,6 +1283,111 @@ class SimpleInMemoryKeyValueStorageTest {
     }
 
     @Test
+    public void invokeWithTombstoneCondition_successBranch() {
+        byte[] key1 = k(1);
+        byte[] val1_1 = kv(1, 11);
+
+        byte[] key2 = k(2);
+        byte[] val2 = kv(2, 2);
+
+        byte[] key3 = k(3);
+        byte[] val3 = kv(3, 3);
+
+        assertEquals(0, storage.revision());
+        assertEquals(0, storage.updateCounter());
+
+        storage.put(key1, val1_1);
+        storage.remove(key1); // Should be tombstone after remove.
+
+        assertEquals(2, storage.revision());
+        assertEquals(2, storage.updateCounter());
+
+        boolean branch = storage.invoke(
+                new TombstoneCondition(key1),
+                List.of(new Operation(OperationType.PUT, key2, val2)),
+                List.of(new Operation(OperationType.PUT, key3, val3))
+        );
+
+        // "Success" branch is applied.
+        assertTrue(branch);
+        assertEquals(3, storage.revision());
+        assertEquals(3, storage.updateCounter());
+
+        Entry e1 = storage.get(key1);
+
+        assertFalse(e1.empty());
+        assertTrue(e1.tombstone());
+        assertEquals(2, e1.revision());
+        assertEquals(2, e1.updateCounter());
+        assertNull(e1.value());
+
+        Entry e2 = storage.get(key2);
+
+        assertFalse(e2.empty());
+        assertFalse(e2.tombstone());
+        assertEquals(3, e2.revision());
+        assertEquals(3, e2.updateCounter());
+        assertArrayEquals(val2, e2.value());
+
+        // "Failure" branch isn't applied.
+        Entry e3 = storage.get(key3);
+
+        assertTrue(e3.empty());
+    }
+
+    @Test
+    public void invokeWithTombstoneCondition_failureBranch() {
+        byte[] key1 = k(1);
+        byte[] val1_1 = kv(1, 11);
+
+        byte[] key2 = k(2);
+        byte[] val2 = kv(2, 2);
+
+        byte[] key3 = k(3);
+        byte[] val3 = kv(3, 3);
+
+        assertEquals(0, storage.revision());
+        assertEquals(0, storage.updateCounter());
+
+        storage.put(key1, val1_1);
+
+        assertEquals(1, storage.revision());
+        assertEquals(1, storage.updateCounter());
+
+        boolean branch = storage.invoke(
+                new TombstoneCondition(key1),
+                List.of(new Operation(OperationType.PUT, key2, val2)),
+                List.of(new Operation(OperationType.PUT, key3, val3))
+        );
+
+        // "Failure" branch is applied.
+        assertFalse(branch);
+        assertEquals(2, storage.revision());
+        assertEquals(2, storage.updateCounter());
+
+        Entry e1 = storage.get(key1);
+
+        assertFalse(e1.empty());
+        assertFalse(e1.tombstone());
+        assertEquals(1, e1.revision());
+        assertEquals(1, e1.updateCounter());
+        assertArrayEquals(val1_1, e1.value());
+
+        Entry e3 = storage.get(key3);
+
+        assertFalse(e3.empty());
+        assertFalse(e3.tombstone());
+        assertEquals(2, e3.revision());
+        assertEquals(2, e3.updateCounter());
+        assertArrayEquals(val3, e3.value());
+
+        // "Success" branch isn't applied.
+        Entry e2 = storage.get(key2);
+
+        assertTrue(e2.empty());
+    }
+
+    @Test
     public void invokeWithValueCondition_successBranch() {
         byte[] key1 = k(1);
         byte[] val1_1 = kv(1, 11);
diff --git a/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/TombstoneConditionTest.java b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/TombstoneConditionTest.java
new file mode 100644
index 0000000..9fe26e7
--- /dev/null
+++ b/modules/metastorage-server/src/test/java/org/apache/ignite/internal/metastorage/server/TombstoneConditionTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for entry tombstone condition.
+ *
+ * @see TombstoneCondition
+ */
+public class TombstoneConditionTest {
+    /** Entry key. */
+    private static final byte[] KEY = new byte[] {1};
+
+    /** Entry value. */
+    private static final byte[] VAL = new byte[] {1};
+
+    /** Regular entry. */
+    private static final Entry ENTRY = new Entry(KEY, VAL, 1, 1);
+
+    /** Empty entry. */
+    private static final Entry EMPTY = Entry.empty(KEY);
+
+    /** Tombstone entry. */
+    private static final Entry TOMBSTONE = Entry.tombstone(KEY, 1, 1);
+
+    /**
+     * Tests {@link TombstoneCondition} condition for regular, empty and tombstone entries.
+     */
+    @Test
+    public void tombstone() {
+        Condition cond = new TombstoneCondition(KEY);
+
+        assertFalse(cond.test(ENTRY));
+        assertFalse(cond.test(EMPTY));
+        assertTrue(cond.test(TOMBSTONE));
+    }
+}