/** * Sometimes an unexpected exception happens further down in the pipeline, this is why we * wrap them inside an UnexpectedElasticsearchClientException so that information about where * in the pipeline the unexpected exception is not lost, here a call to 9200 fails using a webexception. * It then falls over to 9201 which throws an hard exception from within IConnection. We assert that we * can still see the audit trail for the whole coordinated request. */ [U] public async Task WillFailOverKnowConnectionExceptionButNotUnexpected() { var audit = new Auditor(() => Framework.Cluster .Nodes(10) #if DOTNETCORE .ClientCalls(r => r.OnPort(9200).FailAlways(new System.Net.Http.HttpRequestException("recover"))) #else .ClientCalls(r => r.OnPort(9200).FailAlways(new WebException("recover"))) #endif .ClientCalls(r => r.OnPort(9201).FailAlways(new Exception("boom!"))) .StaticConnectionPool() .Settings(s => s.DisablePing()) ); audit = await audit.TraceUnexpectedException( new ClientCall { { AuditEvent.BadResponse, 9200 }, { AuditEvent.BadResponse, 9201 }, }, (e) => { e.FailureReason.Should().Be(PipelineFailure.Unexpected); e.InnerException.Should().NotBeNull(); e.InnerException.Message.Should().Be("boom!"); } ); }
/** == Unexpected exceptions * When a client call throws an exception that the IConnction can not handle, this exception will bubble * out the client as an UnexpectedElasticsearchClientException, regardless whether the client is configured to throw or not. * An IConnection is in charge of knowning what exceptions it can recover from or not. The default IConnection that is based on WebRequest can and * will recover from WebExceptions but others will be grounds for immediately exiting the pipeline. */ [U] public async Task UnexpectedExceptionsBubbleOut() { var audit = new Auditor(() => Framework.Cluster .Nodes(10) .ClientCalls(r => r.SucceedAlways()) .ClientCalls(r => r.OnPort(9201).FailAlways(new Exception("boom!"))) .StaticConnectionPool() .Settings(s => s.DisablePing()) ); audit = await audit.TraceCall( new ClientCall { { AuditEvent.HealthyResponse, 9200 }, } ); audit = await audit.TraceUnexpectedException( new ClientCall { { AuditEvent.BadResponse, 9201 }, }, (e) => { e.FailureReason.Should().Be(PipelineFailure.Unexpected); e.InnerException.Should().NotBeNull(); e.InnerException.Message.Should().Be("boom!"); } ); }
/** * An unexpected hard exception on ping and sniff is something we *do* try to revover from and failover. * Here pinging nodes on first use is enabled and 9200 throws on ping, we still fallover to 9201's ping succeeds. * However the client call on 9201 throws a hard exception we can not recover from */ [U] public async Task PingUnexceptedExceptionDoesFailOver() { var audit = new Auditor(() => Framework.Cluster .Nodes(10) .Ping(r => r.OnPort(9200).FailAlways(new Exception("ping exception"))) .Ping(r => r.OnPort(9201).SucceedAlways()) .ClientCalls(r => r.OnPort(9201).FailAlways(new Exception("boom!"))) .StaticConnectionPool() .AllDefaults() ); audit = await audit.TraceUnexpectedException( new ClientCall { { AuditEvent.PingFailure, 9200 }, { AuditEvent.PingSuccess, 9201 }, { AuditEvent.BadResponse, 9201 }, }, (e) => { e.FailureReason.Should().Be(PipelineFailure.Unexpected); /** InnerException is the exception that brought the request down */ e.InnerException.Should().NotBeNull(); e.InnerException.Message.Should().Be("boom!"); /** The hard exception that happened on ping is still available though */ e.SeenExceptions.Should().NotBeEmpty(); var pipelineException = e.SeenExceptions.First(); pipelineException.FailureReason.Should().Be(PipelineFailure.PingFailure); pipelineException.InnerException.Message.Should().Be("ping exception"); /** Seen exception is hard to relate back to a point in time, the exception is also * available on the audit trail */ var pingException = e.AuditTrail.First(a => a.Event == AuditEvent.PingFailure).Exception; pingException.Should().NotBeNull(); pingException.Message.Should().Be("ping exception"); } ); }