This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 3123460 IGNITE-16025 .NET: Add thin client retry policy (#9786)
3123460 is described below
commit 312346035de8923188def37e2f9ba130f6b67d11
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Thu Feb 3 14:04:05 2022 +0300
IGNITE-16025 .NET: Add thin client retry policy (#9786)
* Add `IClientRetryPolicy` interface.
* Add predefined policies: `ClientRetryAllPolicy`, `ClientRetryReadPolicy`.
* Add `ClientConfiguration.retryPolicy` property, defaults to null (keep
existing behavior - no retries by default).
https://cwiki.apache.org/confluence/display/IGNITE/IEP-82+Thin+Client+Retry+Policy
---
.../apache/ignite/client/ClientOperationType.java | 4 +-
.../Client/ClientConnectionTest.cs | 211 ++++++++++++++++++++
.../ClientProtocolCompatibilityTest.cs | 2 +-
.../Client/Compute/ComputeClientTests.cs | 2 +-
.../Client/IgniteClientConfigurationTest.cs | 2 -
.../Client/TestRetryPolicy.cs | 57 ++++++
.../Client/ClientOperationType.cs | 219 +++++++++++++++++++++
.../Client/ClientRetryAllPolicy.cs | 31 +++
.../Client/ClientRetryReadPolicy.cs | 55 ++++++
.../Client/IClientRetryPolicy.cs | 36 ++++
.../Client/IClientRetryPolicyContext.cs | 47 +++++
.../Client/IgniteClientConfiguration.cs | 22 +++
.../IgniteClientConfigurationSection.xsd | 17 ++
.../Impl/Client/ClientFailoverSocket.cs | 172 +++++++++++++++-
.../Impl/Client/ClientOpExtensions.cs | 158 +++++++++++++++
.../Impl/Client/ClientRetryPolicyContext.cs | 59 ++++++
.../Apache.Ignite.Core/Impl/Common/TypeCaster.cs | 8 +-
17 files changed, 1082 insertions(+), 20 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/client/ClientOperationType.java
b/modules/core/src/main/java/org/apache/ignite/client/ClientOperationType.java
index f99b2e0..d5ac4b6 100644
---
a/modules/core/src/main/java/org/apache/ignite/client/ClientOperationType.java
+++
b/modules/core/src/main/java/org/apache/ignite/client/ClientOperationType.java
@@ -55,7 +55,7 @@ public enum ClientOperationType {
CACHE_GET,
/**
- * Get value from cache ({@link ClientCache#get(Object)}).
+ * Put value to cache ({@link ClientCache#put(Object, Object)}).
*/
CACHE_PUT,
@@ -106,7 +106,7 @@ public enum ClientOperationType {
CACHE_REMOVE_MULTIPLE,
/**
- * Remove everyting from cache ({@link ClientCache#removeAll()}).
+ * Remove everything from cache ({@link ClientCache#removeAll()}).
*/
CACHE_REMOVE_EVERYTHING,
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
index b7c65bb..92501b9 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
@@ -34,6 +34,7 @@ namespace Apache.Ignite.Core.Tests.Client
using Apache.Ignite.Core.Impl.Common;
using Apache.Ignite.Core.Log;
using Apache.Ignite.Core.Tests.Client.Cache;
+ using Apache.Ignite.Core.Tests.Client.Compute;
using NUnit.Framework;
/// <summary>
@@ -643,6 +644,216 @@ namespace Apache.Ignite.Core.Tests.Client
}
/// <summary>
+ /// Tests automatic retry with one server.
+ /// </summary>
+ [Test]
+ public void TestFailoverWithRetryPolicyReconnectsToNewNode()
+ {
+ Ignition.Start(TestUtils.GetTestConfiguration());
+
+ var cfg = new IgniteClientConfiguration
+ {
+ Endpoints = new[] { "127.0.0.1" },
+ RetryPolicy = new ClientRetryReadPolicy()
+ };
+
+ using (var client = Ignition.StartClient(cfg))
+ {
+ var restartTask = Task.Run(() =>
+ {
+ Ignition.StopAll(true);
+ Thread.Sleep(100);
+ Ignition.Start(TestUtils.GetTestConfiguration());
+ });
+
+ while (!restartTask.IsCompleted)
+ {
+ // Operations do not fail while the only node is being
restarted.
+ Assert.AreEqual(0, client.GetCacheNames().Count);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Tests that operation fails with an exception when retry limit is
reached.
+ /// </summary>
+ [Test]
+ public void TestFailoverWithRetryPolicyThrowsOnRetryCountExceeded()
+ {
+ Ignition.Start(TestUtils.GetTestConfiguration());
+
+ var retryLimit = 4;
+
+ var cfg = new IgniteClientConfiguration
+ {
+ Endpoints = new[] { "127.0.0.1" },
+ RetryPolicy = new ClientRetryAllPolicy(),
+ RetryLimit = retryLimit
+ };
+
+ using (var client = Ignition.StartClient(cfg))
+ {
+ Assert.AreEqual(0, client.GetCacheNames().Count);
+
+ Ignition.StopAll(true);
+
+ var ex = Assert.Throws<IgniteClientException>(() =>
client.GetCacheNames());
+ StringAssert.StartsWith($"Operation failed after {retryLimit}
retries", ex.Message);
+
+ Assert.IsNotNull(ex.InnerException);
+ Assert.IsInstanceOf<AggregateException>(ex.InnerException);
+ Assert.AreEqual(retryLimit,
((AggregateException)ex.InnerException).InnerExceptions.Count);
+ }
+ }
+
+ /// <summary>
+ /// Tests that failed operations not related to connection issues are
not retried.
+ /// </summary>
+ [Test]
+ public void TestFailoverWithRetryPolicyDoesNotRetryUnrelatedErrors()
+ {
+ Ignition.Start(TestUtils.GetTestConfiguration());
+
+ var cfg = new IgniteClientConfiguration
+ {
+ Endpoints = new[] { "127.0.0.1" },
+ RetryPolicy = new ClientRetryAllPolicy()
+ };
+
+ using (var client = Ignition.StartClient(cfg))
+ {
+ var ex = Assert.Catch<Exception>(() =>
+
client.GetCompute().ExecuteJavaTask<object>(ComputeClientTests.TestTask, null));
+
+ StringAssert.StartsWith(
+ "Compute grid functionality is disabled for thin clients",
ex.GetInnermostException().Message);
+ }
+ }
+
+ /// <summary>
+ /// Tests automatic retry with multiple servers.
+ /// </summary>
+ [Test]
+ public void
TestFailoverWithRetryPolicyCompletesOperationWithoutException(
+ [Values(true, false)] bool async,
+ [Values(true, false)] bool partitionAware)
+ {
+ // Start 3 nodes.
+ Func<string, IgniteConfiguration> getConfig = name =>
+ new IgniteConfiguration(TestUtils.GetTestConfiguration(name:
name))
+ {
+ ClientConnectorConfiguration = new
ClientConnectorConfiguration
+ {
+ ThinClientConfiguration = new ThinClientConfiguration
+ {
+ MaxActiveComputeTasksPerConnection = 1
+ }
+ }
+ };
+
+ Ignition.Start(getConfig("0"));
+ Ignition.Start(getConfig("1"));
+ Ignition.Start(getConfig("2"));
+
+ // Connect client.
+ var port = IgniteClientConfiguration.DefaultPort;
+ var cfg = new IgniteClientConfiguration
+ {
+ Endpoints = new[]
+ {
+ "localhost",
+ string.Format("127.0.0.1:{0}..{1}", port + 1, port + 2)
+ },
+ RetryPolicy = new ClientRetryAllPolicy(),
+ RetryLimit = 3,
+ EnablePartitionAwareness = partitionAware
+ };
+
+ // ReSharper disable AccessToDisposedClosure
+ using (var client = Ignition.StartClient(cfg))
+ {
+ var cache = client.GetOrCreateCache<int, int>("c");
+
+ // Check all DoOp overloads.
+ Action checkOperation = partitionAware
+ ? async
+ ? (Action)(() =>
Assert.IsFalse(cache.ContainsKeyAsync(1).Result))
+ : () => Assert.IsFalse(cache.ContainsKey(1))
+ : async
+ ? (Action)(() =>
Assert.IsNotNull(client.GetCompute().ExecuteJavaTaskAsync<object>(
+ ComputeClientTests.TestTask, null).Result))
+ : () => Assert.AreEqual(1,
client.GetCacheNames().Count);
+
+ checkOperation();
+
+ // Stop first node.
+ var nodeId = ((IPEndPoint) client.RemoteEndPoint).Port - port;
+ Ignition.Stop(nodeId.ToString(), true);
+
+ checkOperation();
+
+ // Stop second node.
+ nodeId = ((IPEndPoint) client.RemoteEndPoint).Port - port;
+ Ignition.Stop(nodeId.ToString(), true);
+
+ checkOperation();
+
+ // Stop all nodes.
+ Ignition.StopAll(true);
+
+ Assert.IsNotNull(GetSocketException(Assert.Catch(() =>
client.GetCacheNames())));
+ Assert.IsNotNull(GetSocketException(Assert.Catch(() =>
client.GetCacheNames())));
+ }
+ }
+
+ /// <summary>
+ /// Tests custom retry policy.
+ /// </summary>
+ [Test]
+ public void TestCustomRetryPolicyIsInvokedWithCorrectContext()
+ {
+ Ignition.Start(TestUtils.GetTestConfiguration());
+
+ var retryPolicy = new
TestRetryPolicy(ClientOperationType.CacheGetNames);
+
+ var cfg = new IgniteClientConfiguration
+ {
+ Endpoints = new[] { "127.0.0.1" },
+ RetryPolicy = retryPolicy,
+ RetryLimit = 2
+ };
+
+ using (var client = Ignition.StartClient(cfg))
+ {
+ Assert.AreEqual(0, client.GetCacheNames().Count);
+
+ Ignition.StopAll(true);
+
+ var errorWithoutRetry = GetSocketException(Assert.Catch(() =>
client.GetCluster().GetNodes()));
+ var errorWithRetry = Assert.Throws<IgniteClientException>(()
=> client.GetCacheNames());
+
+ Assert.IsNotNull(errorWithoutRetry);
+ StringAssert.StartsWith("Operation failed after 2 retries",
errorWithRetry.Message);
+
+ Assert.AreEqual(3, retryPolicy.Invocations.Count);
+
+ Assert.AreEqual(ClientOperationType.ClusterGroupGetNodes,
retryPolicy.Invocations[0].Operation);
+ Assert.AreEqual(0, retryPolicy.Invocations[0].Iteration);
+ Assert.AreSame(retryPolicy,
retryPolicy.Invocations[0].Configuration.RetryPolicy);
+ Assert.AreEqual(2,
retryPolicy.Invocations[0].Configuration.RetryLimit);
+
Assert.IsInstanceOf<SocketException>(retryPolicy.Invocations[0].Exception.GetBaseException());
+
+ Assert.AreEqual(ClientOperationType.CacheGetNames,
retryPolicy.Invocations[1].Operation);
+ Assert.AreEqual(0, retryPolicy.Invocations[1].Iteration);
+
Assert.IsInstanceOf<SocketException>(retryPolicy.Invocations[1].Exception.GetBaseException());
+
+ Assert.AreEqual(ClientOperationType.CacheGetNames,
retryPolicy.Invocations[2].Operation);
+ Assert.AreEqual(1, retryPolicy.Invocations[2].Iteration);
+
Assert.IsInstanceOf<SocketException>(retryPolicy.Invocations[2].Exception.GetBaseException());
+ }
+ }
+
+ /// <summary>
/// Tests that client stops it's receiver thread upon disposal.
/// </summary>
[Test]
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compatibility/ClientProtocolCompatibilityTest.cs
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compatibility/ClientProtocolCompatibilityTest.cs
index 6fa058c..e6ca434 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compatibility/ClientProtocolCompatibilityTest.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compatibility/ClientProtocolCompatibilityTest.cs
@@ -201,7 +201,7 @@ namespace Apache.Ignite.Core.Tests.Client.Compatibility
/// </summary>
internal static void AssertNotSupportedFeatureOperation(Action action,
ClientBitmaskFeature feature, ClientOp op)
{
- var ex = Assert.Throws<IgniteClientException>(() => action());
+ var ex = Assert.Catch<Exception>(() =>
action()).GetBaseException();
var expectedMessage = string.Format(
"Operation {0} is not supported by the server. " +
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compute/ComputeClientTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compute/ComputeClientTests.cs
index adce7b0..3efdd46 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compute/ComputeClientTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/Compute/ComputeClientTests.cs
@@ -40,7 +40,7 @@ namespace Apache.Ignite.Core.Tests.Client.Compute
public class ComputeClientTests : ClientTestBase
{
/** */
- private const string TestTask =
"org.apache.ignite.internal.client.thin.TestTask";
+ public const string TestTask =
"org.apache.ignite.internal.client.thin.TestTask";
/** */
private const string TestResultCacheTask =
"org.apache.ignite.internal.client.thin.TestResultCacheTask";
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs
index 1aacaf2..3a09e4b 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/IgniteClientConfigurationTest.cs
@@ -300,7 +300,6 @@ namespace Apache.Ignite.Core.Tests.Client
}
}
-#if !NETCOREAPP
/// <summary>
/// Tests the schema validation.
/// </summary>
@@ -324,7 +323,6 @@ namespace Apache.Ignite.Core.Tests.Client
"IgniteClientConfigurationSection.xsd",
"igniteClientConfiguration",
typeof(IgniteClientConfiguration));
}
-#endif
/// <summary>
/// Tests <see cref="TransactionClientConfiguration"/> copy ctor.
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/TestRetryPolicy.cs
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/TestRetryPolicy.cs
new file mode 100644
index 0000000..5f0c75d
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/TestRetryPolicy.cs
@@ -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.
+ */
+
+namespace Apache.Ignite.Core.Tests.Client
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using Apache.Ignite.Core.Client;
+
+ /// <summary>
+ /// Test policy.
+ /// </summary>
+ public class TestRetryPolicy : IClientRetryPolicy
+ {
+ /** */
+ private readonly IReadOnlyCollection<ClientOperationType>
_allowedOperations;
+
+ /** */
+ private readonly List<IClientRetryPolicyContext> _invocations = new
List<IClientRetryPolicyContext>();
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="TestRetryPolicy"/> class.
+ /// </summary>
+ /// <param name="allowedOperations">A list of operation types to
retry.</param>
+ public TestRetryPolicy(params ClientOperationType[] allowedOperations)
+ {
+ _allowedOperations = allowedOperations.ToArray();
+ }
+
+ /// <summary>
+ /// Gets the invocations.
+ /// </summary>
+ public IReadOnlyList<IClientRetryPolicyContext> Invocations =>
_invocations;
+
+ /** <inheritDoc /> */
+ public bool ShouldRetry(IClientRetryPolicyContext context)
+ {
+ _invocations.Add(context);
+
+ return _allowedOperations.Contains(context.Operation);
+ }
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientOperationType.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientOperationType.cs
new file mode 100644
index 0000000..51aeb70
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientOperationType.cs
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Client
+{
+ using Apache.Ignite.Core.Client.Cache;
+ using Apache.Ignite.Core.Client.Compute;
+ using Apache.Ignite.Core.Client.Services;
+ using Apache.Ignite.Core.Client.Transactions;
+
+ /// <summary>
+ /// Client operation type.
+ /// </summary>
+ public enum ClientOperationType
+ {
+ /// <summary>
+ /// Create cache <see
cref="IIgniteClient.CreateCache{TK,TV}(string)"/>,
+ /// <see
cref="IIgniteClient.CreateCache{TK,TV}(CacheClientConfiguration)"/>.
+ /// </summary>
+ CacheCreate,
+
+ /// <summary>
+ /// Get or create cache <see
cref="IIgniteClient.GetOrCreateCache{TK,TV}(string)"/>,
+ /// <see
cref="IIgniteClient.GetOrCreateCache{TK,TV}(CacheClientConfiguration)"/>.
+ /// </summary>
+ CacheGetOrCreate,
+
+ /// <summary>
+ /// Get cache names <see cref="IIgniteClient.GetCacheNames"/>.
+ /// </summary>
+ CacheGetNames,
+
+ /// <summary>
+ /// Destroy cache <see cref="IIgniteClient.DestroyCache"/>.
+ /// </summary>
+ CacheDestroy,
+
+ /// <summary>
+ /// Get value from cache <see cref="ICacheClient{TK,TV}.Get"/>.
+ /// </summary>
+ CacheGet,
+
+ /// <summary>
+ /// Put value to cache <see cref="ICacheClient{TK,TV}.Put"/>.
+ /// </summary>
+ CachePut,
+
+ /// <summary>
+ /// Determines if the cache contains a key <see
cref="ICacheClient{TK,TV}.Put"/>.
+ /// </summary>
+ CacheContainsKey,
+
+ /// <summary>
+ /// Determines if the cache contains multiple keys (<see
cref="ICacheClient{TK,TV}.ContainsKeys"/>).
+ /// </summary>
+ CacheContainsKeys,
+
+ /// <summary>
+ /// Get cache configuration (<see
cref="ICacheClient{TK,TV}.GetConfiguration"/>).
+ /// </summary>
+ CacheGetConfiguration,
+
+ /// <summary>
+ /// Get cache size (<see cref="ICacheClient{TK,TV}.GetSize"/>).
+ /// </summary>
+ CacheGetSize,
+
+ /// <summary>
+ /// Put values to cache (<see cref="ICacheClient{TK,TV}.PutAll"/>).
+ /// </summary>
+ CachePutAll,
+
+ /// <summary>
+ /// Get values from cache (<see cref="ICacheClient{TK,TV}.GetAll"/>).
+ /// </summary>
+ CacheGetAll,
+
+ /// <summary>
+ /// Replace cache value (<see
cref="ICacheClient{TK,TV}.Replace(TK,TV)"/>,
+ /// <see cref="ICacheClient{TK,TV}.Replace(TK,TV,TV)"/>).
+ /// </summary>
+ CacheReplace,
+
+ /// <summary>
+ /// Remove entry from cache (<see
cref="ICacheClient{TK,TV}.Remove(TK)" />,
+ /// <see cref="ICacheClient{TK,TV}.Remove(TK,TV)"/>).
+ /// </summary>
+ CacheRemoveOne,
+
+ /// <summary>
+ /// Remove entries from cache (<see
cref="ICacheClient{TK,TV}.RemoveAll(System.Collections.Generic.IEnumerable{TK})"/>).
+ /// </summary>
+ CacheRemoveMultiple,
+
+ /// <summary>
+ /// Remove everything from cache (<see
cref="ICacheClient{TK,TV}.RemoveAll()"/>).
+ /// </summary>
+ CacheRemoveEverything,
+
+ /// <summary>
+ /// Clear cache entry (<see cref="ICacheClient{TK,TV}.Clear(TK)"/>).
+ /// </summary>
+ CacheClearOne,
+
+ /// <summary>
+ /// Clear multiple cache entries (<see
cref="ICacheClient{TK,TV}.ClearAll"/>).
+ /// </summary>
+ CacheClearMultiple,
+
+ /// <summary>
+ /// Clear entire cache (<see cref="ICacheClient{TK,TV}.Clear()"/>).
+ /// </summary>
+ CacheClearEverything,
+
+ /// <summary>
+ /// Get and put (<see cref="ICacheClient{TK,TV}.GetAndPut(TK, TV)"/>).
+ /// </summary>
+ CacheGetAndPut,
+
+ /// <summary>
+ /// Get and remove (<see
cref="ICacheClient{TK,TV}.GetAndRemove(TK)"/>).
+ /// </summary>
+ CacheGetAndRemove,
+
+ /// <summary>
+ /// Get and replace (<see cref="ICacheClient{TK,TV}.GetAndReplace(TK,
TV)"/>).
+ /// </summary>
+ CacheGetAndReplace,
+
+ /// <summary>
+ /// Put if absent (<see cref="ICacheClient{TK,TV}.PutIfAbsent(TK,
TV)"/>).
+ /// </summary>
+ CachePutIfAbsent,
+
+ /// <summary>
+ /// Get and put if absent (<see
cref="ICacheClient{TK,TV}.GetAndPutIfAbsent(TK, TV)"/>).
+ /// </summary>
+ CacheGetAndPutIfAbsent,
+
+ /// <summary>
+ /// Scan query (<see
cref="ICacheClient{TK,TV}.Query(Apache.Ignite.Core.Cache.Query.ScanQuery{TK,TV})"/>).
+ /// </summary>
+ QueryScan,
+
+ /// <summary>
+ /// SQL query (<see
cref="ICacheClient{TK,TV}.Query(Apache.Ignite.Core.Cache.Query.SqlFieldsQuery)"/>).
+ /// </summary>
+ QuerySql,
+
+ /// <summary>
+ /// Continuous query (<see
cref="ICacheClient{TK,TV}.QueryContinuous"/>).
+ /// </summary>
+ QueryContinuous,
+
+ /// <summary>
+ /// Start transaction (<see cref="ITransactionsClient.TxStart()"/>).
+ /// </summary>
+ TransactionStart,
+
+ /// <summary>
+ /// Get cluster state (<see cref="IClientCluster.IsActive"/>).
+ /// </summary>
+ ClusterGetState,
+
+ /// <summary>
+ /// Change cluster state (<see cref="IClientCluster.SetActive"/>).
+ /// </summary>
+ ClusterChangeState,
+
+ /// <summary>
+ /// Get cluster WAL state (<see cref="IClientCluster.IsWalEnabled"/>).
+ /// </summary>
+ ClusterGetWalState,
+
+ /// <summary>
+ /// Change cluster WAL state (<see cref="IClientCluster.EnableWal"/>,
<see cref="IClientCluster.DisableWal"/>).
+ /// </summary>
+ ClusterChangeWalState,
+
+ /// <summary>
+ /// Get cluster nodes (<see cref="IClientClusterGroup.GetNodes"/>).
+ /// </summary>
+ ClusterGroupGetNodes,
+
+ /// <summary>
+ /// Execute compute task (<see
cref="IComputeClient.ExecuteJavaTask{TRes}"/>).
+ /// </summary>
+ ComputeTaskExecute,
+
+ /// <summary>
+ /// Invoke service.
+ /// </summary>
+ ServiceInvoke,
+
+ /// <summary>
+ /// Get service descriptors (<see
cref="IServicesClient.GetServiceDescriptors"/>).
+ /// </summary>
+ ServiceGetDescriptors,
+
+ /// <summary>
+ /// Get service descriptor (<see
cref="IServicesClient.GetServiceDescriptor"/>).
+ /// </summary>
+ ServiceGetDescriptor
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientRetryAllPolicy.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientRetryAllPolicy.cs
new file mode 100644
index 0000000..78c901c
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientRetryAllPolicy.cs
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Client
+{
+ /// <summary>
+ /// Retry policy that always returns <c>true</c>.
+ /// </summary>
+ public class ClientRetryAllPolicy : IClientRetryPolicy
+ {
+ /** <inheritDoc /> */
+ public bool ShouldRetry(IClientRetryPolicyContext context)
+ {
+ return true;
+ }
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientRetryReadPolicy.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientRetryReadPolicy.cs
new file mode 100644
index 0000000..63834ac
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/ClientRetryReadPolicy.cs
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Client
+{
+ using Apache.Ignite.Core.Impl.Common;
+
+ /// <summary>
+ /// Retry policy that returns true for all read-only operations that do
not modify data.
+ /// </summary>
+ public sealed class ClientRetryReadPolicy : IClientRetryPolicy
+ {
+ /** <inheritDoc /> */
+ public bool ShouldRetry(IClientRetryPolicyContext context)
+ {
+ IgniteArgumentCheck.NotNull(context, nameof(context));
+
+ switch (context.Operation)
+ {
+ case ClientOperationType.CacheGetNames:
+ case ClientOperationType.CacheGet:
+ case ClientOperationType.CacheContainsKey:
+ case ClientOperationType.CacheContainsKeys:
+ case ClientOperationType.CacheGetConfiguration:
+ case ClientOperationType.CacheGetSize:
+ case ClientOperationType.CacheGetAll:
+ case ClientOperationType.QueryScan:
+ case ClientOperationType.QueryContinuous:
+ case ClientOperationType.ClusterGetState:
+ case ClientOperationType.ClusterGetWalState:
+ case ClientOperationType.ClusterGroupGetNodes:
+ case ClientOperationType.ServiceGetDescriptors:
+ case ClientOperationType.ServiceGetDescriptor:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IClientRetryPolicy.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IClientRetryPolicy.cs
new file mode 100644
index 0000000..a4e2fd0
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IClientRetryPolicy.cs
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Client
+{
+ /// <summary>
+ /// Client retry policy determines whether client operations that have
failed due to a connection issue
+ /// should be retried.
+ /// </summary>
+ public interface IClientRetryPolicy
+ {
+ /// <summary>
+ /// Gets a value indicating whether a client operation that has failed
due to a connection issue
+ /// should be retried.
+ /// </summary>
+ /// <param name="context">Operation context.</param>
+ /// <returns>
+ /// <c>true</c> if the operation should be retried on another
connection, <c>false</c> otherwise.
+ /// </returns>
+ bool ShouldRetry(IClientRetryPolicyContext context);
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IClientRetryPolicyContext.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IClientRetryPolicyContext.cs
new file mode 100644
index 0000000..692ef1c
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IClientRetryPolicyContext.cs
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Client
+{
+ using System;
+
+ /// <summary>
+ /// Retry policy context. See <see cref="IClientRetryPolicy.ShouldRetry"/>.
+ /// </summary>
+ public interface IClientRetryPolicyContext
+ {
+ /// <summary>
+ /// Gets the client configuration.
+ /// </summary>
+ IgniteClientConfiguration Configuration { get; }
+
+ /// <summary>
+ /// Gets the operation type.
+ /// </summary>
+ ClientOperationType Operation { get; }
+
+ /// <summary>
+ /// Gets the current iteration.
+ /// </summary>
+ int Iteration { get; }
+
+ /// <summary>
+ /// Gets the exception that caused current retry iteration.
+ /// </summary>
+ Exception Exception { get; }
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs
index cb38b72..3692f2f7 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Core/Client/IgniteClientConfiguration.cs
@@ -131,6 +131,9 @@ namespace Apache.Ignite.Core.Client
{
TransactionConfiguration = new
TransactionClientConfiguration(cfg.TransactionConfiguration);
}
+
+ RetryLimit = cfg.RetryLimit;
+ RetryPolicy = cfg.RetryPolicy;
}
/// <summary>
@@ -245,6 +248,25 @@ namespace Apache.Ignite.Core.Client
public TransactionClientConfiguration TransactionConfiguration { get;
set; }
/// <summary>
+ /// Gets or sets the retry policy. When a request fails due to a
connection error,
+ /// Ignite will retry the request if the specified policy allows it.
+ /// <para />
+ /// Default is null: operations won't be retried.
+ /// <para />
+ /// See also <see cref="ClientRetryAllPolicy"/>, <see
cref="ClientRetryReadPolicy"/>, <see cref="RetryLimit"/>.
+ /// </summary>
+ public IClientRetryPolicy RetryPolicy { get; set; }
+
+ /// <summary>
+ /// Gets or sets the retry limit. When a request fails due to a
connection error,
+ /// Ignite will retry the request if the specified <see
cref="RetryPolicy"/> allows it. When this property is
+ /// greater than <c>0</c>, Ignite will limit the number of retries.
+ /// <para />
+ /// Default is <c>0</c>: no limit on retries.
+ /// </summary>
+ public int RetryLimit { get; set; }
+
+ /// <summary>
/// Gets or sets custom binary processor. Internal property for tests.
/// </summary>
internal IBinaryProcessor BinaryProcessor { get; set; }
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
index b610318..4c3e26f 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
+++
b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd
@@ -282,6 +282,18 @@
</xs:attribute>
</xs:complexType>
</xs:element>
+ <xs:element name="retryPolicy" minOccurs="0">
+ <xs:annotation>
+ <xs:documentation>Retry policy for failed
operations.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="type" type="xs:string"
use="required">
+ <xs:annotation>
+ <xs:documentation>Assembly-qualified type
name.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
</xs:all>
<xs:attribute name="host" type="xs:string" use="required">
<xs:annotation>
@@ -333,6 +345,11 @@
<xs:documentation>Password to be used to connect to
secured cluster.</xs:documentation>
</xs:annotation>
</xs:attribute>
+ <xs:attribute name="retryLimit" type="xs:int">
+ <xs:annotation>
+ <xs:documentation>Operation retry limit when RetryPolicy
is set.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs
index d23bd72..263f4f6 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientFailoverSocket.cs
@@ -142,7 +142,23 @@ namespace Apache.Ignite.Core.Impl.Client
Func<ClientResponseContext, T> readFunc,
Func<ClientStatusCode, string, T> errorFunc = null)
{
- return GetSocket().DoOutInOp(opId, writeAction, readFunc,
errorFunc);
+ var attempt = 0;
+ List<Exception> errors = null;
+
+ while (true)
+ {
+ try
+ {
+ return GetSocket().DoOutInOp(opId, writeAction, readFunc,
errorFunc);
+ }
+ catch (Exception e)
+ {
+ if (!HandleOpError(e, opId, ref attempt, ref errors))
+ {
+ throw;
+ }
+ }
+ }
}
/// <summary>
@@ -156,15 +172,31 @@ namespace Apache.Ignite.Core.Impl.Client
TKey key,
Func<ClientStatusCode, string, T> errorFunc = null)
{
- var socket = GetAffinitySocket(cacheId, key) ?? GetSocket();
+ var attempt = 0;
+ List<Exception> errors = null;
- return socket.DoOutInOp(opId, writeAction, readFunc, errorFunc);
+ while (true)
+ {
+ try
+ {
+ var socket = GetAffinitySocket(cacheId, key) ??
GetSocket();
+
+ return socket.DoOutInOp(opId, writeAction, readFunc,
errorFunc);
+ }
+ catch (Exception e)
+ {
+ if (!HandleOpError(e, opId, ref attempt, ref errors))
+ {
+ throw;
+ }
+ }
+ }
}
/// <summary>
/// Performs an async send-receive operation with partition awareness.
/// </summary>
- public Task<T> DoOutInOpAffinityAsync<T, TKey>(
+ public async Task<T> DoOutInOpAffinityAsync<T, TKey>(
ClientOp opId,
Action<ClientRequestContext> writeAction,
Func<ClientResponseContext, T> readFunc,
@@ -172,18 +204,51 @@ namespace Apache.Ignite.Core.Impl.Client
TKey key,
Func<ClientStatusCode, string, T> errorFunc = null)
{
- var socket = GetAffinitySocket(cacheId, key) ?? GetSocket();
+ var attempt = 0;
+ List<Exception> errors = null;
- return socket.DoOutInOpAsync(opId, writeAction, readFunc,
errorFunc);
+ while (true)
+ {
+ try
+ {
+ var socket = GetAffinitySocket(cacheId, key) ??
GetSocket();
+
+ return await socket.DoOutInOpAsync(opId, writeAction,
readFunc, errorFunc).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ if (!HandleOpError(e, opId, ref attempt, ref errors))
+ {
+ throw;
+ }
+ }
+ }
}
/// <summary>
/// Performs an async send-receive operation.
/// </summary>
- public Task<T> DoOutInOpAsync<T>(ClientOp opId,
Action<ClientRequestContext> writeAction,
+ public async Task<T> DoOutInOpAsync<T>(ClientOp opId,
Action<ClientRequestContext> writeAction,
Func<ClientResponseContext, T> readFunc, Func<ClientStatusCode,
string, T> errorFunc = null)
{
- return GetSocket().DoOutInOpAsync(opId, writeAction, readFunc,
errorFunc);
+ var attempt = 0;
+ List<Exception> errors = null;
+
+ while (true)
+ {
+ try
+ {
+ return await GetSocket().DoOutInOpAsync(opId, writeAction,
readFunc, errorFunc)
+ .ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ if (!HandleOpError(e, opId, ref attempt, ref errors))
+ {
+ throw;
+ }
+ }
+ }
}
/// <summary>
@@ -338,7 +403,7 @@ namespace Apache.Ignite.Core.Impl.Client
Justification = "There is no finalizer.")]
public void Dispose()
{
- // Lock order: same as in OnAffinityTopologyVersionChange.
+ // Lock order: same as in OnAffinityTopologyVersionChange.
lock (_topologyUpdateLock)
lock (_socketLock)
{
@@ -924,5 +989,94 @@ namespace Apache.Ignite.Core.Impl.Client
? BinaryNameMapperMode.BasicSimple
: BinaryNameMapperMode.BasicFull;
}
+
+ /// <summary>
+ /// Gets a value indicating whether a failed operation should be
retried.
+ /// </summary>
+ /// <param name="exception">Exception that caused the operation to
fail.</param>
+ /// <param name="op">Operation code.</param>
+ /// <param name="attempt">Current attempt.</param>
+ /// <returns>
+ /// <c>true</c> if the operation should be retried on another
connection, <c>false</c> otherwise.
+ /// </returns>
+ private bool ShouldRetry(Exception exception, ClientOp op, int attempt)
+ {
+ var e = exception;
+
+ while (e != null && !(e is SocketException))
+ {
+ e = e.InnerException;
+ }
+
+ if (e == null)
+ {
+ // Only retry socket exceptions.
+ return false;
+ }
+
+ if (_config.RetryPolicy == null)
+ {
+ return false;
+ }
+
+ if (_config.RetryLimit > 0 && attempt >= _config.RetryLimit)
+ {
+ return false;
+ }
+
+ var publicOpType = op.ToPublicOperationsType();
+
+ if (publicOpType == null)
+ {
+ // System operation.
+ return true;
+ }
+
+ var ctx = new ClientRetryPolicyContext(_config,
publicOpType.Value, attempt, exception);
+
+ return _config.RetryPolicy.ShouldRetry(ctx);
+ }
+
+ /// <summary>
+ /// Handles operation error.
+ /// </summary>
+ /// <param name="exception">Error.</param>
+ /// <param name="op">Operation code.</param>
+ /// <param name="attempt">Current attempt.</param>
+ /// <param name="errors">Previous errors.</param>
+ /// <returns>True if the error was handled, false otherwise.</returns>
+ private bool HandleOpError(
+ Exception exception,
+ ClientOp op,
+ ref int attempt,
+ ref List<Exception> errors)
+ {
+ if (!ShouldRetry(exception, op, attempt))
+ {
+ if (errors == null)
+ {
+ return false;
+ }
+
+ var inner = new AggregateException(errors);
+
+ throw new IgniteClientException(
+ $"Operation failed after {attempt} retries, examine
InnerException for details.",
+ inner);
+ }
+
+ if (errors == null)
+ {
+ errors = new List<Exception> { exception };
+ }
+ else
+ {
+ errors.Add(exception);
+ }
+
+ attempt++;
+
+ return true;
+ }
}
}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOpExtensions.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOpExtensions.cs
new file mode 100644
index 0000000..91357cc
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientOpExtensions.cs
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Client
+{
+ using Apache.Ignite.Core.Client;
+
+ /// <summary>
+ /// Extensions for <see cref="ClientOp"/>.
+ /// </summary>
+ internal static class ClientOpExtensions
+ {
+ /// <summary>
+ /// Converts the internal op code to a public operation type.
+ /// </summary>
+ /// <param name="op">Operation code.</param>
+ /// <returns>Operation type.</returns>
+ public static ClientOperationType? ToPublicOperationsType(this
ClientOp op)
+ {
+ switch (op)
+ {
+ case ClientOp.CacheGetOrCreateWithName:
+ case ClientOp.CacheGetOrCreateWithConfiguration:
+ return ClientOperationType.CacheGetOrCreate;
+
+ case ClientOp.CacheCreateWithConfiguration:
+ case ClientOp.CacheCreateWithName:
+ return ClientOperationType.CacheCreate;
+
+ case ClientOp.CachePut:
+ return ClientOperationType.CachePut;
+
+ case ClientOp.CacheGet:
+ return ClientOperationType.CacheGet;
+
+ case ClientOp.CacheGetNames:
+ return ClientOperationType.CacheGetNames;
+
+ case ClientOp.CacheDestroy:
+ return ClientOperationType.CacheDestroy;
+
+ case ClientOp.CacheContainsKey:
+ return ClientOperationType.CacheContainsKey;
+
+ case ClientOp.CacheContainsKeys:
+ return ClientOperationType.CacheContainsKeys;
+
+ case ClientOp.CacheGetConfiguration:
+ return ClientOperationType.CacheGetConfiguration;
+
+ case ClientOp.CacheGetSize:
+ return ClientOperationType.CacheGetSize;
+
+ case ClientOp.CachePutAll:
+ return ClientOperationType.CachePutAll;
+
+ case ClientOp.CacheGetAll:
+ return ClientOperationType.CacheGetAll;
+
+ case ClientOp.CacheReplaceIfEquals:
+ case ClientOp.CacheReplace:
+ return ClientOperationType.CacheReplace;
+
+ case ClientOp.CacheRemoveKey:
+ case ClientOp.CacheRemoveIfEquals:
+ return ClientOperationType.CacheRemoveOne;
+
+ case ClientOp.CacheRemoveKeys:
+ return ClientOperationType.CacheRemoveMultiple;
+
+ case ClientOp.CacheRemoveAll:
+ return ClientOperationType.CacheRemoveEverything;
+
+ case ClientOp.CacheGetAndPut:
+ return ClientOperationType.CacheGetAndPut;
+
+ case ClientOp.CacheGetAndRemove:
+ return ClientOperationType.CacheGetAndRemove;
+
+ case ClientOp.CacheGetAndReplace:
+ return ClientOperationType.CacheGetAndReplace;
+
+ case ClientOp.CachePutIfAbsent:
+ return ClientOperationType.CachePutIfAbsent;
+
+ case ClientOp.CacheGetAndPutIfAbsent:
+ return ClientOperationType.CacheGetAndPutIfAbsent;
+
+ case ClientOp.CacheClear:
+ return ClientOperationType.CacheClearEverything;
+
+ case ClientOp.CacheClearKey:
+ return ClientOperationType.CacheClearOne;
+
+ case ClientOp.CacheClearKeys:
+ return ClientOperationType.CacheClearMultiple;
+
+ case ClientOp.QueryScan:
+ return ClientOperationType.QueryScan;
+
+ case ClientOp.QuerySql:
+ case ClientOp.QuerySqlFields:
+ return ClientOperationType.QuerySql;
+
+ case ClientOp.QueryContinuous:
+ return ClientOperationType.QueryContinuous;
+
+ case ClientOp.TxStart:
+ return ClientOperationType.TransactionStart;
+
+ case ClientOp.ClusterIsActive:
+ return ClientOperationType.ClusterGetState;
+
+ case ClientOp.ClusterChangeState:
+ return ClientOperationType.ClusterChangeState;
+
+ case ClientOp.ClusterGetWalState:
+ return ClientOperationType.ClusterGetWalState;
+
+ case ClientOp.ClusterChangeWalState:
+ return ClientOperationType.ClusterChangeWalState;
+
+ case ClientOp.ClusterGroupGetNodeIds:
+ case ClientOp.ClusterGroupGetNodesInfo:
+ return ClientOperationType.ClusterGroupGetNodes;
+
+ case ClientOp.ComputeTaskExecute:
+ return ClientOperationType.ComputeTaskExecute;
+
+ case ClientOp.ServiceInvoke:
+ return ClientOperationType.ServiceInvoke;
+
+ case ClientOp.ServiceGetDescriptors:
+ return ClientOperationType.ServiceGetDescriptors;
+
+ case ClientOp.ServiceGetDescriptor:
+ return ClientOperationType.ServiceGetDescriptor;
+
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientRetryPolicyContext.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientRetryPolicyContext.cs
new file mode 100644
index 0000000..a1463df
--- /dev/null
+++
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Client/ClientRetryPolicyContext.cs
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Client
+{
+ using System;
+ using Apache.Ignite.Core.Client;
+
+ /// <summary>
+ /// Retry policy context.
+ /// </summary>
+ internal sealed class ClientRetryPolicyContext : IClientRetryPolicyContext
+ {
+ /// <summary>
+ /// Initializes a new instance of <see
cref="ClientRetryPolicyContext"/> class.
+ /// </summary>
+ /// <param name="configuration">Configuration.</param>
+ /// <param name="operation">Operation.</param>
+ /// <param name="iteration">Iteration.</param>
+ /// <param name="exception">Exception.</param>
+ public ClientRetryPolicyContext(
+ IgniteClientConfiguration configuration,
+ ClientOperationType operation,
+ int iteration,
+ Exception exception)
+ {
+ Configuration = configuration;
+ Operation = operation;
+ Iteration = iteration;
+ Exception = exception;
+ }
+
+ /** <inheritDoc /> */
+ public IgniteClientConfiguration Configuration { get; }
+
+ /** <inheritDoc /> */
+ public ClientOperationType Operation { get; }
+
+ /** <inheritDoc /> */
+ public int Iteration { get; }
+
+ /** <inheritDoc /> */
+ public Exception Exception { get; }
+ }
+}
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
index c957d93..e5d4c34 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/TypeCaster.cs
@@ -18,13 +18,11 @@
namespace Apache.Ignite.Core.Impl.Common
{
using System;
- using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
- using System.Reflection;
/// <summary>
- /// Does type casts without extra boxing.
+ /// Does type casts without extra boxing.
/// Should be used when casting compile-time incompatible value types
instead of "(T)(object)x".
/// </summary>
/// <typeparam name="T">Target type</typeparam>
@@ -64,7 +62,7 @@ namespace Apache.Ignite.Core.Impl.Common
/// <summary>
/// Compiled caster delegate.
/// </summary>
- [SuppressMessage("Microsoft.Performance",
"CA1823:AvoidUnusedPrivateFields",
+ [SuppressMessage("Microsoft.Performance",
"CA1823:AvoidUnusedPrivateFields",
Justification = "Incorrect warning")]
[SuppressMessage("Microsoft.Design",
"CA1000:DoNotDeclareStaticMembersOnGenericTypes",
Justification = "Intended usage to leverage compiler
caching.")]
@@ -125,4 +123,4 @@ namespace Apache.Ignite.Core.Impl.Common
}
}
}
-}
\ No newline at end of file
+}