예제 #1
0
        public static IElasticLowLevelClient CreateClient(Func <VirtualCluster, VirtualCluster> setup)
        {
            var cluster         = VirtualClusterWith.Nodes(1).Ping(c => c.SucceedAlways());
            var virtualSettings = setup(cluster)
                                  .StaticConnectionPool()
                                  .AllDefaults();

            var settings = new ConnectionConfiguration(virtualSettings.ConnectionPool, virtualSettings.Connection, Serializer)
                           .DisablePing()
                           .EnableDebugMode();

            return(new ElasticLowLevelClient(settings));
        }
        [U] public async Task ThrowsExceptionWithNoRules()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(1)
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing().EnableDebugMode())
                                    );
            var e = await Assert.ThrowsAsync <UnexpectedElasticsearchClientException>(
                async() => await audit.TraceCalls(new ClientCall {
            }));

            e.Message.Should().Contain("No ClientCalls defined for the current VirtualCluster, so we do not know how to respond");
        }
예제 #3
0
        public async Task PicksADifferentNodeEachTimeAnodeIsDown()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(4)
                                    .ClientCalls(p => p.Fails(Always))
                                    .StickySniffingConnectionPool()
                                    .Settings(p => p.DisablePing().SniffOnStartup(false).SniffOnConnectionFault(false))
                                    );

            await audit.TraceCalls(
                /** All the calls fail */
                new ClientCall {
                { BadResponse, 9200 },
                { BadResponse, 9201 },
                { BadResponse, 9202 },
                { BadResponse, 9203 },
                { MaxRetriesReached },
                { FailedOverAllNodes },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },

                /** After all our registered nodes are marked dead we want to sample a single dead node
                 * each time to quickly see if the cluster is back up. We do not want to retry all 4
                 * nodes
                 */
                new ClientCall {
                { AllNodesDead },
                { Resurrection, 9200 },
                { BadResponse, 9200 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },
                new ClientCall {
                { AllNodesDead },
                { Resurrection, 9201 },
                { BadResponse, 9201 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },
                new ClientCall {
                { AllNodesDead },
                { Resurrection, 9202 },
                { BadResponse, 9202 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },
                new ClientCall {
                { AllNodesDead },
                { Resurrection, 9203 },
                { BadResponse, 9203 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            }
                );
        }
예제 #4
0
        public async Task FailOverResultsInSpans()
        {
            var payloadSender = new MockPayloadSender();
            var cluster       = VirtualClusterWith.Nodes(10)
                                .ClientCalls(c => c.FailAlways())
                                .ClientCalls(r => r.OnPort(9209).SucceedAlways())
                                .StaticConnectionPool()
                                .AllDefaults();
            var client = cluster.Client;

            using (var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)))
                using (agent.Subscribe(new ElasticsearchDiagnosticsSubscriber()))
                {
                    var searchResponse = await agent.Tracer.CaptureTransaction("Call Client", ApiConstants.ActionExec,
                                                                               async() => await client.SearchAsync <StringResponse>(PostData.Empty)
                                                                               );

                    searchResponse.Should().NotBeNull();
                    searchResponse.Success.Should().BeTrue();
                    searchResponse.AuditTrail.Should().NotBeEmpty();

                    var failed = searchResponse.AuditTrail.Where(a => a.Event == AuditEvent.BadResponse);
                    failed.Should().HaveCount(9);

                    var spans = payloadSender.SpansOnFirstTransaction;
                    spans.Should().NotBeEmpty();
                    var pings = spans.Where(s => s.Action == "Ping");
                    pings.Should().HaveCount(10);
                    spans.Where(s => s.Action == "CallElasticsearch").Should().HaveCount(10);

                    spans.Should().OnlyContain(s => s.Context != null);
                    spans.Should().OnlyContain(s => s.Context.Db != null);
                    spans.Should().OnlyContain(s => s.Context.Db.Statement != null);

                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch).Context.Destination.Should().NotBeNull();
                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch).Context.Destination.Address.Should().Be("localhost");
                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch).Context.Destination.Port.Should().Be(9200);

                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch).Context.Destination.Service.Should().NotBeNull();
                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch).Context.Destination.Service.Type.Should().Be(ApiConstants.TypeDb);
                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch)
                    .Context.Destination.Service.Name.Should()
                    .Be(ApiConstants.SubtypeElasticsearch);
                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch)
                    .Context.Destination.Service.Resource.Should()
                    .Be(ApiConstants.SubtypeElasticsearch);

                    spans.First(n => n.Subtype == ApiConstants.SubtypeElasticsearch).Outcome.Should().Be(Outcome.Success);
                }
        }
