blob: 7c7201886a04f3f069d29122dbd64e8d16175a9c [file] [log] [blame]
/*
* 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.Tests
{
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Ignite.Table;
using NUnit.Framework;
/// <summary>
/// Tests client behavior with different <see cref="IgniteClientConfiguration.RetryPolicy"/> settings.
/// </summary>
public class RetryPolicyTests
{
private const int IterCount = 100;
[Test]
public async Task TestFailoverWithRetryPolicyCompletesOperationWithoutException()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = new RetryLimitPolicy { RetryLimit = 1 }
};
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
for (int i = 0; i < IterCount; i++)
{
await client.Tables.GetTablesAsync();
}
}
[Test]
public async Task TestFailoverWithRetryPolicyDoesNotRetryUnrelatedErrors()
{
var cfg = new IgniteClientConfiguration { RetryPolicy = new RetryLimitPolicy() };
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
var ex = Assert.ThrowsAsync<IgniteException>(async () => await client.Tables.GetTableAsync("bad-table"));
StringAssert.Contains(FakeServer.Err, ex!.Message);
}
[Test]
public async Task TestFailoverWithRetryPolicyDoesNotRetryTxCommit()
{
var testRetryPolicy = new TestRetryPolicy();
var cfg = new IgniteClientConfiguration { RetryPolicy = testRetryPolicy };
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
var tx = await client.Transactions.BeginAsync();
await TestUtils.ForceLazyTxStart(tx, client);
Assert.ThrowsAsync<IgniteClientConnectionException>(async () => await tx.CommitAsync());
Assert.IsEmpty(testRetryPolicy.Invocations);
}
[Test]
public async Task TestFailoverWithRetryPolicyThrowsOnRetryLimitExceeded()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = new TestRetryPolicy { RetryLimit = 5 }
};
using var server = new FakeServer(ctx => ctx.RequestCount > 1);
using var client = await server.ConnectClientAsync(cfg);
await client.Tables.GetTablesAsync();
var ex = Assert.ThrowsAsync<IgniteClientConnectionException>(async () => await client.Tables.GetTablesAsync());
Assert.AreEqual("Operation TablesGet failed after 5 retries, examine InnerException for details.", ex!.Message);
}
[Test]
public async Task TestFailoverWithRetryPolicyThrowsOnDefaultRetryLimitExceeded()
{
using var server = new FakeServer(ctx => ctx.RequestCount > 1);
using var client = await server.ConnectClientAsync();
await client.Tables.GetTablesAsync();
var ex = Assert.ThrowsAsync<IgniteClientConnectionException>(async () => await client.Tables.GetTablesAsync());
Assert.AreEqual("Operation TablesGet failed after 16 retries, examine InnerException for details.", ex!.Message);
}
[Test]
public async Task TestZeroRetryLimitDoesNotLimitRetryCount()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = new RetryLimitPolicy
{
RetryLimit = 0
}
};
using var server = new FakeServer(ctx => ctx.RequestCount % 30 != 0);
using var client = await server.ConnectClientAsync(cfg);
for (var i = 0; i < IterCount; i++)
{
await client.Tables.GetTablesAsync();
}
}
[Test]
public async Task TestRetryPolicyIsDisabledByDefault()
{
using var server = new FakeServer(ctx => ctx.RequestCount > 1);
using var client = await server.ConnectClientAsync();
await client.Tables.GetTablesAsync();
Assert.ThrowsAsync<IgniteClientConnectionException>(async () => await client.Tables.GetTablesAsync());
}
[Test]
public async Task TestNullRetryPolicyIsSameAsNoRetry()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = null!
};
using var server = new FakeServer(ctx => ctx.RequestCount > 1);
using var client = await server.ConnectClientAsync(cfg);
await client.Tables.GetTablesAsync();
Assert.ThrowsAsync<IgniteClientConnectionException>(async () => await client.Tables.GetTablesAsync());
}
[Test]
public async Task TestCustomRetryPolicyIsInvokedWithCorrectContext()
{
var testRetryPolicy = new TestRetryPolicy { RetryLimit = 3 };
var cfg = new IgniteClientConfiguration
{
RetryPolicy = testRetryPolicy
};
using var server = new FakeServer(ctx => ctx.RequestCount % 3 == 0);
using var client = await server.ConnectClientAsync(cfg);
for (var i = 0; i < IterCount; i++)
{
await client.Tables.GetTablesAsync();
}
Assert.AreEqual(49, testRetryPolicy.Invocations.Count);
var inv = testRetryPolicy.Invocations[0];
Assert.AreNotSame(cfg, inv.Configuration);
Assert.AreSame(testRetryPolicy, inv.Configuration.RetryPolicy);
Assert.AreEqual(ClientOperationType.TablesGet, inv.Operation);
Assert.AreEqual(0, inv.Iteration);
}
[Test]
public async Task TestExceptionInRetryPolicyPropagatesToCaller()
{
var testRetryPolicy = new TestRetryPolicy { ShouldThrow = true };
var cfg = new IgniteClientConfiguration
{
RetryPolicy = testRetryPolicy
};
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
await client.Tables.GetTablesAsync();
var ex = Assert.ThrowsAsync<DataException>(async () => await client.Tables.GetTablesAsync());
Assert.AreEqual("Error in TestRetryPolicy.", ex!.Message);
}
[Test]
public async Task TestTableOperationWithoutTxIsRetried()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = new TestRetryPolicy { RetryLimit = 1 }
};
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
for (int i = 0; i < IterCount; i++)
{
var table = await client.Tables.GetTableAsync(FakeServer.ExistingTableName);
await table!.RecordBinaryView.UpsertAsync(null, new IgniteTuple { ["ID"] = 1 });
}
}
[Test]
public async Task TestTableOperationWithTxIsNotRetried()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = new TestRetryPolicy()
};
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
var tx = await client.Transactions.BeginAsync();
await TestUtils.ForceLazyTxStart(tx, client);
var table = await client.Tables.GetTableAsync(FakeServer.ExistingTableName);
var ex = Assert.ThrowsAsync<IgniteClientConnectionException>(
async () => await table!.RecordBinaryView.UpsertAsync(tx, new IgniteTuple { ["ID"] = 1 }));
StringAssert.StartsWith("Socket is closed due to an error", ex!.Message);
}
[Test]
public async Task TestRetryOperationWithPayloadReusesPooledBufferCorrectly()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = new TestRetryPolicy { RetryLimit = 1 }
};
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
for (int i = 0; i < IterCount; i++)
{
var table = await client.Tables.GetTableAsync(FakeServer.ExistingTableName);
Assert.IsNotNull(table);
}
}
[Test]
public async Task TestRetryReadPolicyDoesNotRetryWriteOperations()
{
var cfg = new IgniteClientConfiguration
{
RetryPolicy = new RetryReadPolicy()
};
using var server = new FakeServer(ctx => ctx.RequestCount % 2 == 0);
using var client = await server.ConnectClientAsync(cfg);
var table = await client.Tables.GetTableAsync(FakeServer.ExistingTableName);
Assert.ThrowsAsync<IgniteClientConnectionException>(
async () => await table!.RecordBinaryView.UpsertAsync(null, new IgniteTuple { ["ID"] = 1 }));
}
[Test]
public void TestToString()
{
Assert.AreEqual("RetryReadPolicy { RetryLimit = 3 }", new RetryReadPolicy { RetryLimit = 3 }.ToString());
Assert.AreEqual("RetryLimitPolicy { RetryLimit = 4 }", new RetryLimitPolicy { RetryLimit = 4 }.ToString());
Assert.AreEqual("TestRetryPolicy { RetryLimit = 5 }", new TestRetryPolicy { RetryLimit = 5 }.ToString());
Assert.AreEqual("RetryNonePolicy { }", new RetryNonePolicy().ToString());
}
private class TestRetryPolicy : RetryLimitPolicy
{
private readonly List<IRetryPolicyContext> _invocations = new();
public IReadOnlyList<IRetryPolicyContext> Invocations => _invocations;
public bool ShouldThrow { get; set; }
public override bool ShouldRetry(IRetryPolicyContext context)
{
_invocations.Add(context);
if (ShouldThrow)
{
throw new DataException("Error in TestRetryPolicy.");
}
return base.ShouldRetry(context);
}
}
}
}