You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sa...@apache.org on 2022/12/19 06:31:33 UTC

[ignite-3] branch main updated: IGNITE-17976 Correct exception is thrown on KeyValueView#get in case of lost majority (#1429)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new ebbda7d373 IGNITE-17976 Correct exception is thrown on KeyValueView#get in case of lost majority (#1429)
ebbda7d373 is described below

commit ebbda7d373011cec85ad8dd0c80e69b7ae9ee771
Author: Alexander Lapin <la...@gmail.com>
AuthorDate: Mon Dec 19 09:31:28 2022 +0300

    IGNITE-17976 Correct exception is thrown on KeyValueView#get in case of lost majority (#1429)
---
 .../java/org/apache/ignite/lang/ErrorGroups.java   |  3 ++
 .../exception/ReplicaUnavailableException.java     |  3 +-
 .../replicator/exception/ReplicationException.java | 10 ++++++
 .../exception/ReplicationTimeoutException.java     |  3 +-
 .../runner/app/ItIgniteNodeRestartTest.java        | 24 ++++++++++---
 .../app/client/ItThinClientTransactionsTest.java   |  2 +-
 .../distributed/storage/InternalTableImpl.java     | 42 +++++++++++++++++++---
 7 files changed, 73 insertions(+), 14 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
index 1a11e32f98..791dddfd18 100755
--- a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
+++ b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
@@ -279,6 +279,9 @@ public class ErrorGroups {
 
         /** Failed to enlist read-write operation into read-only transaction. */
         public static final int TX_INSUFFICIENT_READ_WRITE_OPERATION_ERR = TX_ERR_GROUP.registerErrorCode(9);
+
+        /** The error happens when the replica is not ready to handle a request. */
+        public static final int TX_REPLICA_UNAVAILABLE_ERR = TX_ERR_GROUP.registerErrorCode(10);
     }
 
     /** Replicator error group. */
diff --git a/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicaUnavailableException.java b/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicaUnavailableException.java
index 5c3f61eae0..a5344d1774 100644
--- a/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicaUnavailableException.java
+++ b/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicaUnavailableException.java
@@ -21,13 +21,12 @@ import static org.apache.ignite.lang.ErrorGroups.Replicator.REPLICA_UNAVAILABLE_
 
 import java.util.UUID;
 import org.apache.ignite.internal.replicator.ReplicationGroupId;
-import org.apache.ignite.lang.IgniteInternalException;
 import org.apache.ignite.network.ClusterNode;
 
 /**
  * The exception is thrown when a replica is not ready to handle a request.
  */