예제 #5
0
        public async Task SniffOnStartupReseedsPutsMostWeightedNodeToFront()
        {
            IEnumerable <Node> Nodes(int start) => Enumerable.Range(start, 4)
            .Select(i => new Uri($"http://localhost:{9200 + i}"))
            .Select((u, i) => new Node(u)
            {
                Settings = new Dictionary <string, object> {
                    { "rack", $"rack_{u.Port - 9200}" }
                }
            });

            /** We seed a cluster with an array of 4 Uri's starting at port 9200.
             * Our sniffing sorted connection pool is set up to favor nodes in rack_2
             */
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(4)
                                    .ClientCalls(p => p.SucceedAlways())
                                    .Sniff(s => s.SucceedAlways(VirtualClusterWith
                                                                .Nodes(Nodes(0))
                                                                .ClientCalls(p => p.SucceedAlways()))
                                           )
                                    .StickySniffingConnectionPool(n =>
                                                                  (n.Settings.TryGetValue("rack", out var v) && v.ToString() == "rack_2" ? 10 : 0)
                                                                  )
                                    .Settings(p => p.DisablePing())
                                    );

            /** Sniff happens on 9200 because our seed has no knowledge of rack ids
             * However when we reseed the nodes from the sniff response we sort 9202 to to top
             * because it lives in rack_2
             */
            await audit.TraceCalls(
                new ClientCall
            {
                { SniffOnStartup },
                { SniffSuccess, 9200 },
                { HealthyResponse, 9202 },
            },
                /** We are sticky on 9202 for as long as it keeps returning valid responses */
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9202 } }
                );
        }
예제 #6
0
        /**==== Client uses publish address
         *
         */
        [U] public async Task UsesPublishAddress()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(2)
                                    .MasterEligible(9200)
                                    .ClientCalls(c => c.SucceedAlways())
                                    .Ping(r => r.OnPort(9200).Fails(Once))
                                    .Ping(c => c.SucceedAlways())
                                    .Sniff(p => p.SucceedAlways(VirtualClusterWith
                                                                .Nodes(10)
                                                                .MasterEligible(9200, 9202, 9201)
                                                                .PublishAddress("10.0.12.1")
                                                                .ClientCalls(c => c.SucceedAlways())
                                                                .Ping(c => c.SucceedAlways())
                                                                ))
                                    .SniffingConnectionPool()
                                    .Settings(s => s.SniffOnStartup(false))
                                    );

            void HostAssert(Audit a, string host, int expectedPort)
            {
                a.Node.Uri.Host.Should().Be(host);
                a.Node.Uri.Port.Should().Be(expectedPort);
            }

            void SniffUrlAssert(Audit a, string host, int expectedPort)
            {
                HostAssert(a, host, expectedPort);
                var sniffUri = new UriBuilder(a.Node.Uri)
                {
                    Path  = RequestPipeline.SniffPath,
                    Query = "flat_settings=true&timeout=2s"
                }.Uri;

                sniffUri.PathEquals(a.Path, nameof(SniffUrlAssert));
            }

            audit = await audit.TraceCalls(
                new ClientCall {
                { PingFailure, a => HostAssert(a, "localhost", 9200) },
                { SniffOnFail },
                { SniffSuccess, a => SniffUrlAssert(a, "localhost", 9200) },
                { PingSuccess, a => HostAssert(a, "10.0.12.1", 9200) },
                { HealthyResponse, a => HostAssert(a, "10.0.12.1", 9200) },
                { pool => pool.Nodes.Count.Should().Be(10) }                          // <1> Our pool should now have 10 nodes
            }
                );
        }
        public async Task HttpTeapotDoesNotFallOver()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.FailAlways(418))
                                    .ClientCalls(r => r.OnPort(9201).SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing())
                                    );

            audit = await audit.TraceCall(
                new ClientCall {
                { AuditEvent.BadResponse, 9200 },
            }
                );
        }
        public async Task DoesNotRetryOnSingleNodeConnectionPool()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.FailAlways().Takes(TimeSpan.FromSeconds(3)))
                                    .ClientCalls(r => r.OnPort(9209).SucceedAlways())
                                    .SingleNodeConnection()
                                    .Settings(s => s.DisablePing().MaximumRetries(10))
                                    );

            audit = await audit.TraceCall(
                new ClientCall(r => r.MaxRetries(10)) {
                { BadResponse, 9200 }
            }
                );
        }
