Skip to content

Commit 736160d

Browse files
committed
#802 added additional checks if hard exceptions occur in IConnection (not only faulted tasks, synchronous version was ok but async version did not failover properly
1 parent c3e8349 commit 736160d

File tree

3 files changed

+98
-14
lines changed

3 files changed

+98
-14
lines changed

Diff for: src/Elasticsearch.Net/Connection/Transport.cs

+21-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Net;
77
using System.Security.Cryptography;
88
using System.Security.Cryptography.X509Certificates;
9+
using System.Text.RegularExpressions;
910
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Elasticsearch.Net.Connection.Configuration;
@@ -89,15 +90,24 @@ private Task<bool> PingAsync(ITransportRequestState requestState)
8990
RequestTimeout = pingTimeout
9091
};
9192
var rq = requestState.InitiateRequest(RequestType.Ping);
92-
return this.Connection.Head(requestState.CreatePathOnCurrentNode(""), requestOverrides)
93-
.ContinueWith(t =>
94-
{
95-
rq.Finish(t.Result.Success, t.Result.HttpStatusCode);
96-
rq.Dispose();
97-
var response = t.Result;
98-
using (response.Response)
99-
return response.Success;
100-
});
93+
try
94+
{
95+
return this.Connection.Head(requestState.CreatePathOnCurrentNode(""), requestOverrides)
96+
.ContinueWith(t =>
97+
{
98+
rq.Finish(t.Result.Success, t.Result.HttpStatusCode);
99+
rq.Dispose();
100+
var response = t.Result;
101+
using (response.Response)
102+
return response.Success;
103+
});
104+
}
105+
catch (Exception e)
106+
{
107+
var tcs = new TaskCompletionSource<bool>();
108+
tcs.SetException(e);
109+
return tcs.Task;
110+
}
101111
}
102112

103113
private IList<Uri> Sniff(ITransportRequestState ownerState = null)
@@ -403,14 +413,14 @@ private Task<ElasticsearchResponse<T>> DoRequestAsync<T>(TransportRequestState<T
403413
return this.PingAsync(requestState)
404414
.ContinueWith(t =>
405415
{
416+
if (t.IsFaulted)
417+
return this.RetryRequestAsync(requestState, t.Exception);
406418
if (t.IsCompleted)
407419
{
408420
if (!t.Result)
409421
return this.RetryRequestAsync(requestState, t.Exception);
410422
return this.FinishOrRetryRequestAsync(requestState);
411423
}
412-
if (t.IsFaulted)
413-
return this.RetryRequestAsync(requestState, t.Exception);
414424
return null;
415425
}).Unwrap();
416426
}

Diff for: src/Tests/Elasticsearch.Net.Tests.Unit/Connection/RetryTests.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class RetryTests
2222
.MaximumRetries(_retries);
2323

2424
[Test]
25-
public void ThrowsOutOfNodesException_AndRetriesTheSpecifiedTimes()
25+
public void ThrowsMaxRetryException_AndRetriesTheSpecifiedTimes()
2626
{
2727
using (var fake = new AutoFake(callsDoNothing: true))
2828
{
@@ -43,7 +43,7 @@ public void ThrowsOutOfNodesException_AndRetriesTheSpecifiedTimes()
4343
}
4444

4545
[Test]
46-
public void ThrowsOutOfNodesException_AndRetriesTheSpecifiedTimes_Async()
46+
public void ThrowsMaxRetryException_AndRetriesTheSpecifiedTimes_Async()
4747
{
4848
using (var fake = new AutoFake(callsDoNothing: true))
4949
{
@@ -179,6 +179,6 @@ public void ShouldRetryOn503_Async()
179179
getCall.MustHaveHappened(Repeated.Exactly.Times(_retries + 1));
180180
}
181181
}
182-
182+
183183
}
184184
}

Diff for: src/Tests/Elasticsearch.Net.Tests.Unit/Connection/StaticConnectionPoolRetryTests.cs

+74
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using System.Runtime.InteropServices;
6+
using System.Threading.Tasks;
57
using Autofac;
68
using Autofac.Extras.FakeItEasy;
79
using Elasticsearch.Net.Connection;
@@ -372,5 +374,77 @@ public void IfAllButOneConnectionDiesSubsequentRequestsMustUseTheOneAliveConnect
372374
markLastAlive.MustHaveHappened(Repeated.Exactly.Times(4));
373375
}
374376
}
377+
378+
[Test]
379+
public void ShouldRetryOnPingConnectionException_Async()
380+
{
381+
using (var fake = new AutoFake(callsDoNothing: true))
382+
{
383+
var connectionPool = new StaticConnectionPool(_uris, randomizeOnStartup: false);
384+
var config = new ConnectionConfiguration(connectionPool);
385+
386+
fake.Provide<IConnectionConfigurationValues>(config);
387+
FakeCalls.ProvideDefaultTransport(fake);
388+
389+
var pingCall = FakeCalls.PingAtConnectionLevelAsync(fake);
390+
var seenPorts = new List<int>();
391+
pingCall.ReturnsLazily((Uri u, IRequestConfiguration c) =>
392+
{
393+
seenPorts.Add(u.Port);
394+
throw new Exception("Something bad happened");
395+
});
396+
397+
var getCall = FakeCalls.GetCall(fake);
398+
getCall.Returns(FakeResponse.OkAsync(config));
399+
400+
var client = fake.Resolve<ElasticsearchClient>();
401+
402+
var e = Assert.Throws<MaxRetryException>(async () => await client.InfoAsync());
403+
pingCall.MustHaveHappened(Repeated.Exactly.Times(_retries + 1));
404+
getCall.MustNotHaveHappened();
405+
406+
//make sure that if a ping throws an exception it wont
407+
//keep retrying to ping the same node but failover to the next
408+
seenPorts.ShouldAllBeEquivalentTo(_uris.Select(u=>u.Port));
409+
var ae = e.InnerException as AggregateException;
410+
ae = ae.Flatten();
411+
ae.InnerException.Message.Should().Be("Something bad happened");
412+
}
413+
}
414+
415+
[Test]
416+
public void ShouldRetryOnPingConnectionException()
417+
{
418+
using (var fake = new AutoFake(callsDoNothing: true))
419+
{
420+
var connectionPool = new StaticConnectionPool(_uris, randomizeOnStartup: false);
421+
var config = new ConnectionConfiguration(connectionPool);
422+
423+
fake.Provide<IConnectionConfigurationValues>(config);
424+
FakeCalls.ProvideDefaultTransport(fake);
425+
426+
var pingCall = FakeCalls.PingAtConnectionLevel(fake);
427+
var seenPorts = new List<int>();
428+
pingCall.ReturnsLazily((Uri u, IRequestConfiguration c) =>
429+
{
430+
seenPorts.Add(u.Port);
431+
throw new Exception("Something bad happened");
432+
});
433+
434+
var getCall = FakeCalls.GetSyncCall(fake);
435+
getCall.Returns(FakeResponse.Ok(config));
436+
437+
var client = fake.Resolve<ElasticsearchClient>();
438+
439+
var e = Assert.Throws<MaxRetryException>(() => client.Info());
440+
pingCall.MustHaveHappened(Repeated.Exactly.Times(_retries + 1));
441+
getCall.MustNotHaveHappened();
442+
443+
//make sure that if a ping throws an exception it wont
444+
//keep retrying to ping the same node but failover to the next
445+
seenPorts.ShouldAllBeEquivalentTo(_uris.Select(u=>u.Port));
446+
e.InnerException.Message.Should().Be("Something bad happened");
447+
}
448+
}
375449
}
376450
}

0 commit comments

Comments
 (0)