-public class ReplicaUnavailableException extends IgniteInternalException {
+public class ReplicaUnavailableException extends ReplicationException {
     /**
      * The constructor.
      *
diff --git a/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationException.java b/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationException.java
index 25ebce1186..d4f5ce6322 100644
--- a/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationException.java
+++ b/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationException.java
@@ -46,6 +46,16 @@ public class ReplicationException extends IgniteInternalException {
         this(REPLICA_COMMON_ERR, "Failed to process replica request [replicaGroupId=" + replicaGrpId + ']', cause);
     }
 
+    /**
+     * The constructor.
+     *
+     * @param code    Exception code.
+     * @param message Exception message.
+     */
+    public ReplicationException(int code, String message) {
+        super(code, message);
+    }
+
     /**
      * The constructor.
      *
diff --git a/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationTimeoutException.java b/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationTimeoutException.java
index 4941436346..dcb7913c46 100644
--- a/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationTimeoutException.java
+++ b/modules/replicator/src/main/java/org/apache/ignite/internal/replicator/exception/ReplicationTimeoutException.java
@@ -20,13 +20,12 @@ package org.apache.ignite.internal.replicator.exception;
 import java.util.UUID;
 import org.apache.ignite.internal.replicator.ReplicationGroupId;
 import org.apache.ignite.lang.ErrorGroups.Replicator;
-import org.apache.ignite.lang.IgniteInternalException;
 import org.apache.ignite.lang.IgniteStringFormatter;
 
 /**
  * The exception is thrown when a replication process has been timed out.
  */
-public class ReplicationTimeoutException extends IgniteInternalException {
+public class ReplicationTimeoutException extends ReplicationException {
     /**
      * The constructor.
      *
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
index 18f98dddfd..2022e60a32 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.runner.app;
 
 import static org.apache.ignite.internal.recovery.ConfigurationCatchUpListener.CONFIGURATION_CATCH_UP_DIFFERENCE_PROPERTY;
-import static org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCause;
 import static org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
 import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.apache.ignite.utils.ClusterServiceTestUtils.defaultSerializationRegistry;
@@ -27,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 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.Mockito.mock;
 
@@ -42,7 +42,6 @@ import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -108,6 +107,7 @@ import org.apache.ignite.network.scalecube.TestScaleCubeClusterServiceFactory;
 import org.apache.ignite.sql.Session;
 import org.apache.ignite.table.Table;
 import org.apache.ignite.table.Tuple;
+import org.apache.ignite.tx.TransactionException;
 import org.intellij.lang.annotations.Language;
 import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.AfterEach;
@@ -799,7 +799,6 @@ public class ItIgniteNodeRestartTest extends IgniteAbstractTest {
      * checks that the table created before node stop, is not available when majority if lost.
      */
     @Test
-    @Disabled("https://issues.apache.org/jira/browse/IGNITE-17976")
     public void testOneNodeRestartWithGap() {
         Ignite ignite = startNode(0);
 
@@ -813,9 +812,9 @@ public class ItIgniteNodeRestartTest extends IgniteAbstractTest {
 
         assertNotNull(table);
 
-        assertThrowsWithCause(() -> table.keyValueView().get(null, Tuple.create().set("id", 0)), TimeoutException.class);
+        assertThrows(TransactionException.class, () -> table.keyValueView().get(null, Tuple.create().set("id", 0)));
 
-        createTableWithData(ignite, TABLE_NAME_2, 1, 1);
+        createTableWithoutData(ignite, TABLE_NAME_2, 1, 1);
 
         components = startPartialNode(1, null);
 
@@ -1032,6 +1031,21 @@ public class ItIgniteNodeRestartTest extends IgniteAbstractTest {
         }
     }
 
+    /**
+     * Creates a table.
+     *
+     * @param ignite Ignite.
+     * @param name Table name.
+     * @param replicas Replica factor.
+     * @param partitions Partitions count.
+     */
+    private static void createTableWithoutData(Ignite ignite, String name, int replicas, int partitions) {
+        try (Session session = ignite.sql().createSession()) {
+            session.execute(null, "CREATE TABLE " + name
+                    + "(id INT PRIMARY KEY, name VARCHAR) WITH replicas=" + replicas + ", partitions=" + partitions);
+        }
+    }
+
     /**
      * Configuration catch-up listener for test.
      */
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java
index c9b275410e..a4eae7616e 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java
@@ -193,7 +193,7 @@ public class ItThinClientTransactionsTest extends ItAbstractThinClientTest {
         kvView.put(tx2, -100, "1");
 
         var ex = assertThrows(IgniteException.class, () -> kvView.get(tx1, -100));
-        assertThat(ex.getMessage(), containsString("TimeoutException"));
+        assertThat(ex.getMessage(), containsString("Replication is timed out"));
 
         tx2.rollback();
     }
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java
index 32d44362a1..7f05644750 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java
@@ -22,9 +22,11 @@ import static java.util.concurrent.CompletableFuture.failedFuture;
 import static org.apache.ignite.internal.util.ExceptionUtils.withCause;
 import static org.apache.ignite.lang.ErrorGroups.Replicator.REPLICA_UNAVAILABLE_ERR;
 import static org.apache.ignite.lang.ErrorGroups.Transactions.TX_INSUFFICIENT_READ_WRITE_OPERATION_ERR;
+import static org.apache.ignite.lang.ErrorGroups.Transactions.TX_REPLICA_UNAVAILABLE_ERR;
 
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import java.net.ConnectException;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collection;
@@ -34,9 +36,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
 import java.util.concurrent.Flow.Publisher;
 import java.util.concurrent.Flow.Subscriber;
 import java.util.concurrent.Flow.Subscription;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
@@ -49,6 +53,7 @@ import org.apache.ignite.internal.raft.service.RaftGroupService;
 import org.apache.ignite.internal.replicator.ReplicaService;
 import org.apache.ignite.internal.replicator.ReplicationGroupId;
 import org.apache.ignite.internal.replicator.exception.PrimaryReplicaMissException;
+import org.apache.ignite.internal.replicator.exception.ReplicationException;
 import org.apache.ignite.internal.replicator.message.ReplicaRequest;
 import org.apache.ignite.internal.schema.BinaryRow;
 import org.apache.ignite.internal.schema.BinaryRowEx;
@@ -456,17 +461,27 @@ public class InternalTableImpl implements InternalTable {
             @Override
             public CompletableFuture<T> apply(T r, Throwable e) {
                 if (e != null) {
+                    Throwable e0 = wrapReplicationException((RuntimeException) e);
+
                     return tx0.rollbackAsync().handle((ignored, err) -> {
+
                         if (err != null) {
-                            e.addSuppressed(err);
+                            e0.addSuppressed(err);
                         }
-
-                        throw (RuntimeException) e;
+                        throw (RuntimeException) e0;
                     }); // Preserve failed state.
                 } else {
                     tx0.enlistResultFuture(fut);
 
-                    return implicit ? tx0.commitAsync().thenApply(ignored -> r) : completedFuture(r);
+                    if (implicit) {
+                        return tx0.commitAsync()
+                                .exceptionally(ex -> {
+                                    throw wrapReplicationException((RuntimeException) ex);
+                                })
+                                .thenApply(ignored -> r);
+                    } else {
+                        return completedFuture(r);
+                    }
                 }
             }
         }).thenCompose(x -> x);
@@ -1324,4 +1339,23 @@ public class InternalTableImpl implements InternalTable {
             }
         });
     }
+
+    /**
+     * Wraps {@link ReplicationException} or {@link ConnectException} with {@link TransactionException}.
+     *
+     * @param e {@link ReplicationException} or {@link CompletionException} with cause {@link ConnectException} or {@link TimeoutException}
+     * @return {@link TransactionException}
+     */
+    private RuntimeException wrapReplicationException(RuntimeException e) {
+        RuntimeException e0;
+
+        if (e instanceof ReplicationException || e.getCause() instanceof ReplicationException || e.getCause() instanceof ConnectException
+                || e.getCause() instanceof TimeoutException) {
+            e0 = withCause(TransactionException::new, TX_REPLICA_UNAVAILABLE_ERR, e);
+        } else {
+            e0 = e;
+        }
+
+        return e0;
+    }
 }