예제 #9
0
        public async Task OnlyCallsForcedNode()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.SucceedAlways())
                                    .ClientCalls(r => r.OnPort(9208).FailAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing())
                                    );

            audit = await audit.TraceCall(
                new ClientCall(r => r.ForceNode(new Uri("http://localhost:9208"))) {
                { BadResponse, 9208 }
            }
                );
        }
예제 #10
0
        /** Finally, let's demonstrate disabling both sniff and ping on the request */
        [U] public async Task DisableSniffAndPing()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.SucceedAlways())
                                    .SniffingConnectionPool()
                                    .Settings(s => s.SniffOnStartup())
                                    );

            audit = await audit.TraceCall(
                new ClientCall(r => r.DisableSniffing().DisablePing()) // <1> disable ping and sniff
            {
                { HealthyResponse, 9200 }                              // <2> no ping or sniff before the call
            }
                );
        }
        [U] public async Task AGlobalRuleStaysValidForever()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(1)
                                    .ClientCalls(c => c.SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing())
                                    );

            audit = await audit.TraceCalls(
                Enumerable.Range(0, 1000)
                .Select(i => new ClientCall {
                { AuditEvent.HealthyResponse, 9200 },
            })
                .ToArray()
                );
        }
        public async Task FallsOverDeadNodes()
        {
            /** A cluster with 2 nodes where the second node fails on ping */
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(4)
                                    .ClientCalls(p => p.Succeeds(Always))
                                    .ClientCalls(p => p.OnPort(9201).FailAlways())
                                    .ClientCalls(p => p.OnPort(9203).FailAlways())
                                    .StaticConnectionPool()
                                    .Settings(p => p.DisablePing())
                                    );

            await audit.TraceCalls(
                new ClientCall {
                { HealthyResponse, 9200 },                        // <1> The first call goes to 9200 which succeeds
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(0) }
            },
                new ClientCall {
                { BadResponse, 9201 },                        // <2> The 2nd call does a ping on 9201 because its used for the first time. It fails so we wrap over to node 9202
                { HealthyResponse, 9202 },
                /** Finally we assert that the connectionpool has one node that is marked as dead */
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(1) }
            },
                new ClientCall {
                { BadResponse, 9203 },                        // <3> The next call goes to 9203 which fails so we should wrap over
                { HealthyResponse, 9200 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(2) }
            },
                new ClientCall {
                { HealthyResponse, 9202 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(2) }
            },
                new ClientCall {
                { HealthyResponse, 9200 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(2) }
            },
                new ClientCall {
                { HealthyResponse, 9202 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(2) }
            },
                new ClientCall {
                { HealthyResponse, 9200 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(2) }
            }
                );
        }
        public async Task PicksADifferentNodeEachTimeAnodeIsDown()
        {
            /** A cluster with 2 nodes where the second node fails on ping */
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(4)
                                    .ClientCalls(p => p.Fails(Always))
                                    .StaticConnectionPool()
                                    .Settings(p => p.DisablePing())
                                    );

            await audit.TraceCalls(
                new ClientCall {
                { BadResponse, 9200 },                        // <1> All the calls fail
                { BadResponse, 9201 },
                { BadResponse, 9202 },
                { BadResponse, 9203 },
                { MaxRetriesReached },
                { FailedOverAllNodes },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },
                new ClientCall {
                { AllNodesDead },                         // <2> After all our registered nodes are marked dead we want to sample a single dead node each time to quickly see if the cluster is back up. We do not want to retry all 4 nodes
                { Resurrection, 9201 },
                { BadResponse, 9201 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },
                new ClientCall {
                { AllNodesDead },
                { Resurrection, 9202 },
                { BadResponse, 9202 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },
                new ClientCall {
                { AllNodesDead },
                { Resurrection, 9203 },
                { BadResponse, 9203 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            },
                new ClientCall {
                { AllNodesDead },
                { Resurrection, 9200 },
                { BadResponse, 9200 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(4) }
            }
                );
        }
예제 #14
0
        public async Task PingAfterRevival()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(3)
                                    .ClientCalls(r => r.SucceedAlways())
                                    .ClientCalls(r => r.OnPort(9202).Fails(Once))
                                    .Ping(p => p.SucceedAlways())
                                    .StaticConnectionPool()
                                    .AllDefaults()
                                    );

            audit = await audit.TraceCalls(
                new ClientCall { { PingSuccess, 9200 }, { HealthyResponse, 9200 } },
                new ClientCall { { PingSuccess, 9201 }, { HealthyResponse, 9201 } },
                new ClientCall {
                { PingSuccess, 9202 },
                { BadResponse, 9202 },
                { HealthyResponse, 9200 },
                { pool => pool.Nodes.Where(n => !n.IsAlive).Should().HaveCount(1) }
            },
                new ClientCall { { HealthyResponse, 9201 } },
                new ClientCall { { HealthyResponse, 9200 } },
                new ClientCall { { HealthyResponse, 9201 } },
                new ClientCall {
                { HealthyResponse, 9200 },
                { pool => pool.Nodes.First(n => !n.IsAlive).DeadUntil.Should().BeAfter(DateTime.UtcNow) }
            }
                );

            audit = await audit.TraceCalls(
                new ClientCall { { HealthyResponse, 9201 } },
                new ClientCall { { HealthyResponse, 9200 } },
                new ClientCall { { HealthyResponse, 9201 } }
                );

            audit.ChangeTime(d => d.AddMinutes(20));

            audit = await audit.TraceCalls(
                new ClientCall { { HealthyResponse, 9201 } },
                new ClientCall {
                { Resurrection, 9202 },
                { PingSuccess, 9202 },
                { HealthyResponse, 9202 }
            }
                );
        }
        public async Task ExceptionFallsOverToNextNode()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.FailAlways())
                                    .ClientCalls(r => r.OnPort(9201).SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing())
                                    );

            audit = await audit.TraceCall(
                new ClientCall {
                { AuditEvent.BadResponse, 9200 },
                { AuditEvent.HealthyResponse, 9201 },
            }
                );
        }
