@@ -30,14 +30,14 @@ var audit = new Auditor(() => Framework.Cluster
3030 .AllDefaults()
3131)
3232{
33- AssertPoolBeforeCall = (pool) =>
33+ AssertPoolBeforeStartup = (pool) =>
3434 {
3535 pool.Should().NotBeNull();
3636 pool.Nodes.Should().HaveCount(10);
3737 pool.Nodes.Where(n => n.HoldsData).Should().HaveCount(10);
3838 },
3939
40- AssertPoolAfterCall = (pool) =>
40+ AssertPoolAfterStartup = (pool) =>
4141 {
4242 pool.Should().NotBeNull();
4343 pool.Nodes.Should().HaveCount(8);
@@ -59,7 +59,7 @@ var audit = new Auditor(() => Framework.Cluster
5959 .AllDefaults()
6060)
6161{
62- AssertPoolBeforeCall = (pool) =>
62+ AssertPoolBeforeStartup = (pool) =>
6363 {
6464 pool.Should().NotBeNull();
6565 pool.Nodes.Should().HaveCount(10);
@@ -68,10 +68,10 @@ var audit = new Auditor(() => Framework.Cluster
6868 pool.Nodes.Should().OnlyContain(n => n.Uri.Host == "localhost");
6969 },
7070
71- AssertPoolAfterCall = (pool) =>
71+ AssertPoolAfterStartup = (pool) =>
7272 {
7373 pool.Should().NotBeNull();
74- pool.Nodes.Should().HaveCount(7, "we filtered the node that stores no data ");
74+ pool.Nodes.Should().HaveCount(7, "we filtered the node that has no http enabled ");
7575 pool.Nodes.Should().NotContain(n=>n.Uri.Port == 9201);
7676 pool.Nodes.Where(n => n.HoldsData).Should().HaveCount(5);
7777 }
@@ -80,6 +80,199 @@ var audit = new Auditor(() => Framework.Cluster
8080await audit.TraceStartup();
8181----
8282
83+ [source,csharp]
84+ ----
85+ var masterNodes = new[] {9200, 9201, 9202};
86+
87+ var totalNodesInTheCluster = 20;
88+
89+ var audit = new Auditor(() => Framework.Cluster
90+ .MasterOnlyNodes(masterNodes.Length)
91+ // When the client sniffs on startup we see the cluster is 20 nodes in total
92+ .Sniff(s => s.SucceedAlways()
93+ .Succeeds(Always, Framework.Cluster.Nodes(totalNodesInTheCluster).StoresNoData(masterNodes).MasterEligible(masterNodes))
94+ )
95+ .SniffingConnectionPool()
96+ .Settings(s=>s.DisablePing()) //for testing simplicity we disable pings
97+ )
98+ {
99+ // before the sniff assert we only see three master only nodes
100+ AssertPoolBeforeStartup = (pool) =>
101+ {
102+ pool.Should().NotBeNull();
103+ pool.Nodes.Should().HaveCount(3, "we seeded 3 master only nodes at the start of the application");
104+ pool.Nodes.Where(n => n.HoldsData).Should().HaveCount(0, "none of which hold data");
105+ },
106+ // after sniff assert we now know about the existence of 20 nodes.
107+ AssertPoolAfterStartup = (pool) =>
108+ {
109+ pool.Should().NotBeNull();
110+ var nodes = pool.CreateView().ToList();
111+ nodes.Count().Should().Be(20, "Master nodes are included in the registration of nodes since we still favor sniffing on them");
112+ }
113+ };
114+
115+ var nodes = pool.CreateView().ToList();
116+
117+ audit = await audit.TraceStartup(new ClientCall
118+ {
119+ { SniffSuccess, 9200},
120+ { HealthyResponse, 9203}
121+ });
122+
123+ var seenNodes = new HashSet<int>();
124+ ----
125+
126+ [source,csharp]
127+ ----
128+ var audit = new Auditor(() => Framework.Cluster
129+ .Nodes(10)
130+ .Sniff(s => s.Fails(Always))
131+ .Sniff(s => s.OnPort(9202)
132+ .Succeeds(Always, Framework.Cluster.Nodes(8).MasterEligible(9200, 9201, 9202))
133+ )
134+ .SniffingConnectionPool()
135+ .AllDefaults()
136+ )
137+ {
138+ AssertPoolBeforeStartup = (pool) =>
139+ {
140+ pool.Should().NotBeNull();
141+ pool.Nodes.Should().HaveCount(10);
142+ pool.Nodes.Where(n => n.MasterEligible).Should().HaveCount(10);
143+ },
144+ AssertPoolAfterStartup = (pool) =>
145+ {
146+ pool.Should().NotBeNull();
147+ pool.Nodes.Should().HaveCount(8);
148+ pool.Nodes.Where(n => n.MasterEligible).Should().HaveCount(3);
149+ }
150+ };
151+ await audit.TraceStartup();
152+ ----
153+
154+ [source,csharp]
155+ ----
156+ foreach (var _ in Enumerable.Range(0, 1000))
157+ {
158+ audit = await audit.TraceCalls(
159+ new ClientCall {{HealthyResponse, (a) =>
160+ {
161+ var port = a.Node.Uri.Port;
162+ masterNodes.Should().NotContain(port);
163+ seenNodes.Add(port);
164+ }}}
165+ );
166+ }
167+
168+ seenNodes.Should().HaveCount(totalNodesInTheCluster - masterNodes.Length);
169+ ----
170+
171+ [source,csharp]
172+ ----
173+ var totalNodesInTheCluster = 20;
174+
175+ var setting = "node.attr.rack_id";
176+
177+ var value = "rack_one";
178+
179+ var nodesInRackOne = new[] {9204, 9210, 9213};
180+
181+ var audit = new Auditor(() => Framework.Cluster
182+ .Nodes(totalNodesInTheCluster)
183+ // When the client sniffs on startup we see the cluster is still 20 nodes in total
184+ // However we are now aware of the actual configured settings on the nodes
185+ .Sniff(s => s.SucceedAlways()
186+ .Succeeds(Always, Framework.Cluster.Nodes(totalNodesInTheCluster).HasSetting(setting, value, nodesInRackOne))
187+ )
188+ .SniffingConnectionPool()
189+ .Settings(s=>s
190+ .DisablePing() //for testing simplicity we disable pings
191+ //We only want to execute API calls to nodes in rack_one
192+ .NodePredicate(node=>node.Settings.ContainsKey(setting) && node.Settings[setting] == value)
193+ )
194+ )
195+ {
196+ AssertPoolAfterStartup = (pool) =>
197+ {
198+ pool.Should().NotBeNull();
199+ var nodes = pool.CreateView().ToList();
200+ nodes.Count(n => n.Settings.ContainsKey(setting)).Should().Be(3, "only three nodes are in rack_one");
201+ }
202+ };
203+
204+ var nodes = pool.CreateView().ToList();
205+
206+ audit = await audit.TraceStartup(new ClientCall
207+ {
208+ { SniffSuccess, 9200},
209+ { HealthyResponse, 9204}
210+ });
211+
212+ var seenNodes = new HashSet<int>();
213+ ----
214+
215+ [source,csharp]
216+ ----
217+ foreach (var _ in Enumerable.Range(0, 1000))
218+ {
219+ audit = await audit.TraceCalls(
220+ new ClientCall {{HealthyResponse, (a) =>
221+ {
222+ var port = a.Node.Uri.Port;
223+ nodesInRackOne.Should().Contain(port);
224+ seenNodes.Add(port);
225+ }}}
226+ );
227+ }
228+
229+ seenNodes.Should().HaveCount(nodesInRackOne.Length);
230+ ----
231+
232+ [source,csharp]
233+ ----
234+ var totalNodesInTheCluster = 20;
235+
236+ var audit = new Auditor(() => Framework.Cluster
237+ .Nodes(totalNodesInTheCluster)
238+ .Sniff(s => s.SucceedAlways()
239+ .Succeeds(Always, Framework.Cluster.Nodes(totalNodesInTheCluster))
240+ )
241+ .SniffingConnectionPool()
242+ .Settings(s => s
243+ .DisablePing()
244+ // evil predicate that declines ALL nodes
245+ .NodePredicate(node => false)
246+ )
247+ );
248+
249+ await audit.TraceUnexpectedElasticsearchException(new ClientCall
250+ {
251+ { SniffOnStartup }, //audit logs we are sniffing for the very very first time one startup
252+ { SniffSuccess }, //this goes ok because we ignore predicate when sniffing
253+ { NoNodesAttempted } //when trying to do an actual API call the predicate prevents any nodes from being attempted
254+ }, e =>
255+ {
256+ e.FailureReason.Should().Be(PipelineFailure.Unexpected);
257+ //generating the debug information should not throw
258+ Func<string> debug = () => e.DebugInformation;
259+ debug.Invoking(s =>s()).ShouldNotThrow();
260+ /* EXAMPLE OF PREVIOUS
261+ # FailureReason: Unrecoverable/Unexpected NoNodesAttempted while attempting POST on default-index/project/_search on an empty node, likely a node predicate on ConnectionSettings not matching ANY nodes
262+ - [1] SniffOnStartup: Took: 00:00:00
263+ - [2] SniffSuccess: Node: http://localhost:9200/ Took: 00:00:00
264+ - [3] NoNodesAttempted: Took: 00:00:00
265+ # Inner Exception: No nodes were attempted, this can happen when a node predicate does not match any nodes
266+ */
267+ });
268+
269+ e.FailureReason.Should().Be(PipelineFailure.Unexpected);
270+
271+ Func<string> debug = () => e.DebugInformation;
272+
273+ debug.Invoking(s =>s()).ShouldNotThrow();
274+ ----
275+
83276[source,csharp]
84277----
85278var audit = new Auditor(() => Framework.Cluster
@@ -91,15 +284,15 @@ var audit = new Auditor(() => Framework.Cluster
91284 .AllDefaults()
92285)
93286{
94- AssertPoolBeforeCall = (pool) =>
287+ AssertPoolBeforeStartup = (pool) =>
95288 {
96289 pool.Should().NotBeNull();
97290 pool.Nodes.Should().HaveCount(10);
98291 pool.Nodes.Where(n => n.HoldsData).Should().HaveCount(10);
99292 pool.Nodes.Should().OnlyContain(n => n.Uri.Host == "localhost");
100293 },
101294
102- AssertPoolAfterCall = (pool) =>
295+ AssertPoolAfterStartup = (pool) =>
103296 {
104297 pool.Should().NotBeNull();
105298 pool.Nodes.Should().HaveCount(8);
@@ -119,11 +312,15 @@ node.MasterEligible.Should().BeTrue();
119312
120313node.HoldsData.Should().BeFalse();
121314
315+ node.Settings.Should().NotBeEmpty().And.Contain("node.attr.rack_id", "rack_one");
316+
122317node = await SniffAndReturnNodeAsync();
123318
124319node.MasterEligible.Should().BeTrue();
125320
126321node.HoldsData.Should().BeFalse();
322+
323+ node.Settings.Should().NotBeEmpty().And.Contain("node.attr.rack_id", "rack_one");
127324----
128325
129326[source,csharp]
@@ -159,31 +356,3 @@ nodes.Should().NotBeEmpty().And.HaveCount(1);
159356var node = nodes.First();
160357----
161358
162- [source,csharp]
163- ----
164- var audit = new Auditor(() => Framework.Cluster
165- .Nodes(10)
166- .Sniff(s => s.Fails(Always))
167- .Sniff(s => s.OnPort(9202)
168- .Succeeds(Always, Framework.Cluster.Nodes(8).MasterEligible(9200, 9201, 9202))
169- )
170- .SniffingConnectionPool()
171- .AllDefaults()
172- )
173- {
174- AssertPoolBeforeCall = (pool) =>
175- {
176- pool.Should().NotBeNull();
177- pool.Nodes.Should().HaveCount(10);
178- pool.Nodes.Where(n => n.MasterEligible).Should().HaveCount(10);
179- },
180- AssertPoolAfterCall = (pool) =>
181- {
182- pool.Should().NotBeNull();
183- pool.Nodes.Should().HaveCount(8);
184- pool.Nodes.Where(n => n.MasterEligible).Should().HaveCount(3);
185- }
186- };
187- await audit.TraceStartup();
188- ----
189-
0 commit comments