private async Task <QueryResult <TResponse> > CallConsulAsync <TResponse>(Func <Task <QueryResult <TResponse> > > call, string monitoringOperation, IDictionary <string, object> monitoringProperties) { using (var eventContext = new EventContext("ConsulRx.Client", monitoringOperation)) { eventContext.AddValues(monitoringProperties); try { var result = await call().ConfigureAwait(false); eventContext.IncludeConsulResult(result); if (!HealthyCodes.Contains(result.StatusCode)) { //if we got an error that indicates either server or client aren't healthy (e.g. 500 or 403) //then model this as an exception (same as if server can't be contacted). We will figure out what to do below throw new ConsulErrorException(result); } return(result); } catch (Exception ex) { eventContext.IncludeException(ex); throw; } } }
private IObservable <TObservation> LongPoll <TResponse, TObservation>(Func <ulong, Task <QueryResult <TResponse> > > poll, Func <QueryResult <TResponse>, TObservation> toObservation, string monitoringOperation, IDictionary <string, object> monitoringProperties) where TObservation : class { return(Observable.Create <TObservation>(async(o, cancel) => { ulong index = default(ulong); var successfullyContactedConsulAtLeastOnce = false; while (true) { using (var eventContext = new EventContext("ConsulRx.Client", monitoringOperation)) { eventContext["RequestIndex"] = index; eventContext.AddValues(monitoringProperties); QueryResult <TResponse> result = null; Exception exception = null; try { result = await poll(index); } catch (Exception ex) { eventContext.IncludeException(ex); exception = ex; } if (cancel.IsCancellationRequested) { eventContext["Cancelled"] = true; o.OnCompleted(); return; } if (result != null) { index = result.LastIndex; eventContext.IncludeConsulResult(result); if (HealthyCodes.Contains(result.StatusCode)) { //200 or 404 are the only response codes we should expect if consul and client are both configured properly successfullyContactedConsulAtLeastOnce = true; } else { //if we got an error that indicates either server or client aren't healthy (e.g. 500 or 403) //then model this as an exception (same as if server can't be contacted). We will figure out what to do below exception = new ConsulErrorException(result); eventContext.SetLevel(Level.Error); } } if (exception == null) { o.OnNext(toObservation(result)); } else { if (successfullyContactedConsulAtLeastOnce) { //if an error occurred, we reset the index so that we can start clean //this is necessary because if the consul cluster was restarted it won't recognize //the old index and will block until the longPollMaxWait expires index = default(ulong); //if we have been successful at contacting consul already, then we will retry under the assumption that //things will eventually get healthy again eventContext["SecondsUntilRetry"] = _retryDelay?.Seconds ?? 0; if (_retryDelay != null) { await Task.Delay(_retryDelay.Value, cancel); } } else { //if we encountered an error at the very beginning, then we don't have enough confidence that retrying will actually help //so we will stream the exception out and let the consumer figure out what to do o.OnError(exception); return; } } } } })); }