예제 #16
0
        public async Task CustomPredicateYieldingNothingThrows()
        {
            var totalNodesInTheCluster = 20;

            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(totalNodesInTheCluster)
                                    .Sniff(s => s.SucceedAlways()
                                           .Succeeds(Always, VirtualClusterWith.Nodes(totalNodesInTheCluster))
                                           )
                                    .SniffingConnectionPool()
                                    .Settings(s => s
                                              .DisablePing()
                                              .NodePredicate(node => false) // <1> A _bad_ predicate that declines *all* nodes
                                              )
                                    );

            /**
             * Now when making the client calls, the audit trail indicates that a sniff on startup succeeds, but the subsequent
             * API call fails because the node predicate filters out all nodes as targets on which to execute API calls
             */
            await audit.TraceUnexpectedElasticsearchException(new ClientCall
            {
                { SniffOnStartup },               // <1> The audit trail indicates a sniff for the very first time on startup
                { SniffSuccess },                 // <2> The sniff succeeds because the node predicate is ignored when sniffing
                { NoNodesAttempted }              // <3> when trying to do an actual API call however, the predicate prevents any nodes from being attempted
            }, e =>
            {
                e.FailureReason.Should().Be(PipelineFailure.Unexpected);

                Func <string> debug = () => e.DebugInformation;
                debug.Invoking(s => s.Invoke()).Should().NotThrow();
            });

            /**
             * An example of the debug information that the client response returns for the previous exception
             *
             * ....
             * # 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
             *  - [1] SniffOnStartup: Took: 00:00:00
             *  - [2] SniffSuccess: Node: http://localhost:9200/ Took: 00:00:00
             *  - [3] NoNodesAttempted: Took: 00:00:00
             * # Inner Exception: No nodes were attempted, this can happen when a node predicate does not match any nodes
             * ....
             */
        }
