Hedging: Adds Read hedging PREVIEW contracts (#4598)

* Update contracts

* removed unneeded changes

* made other contracts public

* update enable method

* comments

* contract update

* changed eneabled to internal

* fixed internal method

* Revert "fixed internal method"

This reverts commit f7da9f06c4.

* revert + change methods to internal

* fixed internal

* changed to factory creation

* disabledstrat fix

* fixed test + contracts

* xml changes

* updated comments

* Fixed Tests

* requested changes

* added client options check and one region check

* fix get

* fixed test

* fixed set

* fix client options

* updatecontracts

* requested changes

* fixed validate method

* Delete MultiRegionSetupHelpers.cs

removed file

* update contracts

* fixed merge

* fixed check
This commit is contained in:
Nalu Tripician 2024-08-29 09:59:44 -04:00 коммит произвёл GitHub
Родитель 6d603f54bd
Коммит d242b8582e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
12 изменённых файлов: 223 добавлений и 45 удалений

Просмотреть файл

@ -694,7 +694,35 @@ namespace Microsoft.Azure.Cosmos
/// <summary>
/// Availability Strategy to be used for periods of high latency
/// </summary>
internal AvailabilityStrategy AvailabilityStrategy { get; set; }
/// /// <example>
/// An example on how to set an availability strategy custom serializer.
/// <code language="c#">
/// <![CDATA[
/// CosmosClient client = new CosmosClientBuilder("connection string")
/// .WithApplicationPreferredRegions(
/// new List<string> { "East US", "Central US", "West US" } )
/// .WithAvailabilityStrategy(
/// AvailabilityStrategy.CrossRegionHedgingStrategy(
/// threshold: TimeSpan.FromMilliseconds(500),
/// thresholdStep: TimeSpan.FromMilliseconds(100)
/// ))
/// .Build();
/// ]]>
/// </code>
/// </example>
/// <remarks>
/// The availability strategy in the example is a Cross Region Hedging Strategy.
/// These strategies take two values, a threshold and a threshold step.When a request that is sent
/// out takes longer than the threshold time, the SDK will hedge to the second region in the application preferred regions list.
/// If a response from either the primary request or the first hedged request is not received
/// after the threshold step time, the SDK will hedge to the third region and so on.
/// </remarks>
#if PREVIEW
public
#else
internal
#endif
AvailabilityStrategy AvailabilityStrategy { get; set; }
/// <summary>
/// Enable partition key level failover
@ -888,6 +916,7 @@ namespace Microsoft.Azure.Cosmos
this.ValidateDirectTCPSettings();
this.ValidateLimitToEndpointSettings();
this.ValidatePartitionLevelFailoverSettings();
this.ValidateAvailabilityStrategy();
ConnectionPolicy connectionPolicy = new ConnectionPolicy()
{
@ -1066,6 +1095,15 @@ namespace Microsoft.Azure.Cosmos
}
}
private void ValidateAvailabilityStrategy()
{
if (this.AvailabilityStrategy != null
&& this.ApplicationPreferredRegions == null && this.ApplicationRegion == null)
{
throw new ArgumentException($"{nameof(this.ApplicationPreferredRegions)} or {nameof(this.ApplicationRegion)} must be set to use {nameof(this.AvailabilityStrategy)}");
}
}
private void ValidateDirectTCPSettings()
{
string settingName = string.Empty;

Просмотреть файл

@ -705,7 +705,12 @@ namespace Microsoft.Azure.Cosmos.Fluent
/// </summary>
/// <param name="strategy"></param>
/// <returns>The CosmosClientBuilder</returns>
internal CosmosClientBuilder WithAvailibilityStrategy(AvailabilityStrategy strategy)
#if PREVIEW
public
#else
internal
#endif
CosmosClientBuilder WithAvailibilityStrategy(AvailabilityStrategy strategy)
{
this.clientOptions.AvailabilityStrategy = strategy;
return this;

Просмотреть файл

@ -79,7 +79,7 @@ namespace Microsoft.Azure.Cosmos.Handlers
await request.AssertPartitioningDetailsAsync(this.client, cancellationToken, request.Trace);
this.FillMultiMasterContext(request);
AvailabilityStrategy strategy = this.AvailabilityStrategy(request);
AvailabilityStrategyInternal strategy = this.AvailabilityStrategy(request);
ResponseMessage response = strategy != null && strategy.Enabled()
? await strategy.ExecuteAvailabilityStrategyAsync(
@ -103,10 +103,17 @@ namespace Microsoft.Azure.Cosmos.Handlers
/// </summary>
/// <param name="request"></param>
/// <returns>whether the request should be a parallel hedging request.</returns>
public AvailabilityStrategy AvailabilityStrategy(RequestMessage request)
public AvailabilityStrategyInternal AvailabilityStrategy(RequestMessage request)
{
return request.RequestOptions?.AvailabilityStrategy
AvailabilityStrategy strategy = request.RequestOptions?.AvailabilityStrategy
?? this.client.ClientOptions.AvailabilityStrategy;
if (strategy == null)
{
return null;
}
return strategy as AvailabilityStrategyInternal;
}
public virtual async Task<ResponseMessage> BaseSendAsync(

Просмотреть файл

@ -76,7 +76,12 @@ namespace Microsoft.Azure.Cosmos
/// reduce latency and increase availability. Currently there is one type of availability strategy, parallel request hedging.
/// If there is a globally enabled availability strategy, setting one in the request options will override the global one.
/// </summary>
internal AvailabilityStrategy AvailabilityStrategy { get; set; }
#if PREVIEW
public
#else
internal
#endif
AvailabilityStrategy AvailabilityStrategy { get; set; }
/// <summary>
/// Gets or sets the boolean to use effective partition key routing in the cosmos db request.

Просмотреть файл

@ -4,29 +4,46 @@
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Handlers;
/// <summary>
/// Types of availability strategies supported
/// </summary>
internal abstract class AvailabilityStrategy
#if PREVIEW
public
#else
internal
#endif
abstract class AvailabilityStrategy
{
/// <summary>
/// Execute the availability strategy
/// Default constructor
/// </summary>
/// <param name="sender"></param>
/// <param name="client"></param>
/// <param name="requestMessage"></param>
/// <param name="cancellationToken"></param>
/// <returns>The response from the service after the availability strategy is executed</returns>
public abstract Task<ResponseMessage> ExecuteAvailabilityStrategyAsync(
Func<RequestMessage, CancellationToken, Task<ResponseMessage>> sender,
CosmosClient client,
RequestMessage requestMessage,
CancellationToken cancellationToken);
internal AvailabilityStrategy()
{
}
internal abstract bool Enabled();
/// <summary>
/// Used on a per request level to disable a client level AvailabilityStrategy
/// </summary>
/// <returns>something</returns>
internal static AvailabilityStrategy DisabledStrategy()
{
return new DisabledAvailabilityStrategy();
}
/// <summary>
/// After a request's duration passes a threshold, this strategy will send out
/// hedged request to other regions. The first hedge request will be sent after the threshold.
/// After that, the strategy will send out a request every thresholdStep
/// until the request is completed or regions are exausted
/// </summary>
/// <param name="threshold"> how long before SDK begins hedging</param>
/// <param name="thresholdStep">Period of time between first hedge and next hedging attempts</param>
/// <returns>something</returns>
public static AvailabilityStrategy CrossRegionHedgingStrategy(TimeSpan threshold,
TimeSpan? thresholdStep)
{
return new CrossRegionHedgingAvailabilityStrategy(threshold, thresholdStep);
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Threading;
using System.Threading.Tasks;
internal abstract class AvailabilityStrategyInternal : AvailabilityStrategy
{
/// <summary>
/// Execute the availability strategy
/// </summary>
/// <param name="sender"></param>
/// <param name="client"></param>
/// <param name="requestMessage"></param>
/// <param name="cancellationToken"></param>
/// <returns>The response from the service after the availability strategy is executed</returns>
internal abstract Task<ResponseMessage> ExecuteAvailabilityStrategyAsync(
Func<RequestMessage, CancellationToken, Task<ResponseMessage>> sender,
CosmosClient client,
RequestMessage requestMessage,
CancellationToken cancellationToken);
/// <summary>
/// Checks to see if the strategy is enabled
/// </summary>
/// <returns>a bool representing if the strategy is enabled</returns>
internal abstract bool Enabled();
}
}

Просмотреть файл

@ -16,12 +16,12 @@ namespace Microsoft.Azure.Cosmos
using Microsoft.Azure.Documents;
/// <summary>
/// Parallel hedging availability strategy. Once threshold time is reached,
/// Hedging availability strategy. Once threshold time is reached,
/// the SDK will send out an additional request to a remote region in parallel
/// if the first parallel request or the original has not returned after the step time,
/// additional parallel requests will be sent out there is a response or all regions are exausted.
/// if the first hedging request or the original has not returned after the step time,
/// additional hedged requests will be sent out there is a response or all regions are exausted.
/// </summary>
internal class CrossRegionParallelHedgingAvailabilityStrategy : AvailabilityStrategy
internal class CrossRegionHedgingAvailabilityStrategy : AvailabilityStrategyInternal
{
private const string HedgeContext = "Hedge Context";
private const string ResponseRegion = "Response Region";
@ -37,11 +37,11 @@ namespace Microsoft.Azure.Cosmos
public TimeSpan ThresholdStep { get; private set; }
/// <summary>
/// Constructor for parallel hedging availability strategy
/// Constructor for hedging availability strategy
/// </summary>
/// <param name="threshold"></param>
/// <param name="thresholdStep"></param>
public CrossRegionParallelHedgingAvailabilityStrategy(
public CrossRegionHedgingAvailabilityStrategy(
TimeSpan threshold,
TimeSpan? thresholdStep)
{
@ -59,17 +59,18 @@ namespace Microsoft.Azure.Cosmos
this.ThresholdStep = thresholdStep ?? TimeSpan.FromMilliseconds(-1);
}
/// <inheritdoc/>
internal override bool Enabled()
{
return true;
}
/// <summary>
/// This method determines if the request should be sent with a parallel hedging availability strategy.
/// This method determines if the request should be sent with a hedging availability strategy.
/// This availability strategy can only be used if the request is a read-only request on a document request.
/// </summary>
/// <param name="request"></param>
/// <returns>whether the request should be a parallel hedging request.</returns>
/// <returns>whether the request should be a hedging request.</returns>
internal bool ShouldHedge(RequestMessage request)
{
//Only use availability strategy for document point operations
@ -88,20 +89,21 @@ namespace Microsoft.Azure.Cosmos
}
/// <summary>
/// Execute the parallel hedging availability strategy
/// Execute the hedging availability strategy
/// </summary>
/// <param name="sender"></param>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns>The response after executing cross region hedging</returns>
public override async Task<ResponseMessage> ExecuteAvailabilityStrategyAsync(
internal override async Task<ResponseMessage> ExecuteAvailabilityStrategyAsync(
Func<RequestMessage, CancellationToken, Task<ResponseMessage>> sender,
CosmosClient client,
RequestMessage request,
CancellationToken cancellationToken)
{
if (!this.ShouldHedge(request))
if (!this.ShouldHedge(request)
|| client.DocumentClient.GlobalEndpointManager.ReadEndpoints.Count == 1)
{
return await sender(request, cancellationToken);
}

Просмотреть файл

@ -10,8 +10,9 @@ namespace Microsoft.Azure.Cosmos
/// <summary>
/// A Disabled availability strategy that does not do anything. Used for overriding the default global availability strategy.
/// </summary>
internal class DisabledAvailabilityStrategy : AvailabilityStrategy
internal class DisabledAvailabilityStrategy : AvailabilityStrategyInternal
{
/// <inheritdoc/>
internal override bool Enabled()
{
return false;
@ -25,7 +26,7 @@ namespace Microsoft.Azure.Cosmos
/// <param name="requestMessage"></param>
/// <param name="cancellationToken"></param>
/// <returns>nothing, this will throw.</returns>
public override Task<ResponseMessage> ExecuteAvailabilityStrategyAsync(
internal override Task<ResponseMessage> ExecuteAvailabilityStrategyAsync(
Func<RequestMessage,
CancellationToken,
Task<ResponseMessage>> sender,

Просмотреть файл

@ -198,7 +198,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
{
ConnectionMode = ConnectionMode.Direct,
ApplicationPreferredRegions = new List<string>() { "Central US", "North Central US" },
AvailabilityStrategy = new CrossRegionParallelHedgingAvailabilityStrategy(
AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy(
threshold: TimeSpan.FromMilliseconds(300),
thresholdStep: TimeSpan.FromMilliseconds(50)),
Serializer = this.cosmosSystemTextJsonSerializer
@ -272,7 +272,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
ItemRequestOptions requestOptions = new ItemRequestOptions
{
AvailabilityStrategy = new CrossRegionParallelHedgingAvailabilityStrategy(
AvailabilityStrategy = new CrossRegionHedgingAvailabilityStrategy(
threshold: TimeSpan.FromMilliseconds(100),
thresholdStep: TimeSpan.FromMilliseconds(50))
};
@ -317,7 +317,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
{
ConnectionMode = ConnectionMode.Direct,
ApplicationPreferredRegions = new List<string>() { "Central US", "North Central US" },
AvailabilityStrategy = new CrossRegionParallelHedgingAvailabilityStrategy(
AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy(
threshold: TimeSpan.FromMilliseconds(100),
thresholdStep: TimeSpan.FromMilliseconds(50)),
Serializer = this.cosmosSystemTextJsonSerializer
@ -416,7 +416,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
{
ConnectionMode = ConnectionMode.Direct,
ApplicationPreferredRegions = new List<string>() { "Central US", "North Central US" },
AvailabilityStrategy = new CrossRegionParallelHedgingAvailabilityStrategy(
AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy(
threshold: TimeSpan.FromMilliseconds(100),
thresholdStep: TimeSpan.FromMilliseconds(50)),
Serializer = this.cosmosSystemTextJsonSerializer
@ -597,7 +597,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
{
ConnectionMode = ConnectionMode.Direct,
ApplicationPreferredRegions = new List<string>() { "Central US", "North Central US", "East US" },
AvailabilityStrategy = new CrossRegionParallelHedgingAvailabilityStrategy(
AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy(
threshold: TimeSpan.FromMilliseconds(100),
thresholdStep: TimeSpan.FromMilliseconds(50)),
Serializer = this.cosmosSystemTextJsonSerializer

Просмотреть файл

@ -105,7 +105,7 @@
ItemRequestOptions requestOptions = new ItemRequestOptions
{
AvailabilityStrategy = new CrossRegionParallelHedgingAvailabilityStrategy(
AvailabilityStrategy = AvailabilityStrategy.CrossRegionHedgingStrategy(
threshold: TimeSpan.FromMilliseconds(100),
thresholdStep: TimeSpan.FromMilliseconds(50))
};

Просмотреть файл

@ -1,5 +1,16 @@
{
"Subclasses": {
"Microsoft.Azure.Cosmos.AvailabilityStrategy;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
"Microsoft.Azure.Cosmos.AvailabilityStrategy CrossRegionHedgingStrategy(System.TimeSpan, System.Nullable`1[System.TimeSpan])": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy CrossRegionHedgingStrategy(System.TimeSpan, System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
}
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.ChangeFeedItem`1;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": {
"Subclasses": {},
"Members": {
@ -377,6 +388,31 @@
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.CosmosClientOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
"Microsoft.Azure.Cosmos.AvailabilityStrategy AvailabilityStrategy": {
"Type": "Property",
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy AvailabilityStrategy;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
"CompilerGeneratedAttribute"
],
"MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
"CompilerGeneratedAttribute"
],
"MethodInfo": "Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
}
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.DistanceFunction;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": {
"Subclasses": {},
"Members": {
@ -570,6 +606,17 @@
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithAvailibilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy)": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithAvailibilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
}
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.Fluent.IndexingPolicyDefinition`1;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:True;IsSerializable:False": {
"Subclasses": {},
"Members": {
@ -658,6 +705,31 @@
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.RequestOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
"Microsoft.Azure.Cosmos.AvailabilityStrategy AvailabilityStrategy": {
"Type": "Property",
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy AvailabilityStrategy;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
"CompilerGeneratedAttribute"
],
"MethodInfo": "Microsoft.Azure.Cosmos.AvailabilityStrategy get_AvailabilityStrategy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
"CompilerGeneratedAttribute"
],
"MethodInfo": "Void set_AvailabilityStrategy(Microsoft.Azure.Cosmos.AvailabilityStrategy);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
}
},
"NestedTypes": {}
},
"Microsoft.Azure.Cosmos.VectorDataType;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": {
"Subclasses": {},
"Members": {

Просмотреть файл

@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos.Tests.Telemetry
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
@ -19,7 +18,6 @@ namespace Microsoft.Azure.Cosmos.Tests.Telemetry
using Microsoft.Azure.Cosmos.Tracing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using static Microsoft.Azure.Cosmos.CrossRegionParallelHedgingAvailabilityStrategy;
[TestClass]
public class OpenTelemetryRecorderTests
@ -173,7 +171,7 @@ namespace Microsoft.Azure.Cosmos.Tests.Telemetry
Assert.AreEqual(
"HedgingResponse",
hedgingResponse,
"HedgingResponse is only used internally in the CrossRegionParallelHedgingAvailabilityStrategy and is never returned. No support Needed.");
"HedgingResponse is only used internally in the CrossRegionHedgingAvailabilityStrategy and is never returned. No support Needed.");
}
else
{