Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CrossRegionHedging: Fixes NullReference Exception Bug #4869

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ internal override async Task<ResponseMessage> ExecuteAvailabilityStrategyAsync(
request,
hedgeRegions.ElementAt(requestNumber),
cancellationToken,
cancellationTokenSource);
cancellationTokenSource,
trace);

requestTasks.Add(primaryRequest);
}
Expand Down Expand Up @@ -262,7 +263,8 @@ private async Task<HedgingResponse> CloneAndSendAsync(
clonedRequest,
region,
cancellationToken,
cancellationTokenSource);
cancellationTokenSource,
trace);
}
}

Expand All @@ -271,7 +273,8 @@ private async Task<HedgingResponse> RequestSenderAndResultCheckAsync(
RequestMessage request,
string hedgedRegion,
CancellationToken cancellationToken,
CancellationTokenSource cancellationTokenSource)
CancellationTokenSource cancellationTokenSource,
ITrace trace)
{
try
{
Expand All @@ -288,9 +291,9 @@ private async Task<HedgingResponse> RequestSenderAndResultCheckAsync(

return new HedgingResponse(false, response, hedgedRegion);
}
catch (OperationCanceledException) when (cancellationTokenSource.IsCancellationRequested)
catch (OperationCanceledException oce ) when (cancellationTokenSource.IsCancellationRequested)
dibahlfi marked this conversation as resolved.
Show resolved Hide resolved
{
return new HedgingResponse(false, null, hedgedRegion);
throw new CosmosOperationCanceledException(oce, trace);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,60 @@ public async Task AvailabilityStrategyStepTests(string operation, string condito
}
}

[TestMethod]
[TestCategory("MultiRegion")]
public async Task AvailabilityStrategyWithCancellationTokenThrowsExceptionTest()
{
FaultInjectionRule responseDelay = new FaultInjectionRuleBuilder(
id: "responseDely",
condition:
new FaultInjectionConditionBuilder()
.WithRegion("Central US")
.WithOperationType(FaultInjectionOperationType.ReadItem)
.Build(),
result:
FaultInjectionResultBuilder.GetResultBuilder(FaultInjectionServerErrorType.ResponseDelay)
.WithDelay(TimeSpan.FromMilliseconds(6000))
.Build())
.WithDuration(TimeSpan.FromMinutes(90))
.WithHitLimit(2)
.Build();

List<FaultInjectionRule> rules = new List<FaultInjectionRule>() { responseDelay };
FaultInjector faultInjector = new FaultInjector(rules);

responseDelay.Disable();

CosmosClientOptions clientOptions = new CosmosClientOptions()
{
ConnectionMode = ConnectionMode.Direct,
ApplicationPreferredRegions = new List<string>() { "Central US", "North Central US" },
AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy(
threshold: TimeSpan.FromMilliseconds(300),
thresholdStep: null),
Serializer = this.cosmosSystemTextJsonSerializer
};

using (CosmosClient faultInjectionClient = new CosmosClient(
connectionString: this.connectionString,
clientOptions: faultInjector.GetFaultInjectionClientOptions(clientOptions)))
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.Cancel();

Database database = faultInjectionClient.GetDatabase(CosmosAvailabilityStrategyTests.dbName);
Container container = database.GetContainer(CosmosAvailabilityStrategyTests.containerName);

CosmosOperationCanceledException cancelledException = await Assert.ThrowsExceptionAsync<CosmosOperationCanceledException>(() =>
container.ReadItemAsync<AvailabilityStrategyTestObject>(
"testId",
new PartitionKey("pk"), cancellationToken: cts.Token
));

}

}

private static async Task HandleChangesAsync(
ChangeFeedProcessorContext context,
IReadOnlyCollection<AvailabilityStrategyTestObject> changes,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
namespace Microsoft.Azure.Cosmos.Tests
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Documents;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using System.Net;
/// <summary>
/// Tests for <see cref="AvailabilityStrategy"/>
/// </summary>
Expand Down Expand Up @@ -58,5 +59,42 @@ public async Task RequestMessageCloneTests()
Assert.AreEqual(httpRequest.DatabaseId, clone.DatabaseId);
}
}

[TestMethod]
public async Task CancellationTokenThrowsExceptionTest()
{
//Arrange
CrossRegionHedgingAvailabilityStrategy availabilityStrategy = new CrossRegionHedgingAvailabilityStrategy(
threshold: TimeSpan.FromMilliseconds(100),
thresholdStep: TimeSpan.FromMilliseconds(50));

RequestMessage request = new RequestMessage
{
ResourceType = ResourceType.Document,
OperationType = OperationType.Read
};

CancellationTokenSource cts = new CancellationTokenSource();
cts.Cancel();

AccountProperties databaseAccount = new AccountProperties()
{
ReadLocationsInternal = new Collection<AccountRegion>()
{
{ new AccountRegion() { Name = "US East", Endpoint = new Uri("https://location1.documents.azure.com").ToString() } },
{ new AccountRegion() { Name = "US West", Endpoint = new Uri("https://location2.documents.azure.com").ToString() } },

}
};
using CosmosClient mockCosmosClient = MockCosmosUtil.CreateMockCosmosClient();
mockCosmosClient.DocumentClient.GlobalEndpointManager.InitializeAccountPropertiesAndStartBackgroundRefresh(databaseAccount);

Func<RequestMessage, CancellationToken, Task<ResponseMessage>> sender = (request, token) => throw new OperationCanceledException("operation cancellation requested");

CosmosOperationCanceledException cancelledException = await Assert.ThrowsExceptionAsync<CosmosOperationCanceledException>(() =>
availabilityStrategy.ExecuteAvailabilityStrategyAsync(sender, mockCosmosClient, request, cts.Token));
}


}
}
Loading