예제 #17
0
		public async Task CanOverrideBadResponse()
		{
			var audit = new Auditor(() => VirtualClusterWith
				.Nodes(10)
				.ClientCalls(r => r.FailAlways(400))
				.StaticConnectionPool()
				.Settings(s => s.DisablePing().MaximumRetries(0))
			);

			audit = await audit.TraceCalls(
				new ClientCall {
					{ BadResponse, 9200 }
				},
				new ClientCall(r => r.AllowedStatusCodes(400)) {
					{ HealthyResponse, 9201 }
				}
			);
		}
예제 #18
0
        /** Now, let's disable pinging on the request */
        [U] public async Task DisablePing()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.SucceedAlways())
                                    .SniffingConnectionPool()
                                    .Settings(s => s.SniffOnStartup())
                                    );

            audit = await audit.TraceCall(
                new ClientCall(r => r.DisablePing())                 // <1> disable ping
            {
                { SniffOnStartup },
                { SniffSuccess, 9200 },                         // <2> No ping after sniffing
                { HealthyResponse, 9200 }
            }
                );
        }
        public async Task RespectsOveralRequestTimeout()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.FailAlways().Takes(TimeSpan.FromSeconds(10)))
                                    .ClientCalls(r => r.OnPort(9209).SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing().RequestTimeout(TimeSpan.FromSeconds(20)))
                                    );

            audit = await audit.TraceCall(
                new ClientCall {
                { BadResponse, 9200 },
                { BadResponse, 9201 },
                { MaxTimeoutReached }
            }
                );
        }
        public async Task RetriesAreLimitedByNodesInPool()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(2)
                                    .ClientCalls(r => r.FailAlways().Takes(TimeSpan.FromSeconds(3)))
                                    .ClientCalls(r => r.OnPort(9209).SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing().RequestTimeout(TimeSpan.FromSeconds(2)).MaxRetryTimeout(TimeSpan.FromSeconds(10)))
                                    );

            audit = await audit.TraceCall(
                new ClientCall {
                { BadResponse, 9200 },
                { BadResponse, 9201 },
                { MaxRetriesReached },
                { FailedOverAllNodes }
            }
                );
        }
