Skip to content

Commit aff6212

Browse files
Cleanup product check and add more comments (#5881) (#5883)
Co-authored-by: Steve Gordon <sgordon@hotmail.co.uk>
1 parent e0dc1fd commit aff6212

File tree

4 files changed

+44
-39
lines changed

4 files changed

+44
-39
lines changed

src/Elasticsearch.Net/ConnectionPool/SingleNodeConnectionPool.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ public SingleNodeConnectionPool(Uri uri, IDateTimeProvider dateTimeProvider = nu
2727
/// <inheritdoc />
2828
public IReadOnlyCollection<Node> Nodes { get; }
2929

30+
/// <inheritdoc />
31+
public ProductCheckStatus ProductCheckStatus { get; set; } = ProductCheckStatus.NotChecked;
32+
3033
/// <inheritdoc />
3134
public bool SniffedOnStartup
3235
{
3336
get => true;
3437
set { }
3538
}
3639

37-
/// <inheritdoc />
38-
public ProductCheckStatus ProductCheckStatus { get; set; }
39-
4040
/// <inheritdoc />
4141
public bool SupportsPinging => false;
4242

src/Elasticsearch.Net/ConnectionPool/StaticConnectionPool.cs

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public StaticConnectionPool(IEnumerable<Uri> uris, bool randomize = true, IDateT
1919
: this(uris.Select(uri => new Node(uri)), randomize, dateTimeProvider) { }
2020

2121
public StaticConnectionPool(IEnumerable<Node> nodes, bool randomize = true, IDateTimeProvider dateTimeProvider = null)
22-
: this(nodes, randomize, randomizeSeed: null, dateTimeProvider) { }
22+
: this(nodes, randomize, null, dateTimeProvider) { }
2323

2424
protected StaticConnectionPool(IEnumerable<Node> nodes, bool randomize, int? randomizeSeed = null, IDateTimeProvider dateTimeProvider = null)
2525
{
@@ -39,30 +39,6 @@ protected StaticConnectionPool(IEnumerable<Node> nodes, Func<Node, float> nodeSc
3939
Initialize(nodes, dateTimeProvider);
4040
}
4141

42-
private void Initialize(IEnumerable<Node> nodes, IDateTimeProvider dateTimeProvider)
43-
{
44-
var nodesProvided = nodes?.ToList() ?? throw new ArgumentNullException(nameof(nodes));
45-
nodesProvided.ThrowIfEmpty(nameof(nodes));
46-
DateTimeProvider = dateTimeProvider ?? Net.DateTimeProvider.Default;
47-
48-
string scheme = null;
49-
foreach (var node in nodesProvided)
50-
{
51-
if (scheme == null)
52-
{
53-
scheme = node.Uri.Scheme;
54-
UsingSsl = scheme == "https";
55-
}
56-
else if (scheme != node.Uri.Scheme)
57-
throw new ArgumentException("Trying to instantiate a connection pool with mixed URI Schemes");
58-
}
59-
60-
InternalNodes = SortNodes(nodesProvided)
61-
.DistinctBy(n => n.Uri)
62-
.ToList();
63-
LastUpdate = DateTimeProvider.Now();
64-
}
65-
6642
/// <inheritdoc />
6743
public DateTime LastUpdate { get; protected set; }
6844

@@ -73,10 +49,10 @@ private void Initialize(IEnumerable<Node> nodes, IDateTimeProvider dateTimeProvi
7349
public virtual IReadOnlyCollection<Node> Nodes => InternalNodes;
7450

7551
/// <inheritdoc />
76-
public bool SniffedOnStartup { get; set; }
52+
public ProductCheckStatus ProductCheckStatus { get; set; } = ProductCheckStatus.NotChecked;
7753

7854
/// <inheritdoc />
79-
public ProductCheckStatus ProductCheckStatus { get; set; }
55+
public bool SniffedOnStartup { get; set; }
8056

8157
/// <inheritdoc />
8258
public virtual bool SupportsPinging => true;
@@ -133,6 +109,28 @@ public virtual void Reseed(IEnumerable<Node> nodes) { } //ignored
133109

134110
void IDisposable.Dispose() => DisposeManagedResources();
135111

112+
private void Initialize(IEnumerable<Node> nodes, IDateTimeProvider dateTimeProvider)
113+
{
114+
var nodesProvided = nodes?.ToList() ?? throw new ArgumentNullException(nameof(nodes));
115+
nodesProvided.ThrowIfEmpty(nameof(nodes));
116+
DateTimeProvider = dateTimeProvider ?? Net.DateTimeProvider.Default;
117+
118+
string scheme = null;
119+
foreach (var node in nodesProvided)
120+
if (scheme == null)
121+
{
122+
scheme = node.Uri.Scheme;
123+
UsingSsl = scheme == "https";
124+
}
125+
else if (scheme != node.Uri.Scheme)
126+
throw new ArgumentException("Trying to instantiate a connection pool with mixed URI Schemes");
127+
128+
InternalNodes = SortNodes(nodesProvided)
129+
.DistinctBy(n => n.Uri)
130+
.ToList();
131+
LastUpdate = DateTimeProvider.Now();
132+
}
133+
136134
protected virtual Node RetryInternalNodes(int globalCursor, Action<AuditEvent, Node> audit = null)
137135
{
138136
audit?.Invoke(AuditEvent.AllNodesDead, null);

src/Elasticsearch.Net/Transport/Pipeline/RequestPipeline.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -682,13 +682,19 @@ private bool ApplyProductCheckRules(RootResponse response)
682682
return true;
683683
}
684684

685-
if (response.HttpStatusCode.HasValue && (response.HttpStatusCode.Value == 401 || response.HttpStatusCode.Value == 403))
685+
// We should always have a status code!
686+
var statusCode = response.HttpStatusCode ?? 0;
687+
688+
// The call to the root path requires monitor permissions. If the current use lacks those, we cannot perform product validation.
689+
if (statusCode is 401 or 403)
686690
{
687-
// The call to the root path requires monitor permissions. If the current use lacks those, we cannot perform product validation.
688691
_connectionPool.ProductCheckStatus = ProductCheckStatus.UndeterminedProduct;
689692
return true;
690693
}
691694

695+
// Any response besides a 200, 401 or 403 is considered a failure and the check is considered incomplete remaining in the NotChecked state.
696+
// This means that the check will run on subsequent requests until we have a valid response to evaluate.
697+
// By returning false, the failure to perform the product check will be included in the audit log.
692698
if (!response.Success) return false;
693699

694700
// Start by assuming the product is valid
@@ -715,6 +721,8 @@ private bool ApplyProductCheckRules(RootResponse response)
715721
_connectionPool.ProductCheckStatus = ProductCheckStatus.InvalidProduct;
716722
}
717723

724+
return true;
725+
718726
// Elasticsearch should be version 6.0.0 or greater
719727
// Note: For best compatibility, the client should not be used with versions prior to 7.0.0, but we do not enforce that here
720728
static bool VersionTooLow(Version version)
@@ -741,8 +749,6 @@ static bool Version714InvalidHeader(Version version, string productName)
741749
{
742750
return version >= Version714 && !ExpectedProductName.Equals(productName, StringComparison.Ordinal);
743751
}
744-
745-
return true;
746752
}
747753

748754
private bool PingDisabled(Node node) =>

tests/Tests/ClientConcepts/ConnectionPooling/ProductChecking/ProductCheckAtStartup.doc.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using System;
56
using System.Threading.Tasks;
67
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
78
using Elasticsearch.Net.VirtualizedCluster;
@@ -50,7 +51,7 @@ public async Task ProductCheckPerformedOnSecondCallWhenFirstCheckFails()
5051
/** Here's an example with a single node cluster which fails for some reason during the first product check attempt. */
5152
var audit = new Auditor(() => VirtualClusterWith
5253
.Nodes(1, productCheckAlwaysSucceeds: false)
53-
.ProductCheck(r => r.Fails(TimesHelper.Once))
54+
.ProductCheck(r => r.Fails(TimesHelper.Once, 429))
5455
.ProductCheck(r => r.SucceedAlways())
5556
.ClientCalls(r => r.SucceedAlways())
5657
.StaticConnectionPool()
@@ -60,12 +61,12 @@ public async Task ProductCheckPerformedOnSecondCallWhenFirstCheckFails()
6061
audit = await audit.TraceCalls(skipProductCheck: false,
6162
new ClientCall() {
6263
{ ProductCheckOnStartup },
63-
{ ProductCheckFailure, 9200 }, // <1> as this is the first call, the product check is executed, but fails
64+
{ ProductCheckFailure, 9200 }, // <1> as this is the first call, the product check is executed, but times out
6465
{ HealthyResponse, 9200 } // <2> the actual request is still sent and succeeds
6566
},
6667
new ClientCall() {
6768
{ ProductCheckOnStartup },
68-
{ ProductCheckSuccess, 9200 }, // <3> as the previous product check failed, it runs again on the second call
69+
{ ProductCheckSuccess, 9200 }, // <3> as the previous product check failed, it runs again on the second call and this time it succeeds
6970
{ HealthyResponse, 9200 }
7071
},
7172
new ClientCall() {
@@ -77,10 +78,10 @@ public async Task ProductCheckPerformedOnSecondCallWhenFirstCheckFails()
7778
[U]
7879
public async Task ProductCheckAttemptsAllNodes()
7980
{
80-
/** Here's an example with a three node cluster which fails for some reason during the first and second product check attempts. */
81+
/** Here's an example with a three node cluster which fails (due to too many requests) during the first and second product check attempts. */
8182
var audit = new Auditor(() => VirtualClusterWith
8283
.Nodes(3, productCheckAlwaysSucceeds: false)
83-
.ProductCheck(r => r.FailAlways())
84+
.ProductCheck(r => r.FailAlways(429))
8485
.ProductCheck(r => r.OnPort(9202).SucceedAlways())
8586
.ClientCalls(r => r.SucceedAlways())
8687
.StaticConnectionPool()

0 commit comments

Comments
 (0)