-
Notifications
You must be signed in to change notification settings - Fork 510
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Cosmos DB] Query max concurrency flag (#3669)
* Extending queries to use max concurrency parallelism * Add new Action Filter for Query Latency Over Efficiency * Adding new tests to ensure that FhirController has the expected attributes. Adding more tests on top of the new filter. Limiting the execution of the filter to Azure API for FHIR only.
- Loading branch information
Showing
16 changed files
with
441 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Controllers/FhirControllerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// ------------------------------------------------------------------------------------------------- | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. | ||
// ------------------------------------------------------------------------------------------------- | ||
|
||
using System; | ||
using System.Linq; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.Health.Api.Features.Audit; | ||
using Microsoft.Health.Fhir.Api.Controllers; | ||
using Microsoft.Health.Fhir.Api.Features.Filters; | ||
using Microsoft.Health.Fhir.Tests.Common; | ||
using Microsoft.Health.Test.Utilities; | ||
using Xunit; | ||
|
||
namespace Microsoft.Health.Fhir.Api.UnitTests.Controllers | ||
{ | ||
[Trait(Traits.OwningTeam, OwningTeam.Fhir)] | ||
[Trait(Traits.Category, Categories.Web)] | ||
public sealed class FhirControllerTests | ||
{ | ||
[Fact] | ||
public void WhenProviderAFhirController_CheckIfAllExpectedServiceFilterAttributesArePresent() | ||
{ | ||
Type[] expectedCustomAttributes = new Type[] | ||
{ | ||
typeof(AuditLoggingFilterAttribute), | ||
typeof(OperationOutcomeExceptionFilterAttribute), | ||
typeof(ValidateFormatParametersAttribute), | ||
typeof(QueryLatencyOverEfficiencyFilterAttribute), | ||
}; | ||
|
||
ServiceFilterAttribute[] serviceFilterAttributes = Attribute.GetCustomAttributes(typeof(FhirController), typeof(ServiceFilterAttribute)) | ||
.Select(a => a as ServiceFilterAttribute) | ||
.ToArray(); | ||
|
||
foreach (Type expectedCustomAttribute in expectedCustomAttributes) | ||
{ | ||
bool attributeWasFound = serviceFilterAttributes.Any(s => s.ServiceType == expectedCustomAttribute); | ||
|
||
if (!attributeWasFound) | ||
{ | ||
string errorMessage = $"The custom attribute '{expectedCustomAttribute.ToString()}' is not assigned to '{nameof(FhirController)}'."; | ||
Assert.Fail(errorMessage); | ||
} | ||
} | ||
} | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
...r.Shared.Api.UnitTests/Features/Filters/QueryLatencyOverEfficiencyFilterAttributeTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// ------------------------------------------------------------------------------------------------- | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. | ||
// ------------------------------------------------------------------------------------------------- | ||
|
||
using System.Collections.Generic; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.Abstractions; | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
using Microsoft.AspNetCore.Routing; | ||
using Microsoft.Health.Core.Features.Context; | ||
using Microsoft.Health.Fhir.Api.Features.Filters; | ||
using Microsoft.Health.Fhir.Core.Features; | ||
using Microsoft.Health.Fhir.Core.Features.Context; | ||
using Microsoft.Health.Fhir.Core.Registration; | ||
using Microsoft.Health.Fhir.Core.UnitTests.Features.Context; | ||
using Microsoft.Health.Fhir.Tests.Common; | ||
using Microsoft.Health.Test.Utilities; | ||
using NSubstitute; | ||
using Xunit; | ||
|
||
namespace Microsoft.Health.Fhir.Api.UnitTests.Features.Filters | ||
{ | ||
[Trait(Traits.OwningTeam, OwningTeam.Fhir)] | ||
[Trait(Traits.Category, Categories.Web)] | ||
public sealed class QueryLatencyOverEfficiencyFilterAttributeTests | ||
{ | ||
private readonly IFhirRuntimeConfiguration _azureApiForFhirConfiguration = new AzureApiForFhirRuntimeConfiguration(); | ||
private readonly IFhirRuntimeConfiguration _azureHealthDataServicesFhirConfiguration = new AzureHealthDataServicesRuntimeConfiguration(); | ||
|
||
[Fact] | ||
public void GivenAValidHttpContextForAzureApiForFhir_WhenItContainsALatencyOverEfficiencyFlag_ThenFhirContextIsDecorated() | ||
{ | ||
var httpRequest = GetFakeHttpContext(isLatencyOverEfficiencyEnabled: true); | ||
|
||
var filter = new QueryLatencyOverEfficiencyFilterAttribute(httpRequest.RequestContext, _azureApiForFhirConfiguration); | ||
filter.OnActionExecuting(httpRequest.ActionContext); | ||
|
||
var fhirContextPropertyBag = httpRequest.RequestContext.RequestContext.Properties; | ||
|
||
Assert.True(fhirContextPropertyBag.ContainsKey(KnownQueryParameterNames.OptimizeConcurrency)); | ||
Assert.Equal(true, fhirContextPropertyBag[KnownQueryParameterNames.OptimizeConcurrency]); | ||
} | ||
|
||
[Fact] | ||
public void GivenAValidHttpContextForAzureHealthDataService_WhenItContainsALatencyOverEfficiencyFlag_ThenFhirContextIsNotDecorated() | ||
{ | ||
// The latency-over-efficiency flag is only applicable to Azure API for FHIR. | ||
|
||
var httpRequest = GetFakeHttpContext(isLatencyOverEfficiencyEnabled: true); | ||
|
||
var filter = new QueryLatencyOverEfficiencyFilterAttribute(httpRequest.RequestContext, _azureHealthDataServicesFhirConfiguration); | ||
filter.OnActionExecuting(httpRequest.ActionContext); | ||
|
||
var fhirContextPropertyBag = httpRequest.RequestContext.RequestContext.Properties; | ||
|
||
Assert.False(fhirContextPropertyBag.ContainsKey(KnownQueryParameterNames.OptimizeConcurrency)); | ||
} | ||
|
||
[Fact] | ||
public void GivenAValidHttpContext_WhenItDoesNotContainALatencyOverEfficiencyFlag_ThenFhirContextIsClean() | ||
{ | ||
var httpRequest = GetFakeHttpContext(isLatencyOverEfficiencyEnabled: false); | ||
|
||
var filter = new QueryLatencyOverEfficiencyFilterAttribute(httpRequest.RequestContext, _azureApiForFhirConfiguration); | ||
filter.OnActionExecuting(httpRequest.ActionContext); | ||
|
||
var fhirContextPropertyBag = httpRequest.RequestContext.RequestContext.Properties; | ||
|
||
Assert.False(fhirContextPropertyBag.ContainsKey(KnownQueryParameterNames.OptimizeConcurrency)); | ||
} | ||
|
||
private static (RequestContextAccessor<IFhirRequestContext> RequestContext, ActionExecutingContext ActionContext) GetFakeHttpContext(bool isLatencyOverEfficiencyEnabled) | ||
{ | ||
var httpContext = new DefaultHttpContext(); | ||
|
||
if (isLatencyOverEfficiencyEnabled) | ||
{ | ||
httpContext.Request.Headers[KnownHeaders.QueryLatencyOverEfficiency] = "true"; | ||
} | ||
|
||
ActionExecutingContext context = new ActionExecutingContext( | ||
new ActionContext( | ||
httpContext, | ||
new RouteData(), | ||
new ActionDescriptor()), | ||
new List<IFilterMetadata>(), | ||
actionArguments: new Dictionary<string, object>(), | ||
FilterTestsHelper.CreateMockFhirController()); | ||
|
||
DefaultFhirRequestContext fhirRequestContext = new DefaultFhirRequestContext(); | ||
|
||
var fhirRequestContextAccessor = Substitute.For<RequestContextAccessor<IFhirRequestContext>>(); | ||
fhirRequestContextAccessor.RequestContext.Returns(fhirRequestContext); | ||
|
||
return (fhirRequestContextAccessor, context); | ||
} | ||
} | ||
} |
153 changes: 153 additions & 0 deletions
153
...Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Headers/HttpContextExtensionsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// ------------------------------------------------------------------------------------------------- | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. | ||
// ------------------------------------------------------------------------------------------------- | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Health.Fhir.Api.Features.Headers; | ||
using Microsoft.Health.Fhir.Api.Features.Resources; | ||
using Microsoft.Health.Fhir.Api.Features.Resources.Bundle; | ||
using Microsoft.Health.Fhir.Core.Features; | ||
using Microsoft.Health.Fhir.Core.Features.Context; | ||
using Microsoft.Health.Fhir.Core.Features.Persistence.Orchestration; | ||
using Microsoft.Health.Fhir.Tests.Common; | ||
using Microsoft.Health.Test.Utilities; | ||
using Xunit; | ||
|
||
namespace Microsoft.Health.Fhir.Api.UnitTests.Features.Headers | ||
{ | ||
[Trait(Traits.OwningTeam, OwningTeam.Fhir)] | ||
[Trait(Traits.Category, Categories.Web)] | ||
public sealed class HttpContextExtensionsTests | ||
{ | ||
[Fact] | ||
public void WhenHttpContextDoesNotHaveCustomHeaders_ReturnDefaultValues() | ||
{ | ||
HttpContext httpContext = GetFakeHttpContext(); | ||
|
||
bool isLatencyOverEfficiencyEnabled = httpContext.IsLatencyOverEfficiencyEnabled(); | ||
Assert.False(isLatencyOverEfficiencyEnabled); | ||
|
||
BundleProcessingLogic bundleProcessingLogic = httpContext.GetBundleProcessingLogic(); | ||
Assert.Equal(BundleProcessingLogic.Sequential, bundleProcessingLogic); | ||
|
||
// #conditionalQueryParallelism | ||
ConditionalQueryProcessingLogic conditionalQueryProcessingLogic = httpContext.GetConditionalQueryProcessingLogic(); | ||
Assert.Equal(ConditionalQueryProcessingLogic.Sequential, conditionalQueryProcessingLogic); | ||
} | ||
|
||
[Theory] | ||
[InlineData("", false)] | ||
[InlineData(null, false)] | ||
[InlineData("false", false)] | ||
[InlineData("falsE", false)] | ||
[InlineData("FALSE", false)] | ||
[InlineData("2112", false)] | ||
[InlineData("true", true)] | ||
[InlineData("true ", true)] | ||
[InlineData("TRUE", true)] | ||
[InlineData(" TRUE ", true)] | ||
[InlineData(" tRuE ", true)] | ||
public void WhenHttpContextHasCustomHeaders_ReturnIfLatencyOverEfficiencyIsEnabled(string value, bool isEnabled) | ||
{ | ||
var httpHeaders = new Dictionary<string, string>() { { KnownHeaders.QueryLatencyOverEfficiency, value } }; | ||
HttpContext httpContext = GetFakeHttpContext(httpHeaders); | ||
|
||
bool isLatencyOverEfficiencyEnabled = httpContext.IsLatencyOverEfficiencyEnabled(); | ||
|
||
Assert.Equal(isEnabled, isLatencyOverEfficiencyEnabled); | ||
} | ||
|
||
[Theory] | ||
[InlineData("", ConditionalQueryProcessingLogic.Sequential)] | ||
[InlineData(null, ConditionalQueryProcessingLogic.Sequential)] | ||
[InlineData("sequential", ConditionalQueryProcessingLogic.Sequential)] | ||
[InlineData("sequential ", ConditionalQueryProcessingLogic.Sequential)] | ||
[InlineData("Sequential", ConditionalQueryProcessingLogic.Sequential)] | ||
[InlineData("2112", ConditionalQueryProcessingLogic.Sequential)] | ||
[InlineData("red barchetta", ConditionalQueryProcessingLogic.Sequential)] | ||
[InlineData("parallel", ConditionalQueryProcessingLogic.Parallel)] | ||
[InlineData("parallel ", ConditionalQueryProcessingLogic.Parallel)] | ||
[InlineData("Parallel", ConditionalQueryProcessingLogic.Parallel)] | ||
[InlineData(" pArAllEl ", ConditionalQueryProcessingLogic.Parallel)] | ||
[InlineData("PARALLEL", ConditionalQueryProcessingLogic.Parallel)] | ||
public void WhenHttpContextHasCustomHeaders_ReturnIfConditionalQueryProcessingLogicIsSet(string value, ConditionalQueryProcessingLogic processingLogic) | ||
{ | ||
// #conditionalQueryParallelism | ||
|
||
var httpHeaders = new Dictionary<string, string>() { { KnownHeaders.ConditionalQueryProcessingLogic, value } }; | ||
HttpContext httpContext = GetFakeHttpContext(httpHeaders); | ||
|
||
ConditionalQueryProcessingLogic conditionalQueryProcessingLogic = httpContext.GetConditionalQueryProcessingLogic(); | ||
|
||
Assert.Equal(processingLogic, conditionalQueryProcessingLogic); | ||
} | ||
|
||
[Theory] | ||
[InlineData("", BundleProcessingLogic.Sequential)] | ||
[InlineData(null, BundleProcessingLogic.Sequential)] | ||
[InlineData("sequential", BundleProcessingLogic.Sequential)] | ||
[InlineData("sequential ", BundleProcessingLogic.Sequential)] | ||
[InlineData("Sequential", BundleProcessingLogic.Sequential)] | ||
[InlineData("2112", BundleProcessingLogic.Sequential)] | ||
[InlineData("red barchetta", BundleProcessingLogic.Sequential)] | ||
[InlineData("parallel", BundleProcessingLogic.Parallel)] | ||
[InlineData("parallel ", BundleProcessingLogic.Parallel)] | ||
[InlineData("Parallel", BundleProcessingLogic.Parallel)] | ||
[InlineData(" pArAllEl ", BundleProcessingLogic.Parallel)] | ||
[InlineData("PARALLEL", BundleProcessingLogic.Parallel)] | ||
public void WhenHttpContextHasCustomHeaders_ReturnIfBundleProcessingLogicIsSet(string value, BundleProcessingLogic processingLogic) | ||
{ | ||
var httpHeaders = new Dictionary<string, string>() { { BundleOrchestratorNamingConventions.HttpHeaderBundleProcessingLogic, value } }; | ||
HttpContext httpContext = GetFakeHttpContext(httpHeaders); | ||
|
||
BundleProcessingLogic bundleProcessingLogic = httpContext.GetBundleProcessingLogic(); | ||
|
||
Assert.Equal(processingLogic, bundleProcessingLogic); | ||
} | ||
|
||
[Fact] | ||
public void WhenProvidedAFhirRequestContext_ThenDecorateItWithOptimizeConcurrency() | ||
{ | ||
// #conditionalQueryParallelism | ||
|
||
IFhirRequestContext fhirRequestContext = new Core.UnitTests.Features.Context.DefaultFhirRequestContext() | ||
{ | ||
BaseUri = new Uri("https://localhost/"), | ||
CorrelationId = Guid.NewGuid().ToString(), | ||
ResponseHeaders = new HeaderDictionary(), | ||
RequestHeaders = new HeaderDictionary(), | ||
}; | ||
|
||
fhirRequestContext.DecorateRequestContextWithOptimizedConcurrency(); | ||
|
||
Assert.True(fhirRequestContext.Properties.ContainsKey(KnownQueryParameterNames.OptimizeConcurrency)); | ||
Assert.Equal(true, fhirRequestContext.Properties[KnownQueryParameterNames.OptimizeConcurrency]); | ||
} | ||
|
||
private static HttpContext GetFakeHttpContext(IReadOnlyDictionary<string, string> optionalHttpHeaders = default) | ||
{ | ||
var httpContext = new DefaultHttpContext() | ||
{ | ||
Request = | ||
{ | ||
Scheme = "https", | ||
Host = new HostString("localhost"), | ||
PathBase = new PathString("/"), | ||
}, | ||
}; | ||
|
||
if (optionalHttpHeaders != null) | ||
{ | ||
foreach (var header in optionalHttpHeaders) | ||
{ | ||
httpContext.Request.Headers.Append(header.Key, header.Value); | ||
} | ||
} | ||
|
||
return httpContext; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.