public async Task <T> ExecuteAsync() { /* this is a stateful object so can only be used once */ if (Interlocked.CompareExchange(ref _started, 1, 0) == 1) { throw new IllegalStateException("This instance can only be executed once. Please instantiate a new instance."); } CacheItem cacheItem; // get from cache if (_requestCache != null) { var key = GetCacheKey(); if (key != null && _requestCache.TryGetValue(key, out cacheItem)) { Metrics.MarkResponseFromCache(); _executionResult = cacheItem.ExecutionResult; _isExecutionComplete = true; RecordExecutedCommand(); try { _executionHook.OnCacheHit(this); } catch (Exception hookEx) { Logger.LogWarning("Error calling CommandExecutionHook.onCacheHit", hookEx); } return(cacheItem.Value); } } T result; long ms; var start = _clock.EllapsedTimeInMs; try { RecordExecutedCommand(); Metrics.IncrementConcurrentExecutionCount(); // mark that we're starting execution on the ExecutionHook // if this hook throws an exception, then a fast-fail occurs with no fallback. No state is left inconsistent _executionHook.OnStart(this); if (_circuitBreaker.AllowRequest) // short circuit closed = OK { if (ExecutionSemaphore.TryAcquire()) // semaphore { try { // if bulkhead by thread var token = Properties.ExecutionTimeoutEnabled.Value ? new CancellationTokenSource(Properties.ExecutionIsolationThreadTimeoutInMilliseconds.Value) : new CancellationTokenSource(); try { if ((_flags & ServiceCommandOptions.SemaphoreExecutionStrategy) != ServiceCommandOptions.SemaphoreExecutionStrategy) { _isExecutedInThread = true; /// /// If any of these hooks throw an exception, then it appears as if the actual execution threw an error /// _executionHook.OnThreadStart(this); _executionHook.OnExecutionStart(this); // Run with bulkhead and timeout var t = await Task.Factory.StartNew( () => Run(token.Token), token.Token, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler, TaskScheduler ) .ConfigureAwait(false); result = await t.ConfigureAwait(false); if (token.IsCancellationRequested) { throw new OperationCanceledException(); } _executionHook.OnThreadComplete(this); } else { // Simple semaphore _executionHook.OnExecutionStart(this); result = await Run(token.Token).ConfigureAwait(false); } try { _executionHook.OnExecutionSuccess(this); } catch (Exception hookEx) { Logger.LogWarning("Error calling CommandExecutionHook.onExecutionSuccess", hookEx); } } catch (AggregateException ae) { ms = _clock.EllapsedTimeInMs - start; var e = ((AggregateException)ae).InnerException; return(await OnExecutionError(ms, e)); } catch (TaskSchedulerException tex) { start = 0; // We don't want register execution time. Metrics.MarkThreadPoolRejection(); return(await GetFallbackOrThrowException(EventType.THREAD_POOL_REJECTED, FailureType.REJECTED_THREAD_EXECUTION, "Thread pool is full", tex.InnerException)); } catch (OperationCanceledException) { // timeout ms = _clock.EllapsedTimeInMs - start; Metrics.MarkTimeout(ms); return(await GetFallbackOrThrowException(EventType.TIMEOUT, FailureType.TIMEOUT, "timed-out", ServiceCommand <T> .TimeoutException)); } catch (Exception e) { ms = _clock.EllapsedTimeInMs - start; e = _executionHook.OnExecutionError(this, e); return(await OnExecutionError(ms, e)); } ms = _clock.EllapsedTimeInMs - start; Metrics.AddCommandExecutionTime(ms); Metrics.MarkSuccess(ms); _circuitBreaker.MarkSuccess(); _executionResult.AddEvent(EventType.SUCCESS); try { _executionHook.OnSuccess(this); } catch (Exception hookEx) { Logger.LogWarning("Error calling CommandExecutionHook.onSuccess", hookEx); } if (_requestCache != null) { var key = GetCacheKey(); cacheItem = new CacheItem { ExecutionResult = new ExecutionResult(_executionResult), Value = result }; cacheItem.ExecutionResult.AddEvent(EventType.RESPONSE_FROM_CACHE); cacheItem.ExecutionResult.ExecutionTime = -1; if (key != null && !_requestCache.TryAdd(key, cacheItem)) { _requestCache.TryGetValue(key, out cacheItem); _executionResult = cacheItem.ExecutionResult; result = cacheItem.Value; start = 0; } } return(result); } finally { ExecutionSemaphore.Release(); } } else { Metrics.MarkSemaphoreRejection(); return(await GetFallbackOrThrowException(EventType.SEMAPHORE_REJECTED, FailureType.REJECTED_SEMAPHORE_EXECUTION, "could not acquire a semaphore for execution", new Exception("could not acquire a semaphore for execution"))); } } else { start = 0; Metrics.MarkShortCircuited(); return(await GetFallbackOrThrowException(EventType.SHORT_CIRCUITED, FailureType.SHORTCIRCUIT, "short-circuited", new Exception(" circuit short-circuited and is OPEN"))); } } finally { if (start > 0) { RecordTotalExecutionTime(start); } Metrics.DecrementConcurrentExecutionCount(); _isExecutionComplete = true; } }