예제 #21
0
        public async Task SniffOnStartUpTakesNewClusterState()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .Sniff(s => s.Fails(Always))
                                    .Sniff(s => s.OnPort(9202).Succeeds(Always, VirtualClusterWith.Nodes(8, startFrom: 9204))) // <1> Sniffing returns 8 nodes, starting from 9204
                                    .SniffingConnectionPool()
                                    .AllDefaults()
                                    );

            await audit.TraceCall(new ClientCall {
                { SniffOnStartup },
                { SniffFailure, 9200 },
                { SniffFailure, 9201 },
                { SniffSuccess, 9202 },
                { PingSuccess, 9204 },                // <2> After successfully sniffing, the ping now happens on 9204
                { HealthyResponse, 9204 }
            });
        }
        public async Task FixedMaximumNumberOfRetries()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.FailAlways())
                                    .ClientCalls(r => r.OnPort(9209).SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing().MaximumRetries(5))
                                    );

            audit = await audit.TraceCall(
                new ClientCall(r => r.MaxRetries(2)) {
                { BadResponse, 9200 },
                { BadResponse, 9201 },
                { BadResponse, 9202 },
                { MaxRetriesReached }
            }
                );
        }
        public async Task FixedMaximumNumberOfRetries()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.FailAlways())
                                    .ClientCalls(r => r.OnPort(9209).SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing().MaximumRetries(3)) // <1> Set the maximum number of retries to 3
                                    );

            audit = await audit.TraceCall(
                new ClientCall {
                { BadResponse, 9200 },
                { BadResponse, 9201 },
                { BadResponse, 9202 },
                { BadResponse, 9203 },
                { MaxRetriesReached }                         // <2> The client call trace returns an `MaxRetriesReached` audit after the initial attempt and the number of retries allowed
            }
                );
        }
예제 #24
0
        public async Task AllNodesArePingedOnlyOnFirstUseProvidedTheyAreHealthy()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(4)
                                    .Ping(p => p.SucceedAlways()) // <1> Pings on nodes always succeed
                                    .StaticConnectionPool()
                                    .AllDefaults()
                                    );

            await audit.TraceCalls(
                new ClientCall { { PingSuccess, 9200 }, { HealthyResponse, 9200 } },               // <2> A successful ping on each node
                new ClientCall { { PingSuccess, 9201 }, { HealthyResponse, 9201 } },
                new ClientCall { { PingSuccess, 9202 }, { HealthyResponse, 9202 } },
                new ClientCall { { PingSuccess, 9203 }, { HealthyResponse, 9203 } },
                new ClientCall { { HealthyResponse, 9200 } },
                new ClientCall { { HealthyResponse, 9201 } },
                new ClientCall { { HealthyResponse, 9202 } },
                new ClientCall { { HealthyResponse, 9203 } },
                new ClientCall { { HealthyResponse, 9200 } }
                );
        }
예제 #25
0
        public async Task RespectsConnectTimeoutOverride()
        {
            /** We set up a 10 node cluster with a global time out of 20 seconds.
             * Each call on a node takes 10 seconds. So we can only try this call on 2 nodes
             * before the max request time out kills the client call.
             */
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .Ping(p => p.SucceedAlways().Takes(TimeSpan.FromSeconds(20)))
                                    .ClientCalls(r => r.SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.RequestTimeout(TimeSpan.FromSeconds(10)).PingTimeout(TimeSpan.FromSeconds(10)))
                                    );

            audit = await audit.TraceCalls(

                /**
                 * The first call uses the configured global settings, request times out after 10 seconds and ping
                 * calls always take 20, so we should see a single ping failure
                 */
                new ClientCall {
                { PingFailure, 9200 },
                { MaxTimeoutReached }
            },

                /**
                 * On the second request we set a request ping timeout override of 2 seconds
                 * We should now see more nodes being tried before the request timeout is hit.
                 */
                new ClientCall(r => r.PingTimeout(TimeSpan.FromSeconds(2)))
            {
                { PingFailure, 9202 },
                { PingFailure, 9203 },
                { PingFailure, 9204 },
                { PingFailure, 9205 },
                { PingFailure, 9206 },
                { MaxTimeoutReached }
            }
                );
        }
