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;
+ }
}