51Degrees / pipeline-dotnet

51Degrees Pipeline for .NET
Other
0 stars 2 forks source link

CloudRequestEngine throwing exceptions on construction when cloud.51degrees.com is down #44

Closed justadreamer closed 1 year ago

justadreamer commented 1 year ago

Problem

Observed

In Cloud/Framework-Web integration if cloud.51degrees.com is down or unavailable due to networking issues - the request to the IIS will fail in the PipelineModule due to the pipeline construction failing despite SuppressProcessExceptions is set to true in the configuration. The observed stack trace would look similar to this:

Exception Details: System.Net.WebException: The remote name could not be resolved: 'cloud.51degrees.com'

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[WebException: The remote name could not be resolved: 'cloud.51degrees.com']
   System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) +638
   System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) +65

[HttpRequestException: An error occurred while sending the request.]

[AggregateException: One or more errors occurred.]
   System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) +4316222
   System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) +12771596
   System.Threading.Tasks.Task`1.get_Result() +34
   FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngine.SendRequestAsync(HttpRequestMessage request) +53

[CloudRequestException: Error waiting for response from the cloud service.]
   FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngine.SendRequestAsync(HttpRequestMessage request) +99
   FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngine.AddCommonHeadersAndSend(HttpRequestMessage request) +226
   FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngine.GetCloudProperties() +98
   FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngine..ctor(ILogger`1 logger, Func`3 aspectDataFactory, HttpClient httpClient, String dataEndpoint, String resourceKey, String licenseKey, String propertiesEndpoint, String evidenceKeysEndpoint, Int32 timeout, List`1 requestedProperties, String cloudRequestOrigin) +593
   FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngineBuilder.NewEngine(List`1 properties) +120
   FiftyOne.Pipeline.Engines.FlowElements.AspectEngineBuilderBase`2.BuildEngine() +28
   FiftyOne.Pipeline.CloudRequestEngine.FlowElements.CloudRequestEngineBuilder.Build() +152

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
   System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +91
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +105
   System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +21
   FiftyOne.Pipeline.Core.FlowElements.PipelineBuilder.AddElementToList(List`1 elements, ElementOptions elementOptions, String elementLocation) +1750
   FiftyOne.Pipeline.Core.FlowElements.PipelineBuilder.BuildFromConfiguration(PipelineOptions options) +205
   FiftyOne.Pipeline.Web.Framework.WebPipeline..ctor() +1229
   FiftyOne.Pipeline.Web.Framework.WebPipeline.GetInstance() +107
   FiftyOne.Pipeline.Web.Framework.PipelineModule.OnBeginRequestJavascript(Object sender, EventArgs e) +59
   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +144
   System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +50
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +73

To reproduce you may disable cloud.51degrees.com in the hosts file by adding a line:

127.0.0.1    cloud.51degrees.com

Root Cause

The pipeline is not even constructed, because CloudRequestEngine as one of its flow elements throws during construction. This happens because on construction it requests the CloudProperties and CloudEvidenceKeys:

CloudRequestEngine https://github.com/51Degrees/pipeline-dotnet/blob/main/FiftyOne.Pipeline.CloudRequestEngine/FlowElements/CloudRequestEngine.cs#L151-L152

Solution

The design decision is that CloudProperties and CloudEvidenceKeys should be converted into lazy properties and should be requested from the server (requested once and then cached) - closer to the point of their use during processing of the evidence. This will allow any exceptions thrown from their initialization be controlled by the SuppressProcessExceptions option value and if it is set to true - the exceptions won't propagate up and fail the whole request to IIS. This should prevent the downtimes of the main service due to downtime of cloud.51degrees.com.