예제 #26
0
        /**[[disable-sniff-ping-per-request]]
         * === Disable sniffing and pinging per request
         *
         * Even if you are using a sniffing connection pool thats set up to sniff on start/failure
         * and pinging enabled, you can opt out of this behaviour on a _per request_ basis.
         *
         * In our first test we set up a cluster that pings and sniffs on startup
         * but we disable the sniffing on our first request so we only see the ping and the response
         */
        [U] public async Task DisableSniff()
        {
            /** Let's set up the cluster and configure clients to **always** sniff on startup */
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.SucceedAlways())
                                    .Sniff(c => c.SucceedAlways())
                                    .Ping(c => c.SucceedAlways())
                                    .SniffingConnectionPool()
                                    .Settings(s => s.SniffOnStartup()) // <1> sniff on startup
                                    );

            /** Now We disable sniffing on the request so even though it's our first call,
             * we do not want to sniff on startup.
             *
             * Instead, the sniff on startup is deferred to the second call into the cluster that
             * does not disable sniffing on a per request basis.
             *
             * And after that no sniff on startup will happen again
             */
            audit = await audit.TraceCalls(
                new ClientCall(r => r.DisableSniffing())       // <1> disable sniffing
            {
                { PingSuccess, 9200 },                         // <2> first call is a successful ping
                { HealthyResponse, 9200 }
            },
                new ClientCall()
            {
                { SniffOnStartup },                         // <3> sniff on startup call happens here, on the second call
                { SniffSuccess, 9200 },
                { PingSuccess, 9200 },
                { HealthyResponse, 9200 }
            },
                new ClientCall()
            {
                { PingSuccess, 9201 },                         // <4> No sniff on startup again
                { HealthyResponse, 9201 }
            }
                );
        }
예제 #27
0
        public async Task ExceptionDoesNotCauseLoseOfSpan()
        {
            var payloadSender = new MockPayloadSender();
            var cluster       = VirtualClusterWith.Nodes(2)
                                .ClientCalls(r => r.OnPort(9200).FailAlways())
                                .ClientCalls(c => c.OnPort(9201).FailAlways(new Exception("boom!")))
                                .StaticConnectionPool()
                                .AllDefaults();
            var client = cluster.Client;

            using (var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)))
                using (agent.Subscribe(new ElasticsearchDiagnosticsSubscriber()))
                {
                    try
                    {
                        var searchResponse = await agent.Tracer.CaptureTransaction("Call Client", ApiConstants.ActionExec,
                                                                                   async() => await client.SearchAsync <StringResponse>(PostData.Empty)
                                                                                   );

                        searchResponse.Should().NotBeNull();
                    }
                    catch (Exception)
                    {
                        // ignored
                    }

                    var spans = payloadSender.SpansOnFirstTransaction;
                    spans.Should().NotBeEmpty();
                    spans.Should().Contain(s => s.Context.Db.Statement != null);
                    //ensure we the last span is closed even if the listener does not receive a response
                    spans.Where(s => s.Action == "Ping").Should().HaveCount(2);
                    spans.Where(s => s.Action == "Ping").All(n => n.Outcome == Outcome.Success).Should().BeTrue();

                    spans.Where(s => s.Action == "CallElasticsearch").Should().HaveCount(2);
                    spans.Where(s => s.Action == "CallElasticsearch").All(n => n.Outcome == Outcome.Failure).Should().BeTrue();

                    payloadSender.Errors.Should().Contain(e => ((Error)e).Exception.Message == "boom!");
                }
        }
        /**
         * When a bad authentication response occurs, the client attempts to deserialize the response body returned;
         *
         * In some setups you might be running behind a proxy and you might need to prevent the client from trying to deserialize
         * bad json. In the following example an HTML response is return but with an application/json content type. If the proxy is not
         * under your control you would need to be able to fix this in the client. Here we make the client aware that 401 responses
         * should never be deserialized by calling `SkipDeserializationForStatusCodes()` on `ConnectionSettings`.
         */
        [U] public async Task BadAuthenticationHtmlResponseIsIgnored()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .Ping(r => r.SucceedAlways())
                                    .ClientCalls(r => r.FailAlways(401).ReturnByteResponse(HtmlNginx401Response, "application/json")) // <1> Always return a 401 bad response with a HTML response on client calls
                                    .StaticConnectionPool()
                                    .Settings(s => s.SkipDeserializationForStatusCodes(401))
                                    );

            audit = await audit.TraceElasticsearchException(
                new ClientCall {
                { AuditEvent.PingSuccess, 9200 },
                { AuditEvent.BadResponse, 9201 },
            },
                (e) =>
            {
                e.FailureReason.Should().Be(PipelineFailure.BadAuthentication);
                e.Response.HttpStatusCode.Should().Be(401);
                e.Response.ResponseBodyInBytes.Should().BeNull();                         // <2> Assert that the response body bytes are null
            }
                );
        }
예제 #29
0
        /**
         * Sometimes, an unexpected exception happens further down in the pipeline. In this scenario, we
         * wrap them inside an `UnexpectedElasticsearchClientException` so that information about where
         * in the pipeline the exception happened is not lost.
         *
         * In this next example, a call to 9200 fails with a `WebException`.
         * The call then rolls over to retry on 9201, which throws an hard exception from within `IConnection`.
         * Finally, we assert that we can still see the audit trail for the whole coordinated request.
         */

        [U] public async Task WillFailOverKnowConnectionExceptionButNotUnexpected()
        {
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.OnPort(9200).FailAlways(new System.Net.Http.HttpRequestException("recover"))) // <1> calls on 9200 set up to throw a `HttpRequestException`
                                    .ClientCalls(r => r.OnPort(9201).FailAlways(new Exception("boom!")))                              // <2> calls on 9201 set up to throw an `Exception`
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing())
                                    );

            audit = await audit.TraceUnexpectedException(
                new ClientCall {
                { AuditEvent.BadResponse, 9200 },
                { AuditEvent.BadResponse, 9201 },                         // <3> Assert that the audit trail for the client call includes the bad response from 9200 and 9201
            },
                (e) =>
            {
                e.FailureReason.Should().Be(PipelineFailure.Unexpected);
                e.InnerException.Should().NotBeNull();
                e.InnerException.Message.Should().Be("boom!");
            }
                );
        }
예제 #30
0
        public async Task RespectsRequestTimeoutOverride()
        {
            /** we set up a 10 node cluster with a global time out of 20 seconds.
             * Each call on a node takes 10 seconds. So we can only try this call on 2 nodes
             * before the max request time out kills the client call.
             */
            var audit = new Auditor(() => VirtualClusterWith
                                    .Nodes(10)
                                    .ClientCalls(r => r.FailAlways().Takes(TimeSpan.FromSeconds(10)))
                                    .ClientCalls(r => r.OnPort(9209).SucceedAlways())
                                    .StaticConnectionPool()
                                    .Settings(s => s.DisablePing().RequestTimeout(TimeSpan.FromSeconds(20)))
                                    );

            audit = await audit.TraceCalls(
                new ClientCall {
                { BadResponse, 9200 },
                { BadResponse, 9201 },
                { MaxTimeoutReached }
            },

                /**
                 * On the second request we specify a request timeout override to 80 seconds
                 * We should now see more nodes being tried.
                 */
                new ClientCall(r => r.RequestTimeout(TimeSpan.FromSeconds(80)))
            {
                { BadResponse, 9203 },
                { BadResponse, 9204 },
                { BadResponse, 9205 },
                { BadResponse, 9206 },
                { BadResponse, 9207 },
                { BadResponse, 9208 },
                { HealthyResponse, 9209 },
            }
                );
        }