Skip to content

Commit

Permalink
Headers: Adds optimized request headers (#3177)
Browse files Browse the repository at this point in the history
Refactor the headers to use the new optimized header

Header structure:

Headers -> public contract (Ideally this should have been a public contract without implementation)
CosmosMessageHeadersInternal -> Internal contract the v3 SDK uses to workaround breaking changes since headers can not be modified. This is common headers used in both the request and response.

StoreRequestHeaders -> converts the RequestNameValueCollection to the CosmosMessageHeadersInternal
StoreResponseHeaders -> converts the StoreResponseNameValueCollection to the CosmosMessageHeadersInternal
HttpResponseHeadersWrapper -> gateway response implementation of CosmosMessageHeadersInternal

Performance results which shows roughly 7% increase in throughput:
  • Loading branch information
j82w authored May 17, 2022
1 parent 416f2a7 commit cda62eb
Show file tree
Hide file tree
Showing 49 changed files with 469 additions and 1,854 deletions.
8 changes: 4 additions & 4 deletions Microsoft.Azure.Cosmos/src/DocumentClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6732,7 +6732,7 @@ private INameValueCollection GetRequestHeaders(
this.isSuccessfullyInitialized,
"GetRequestHeaders should be called after initialization task has been awaited to avoid blocking while accessing ConsistencyLevel property");

INameValueCollection headers = new StoreRequestNameValueCollection();
RequestNameValueCollection headers = new RequestNameValueCollection();

if (this.UseMultipleWriteLocations)
{
Expand All @@ -6755,7 +6755,7 @@ private INameValueCollection GetRequestHeaders(
this.accountServiceConfiguration.DefaultConsistencyLevel));
}

headers.Set(HttpConstants.HttpHeaders.ConsistencyLevel, this.desiredConsistencyLevel.Value.ToString());
headers.ConsistencyLevel = this.desiredConsistencyLevel.Value.ToString();
}

if (options == null)
Expand All @@ -6767,11 +6767,11 @@ private INameValueCollection GetRequestHeaders(
{
if (options.AccessCondition.Type == Documents.Client.AccessConditionType.IfMatch)
{
headers.Set(HttpConstants.HttpHeaders.IfMatch, options.AccessCondition.Condition);
headers.IfMatch = options.AccessCondition.Condition;
}
else
{
headers.Set(HttpConstants.HttpHeaders.IfNoneMatch, options.AccessCondition.Condition);
headers.IfNoneMatch = options.AccessCondition.Condition;
}
}

Expand Down
2 changes: 1 addition & 1 deletion Microsoft.Azure.Cosmos/src/GatewayAccountReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public GatewayAccountReader(Uri serviceEndpoint,

private async Task<AccountProperties> GetDatabaseAccountAsync(Uri serviceEndpoint)
{
INameValueCollection headers = new StoreRequestNameValueCollection();
INameValueCollection headers = new RequestNameValueCollection();
await this.cosmosAuthorization.AddAuthorizationHeaderAsync(
headersCollection: headers,
serviceEndpoint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ await this.partitionRoutingHelper.TryGetTargetRangeFromContinuationTokenRangeAsy
else
{
if (!await this.partitionRoutingHelper.TryAddPartitionKeyRangeToContinuationTokenAsync(
response.Headers.CosmosMessageHeaders,
response.Headers.CosmosMessageHeaders.INameValueCollection,
providedPartitionKeyRanges: providedRanges,
routingMapProvider: routingMapProvider,
collectionRid: collectionFromCache.ResourceId,
Expand Down
10 changes: 5 additions & 5 deletions Microsoft.Azure.Cosmos/src/Handler/RequestInvokerHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public virtual async Task<ResponseMessage> SendAsync(
Content = streamPayload,
};

request.Headers[HttpConstants.HttpHeaders.SDKSupportedCapabilities] = Headers.SDKSupportedCapabilities;
request.Headers.SDKSupportedCapabilities = Headers.SDKSUPPORTEDCAPABILITIES;

if (feedRange != null)
{
Expand Down Expand Up @@ -257,9 +257,9 @@ public virtual async Task<ResponseMessage> SendAsync(
// In this case we route to the physical partition and
// pass the epk range headers to filter within partition
request.PartitionKeyRangeId = new Documents.PartitionKeyRangeIdentity(overlappingRanges[0].Id);
request.Headers[HttpConstants.HttpHeaders.ReadFeedKeyType] = RntbdConstants.RntdbReadFeedKeyType.EffectivePartitionKeyRange.ToString();
request.Headers[HttpConstants.HttpHeaders.StartEpk] = feedRangeEpk.Range.Min;
request.Headers[HttpConstants.HttpHeaders.EndEpk] = feedRangeEpk.Range.Max;
request.Headers.ReadFeedKeyType = RntbdConstants.RntdbReadFeedKeyType.EffectivePartitionKeyRange.ToString();
request.Headers.StartEpk = feedRangeEpk.Range.Min;
request.Headers.EndEpk = feedRangeEpk.Range.Max;
}
}
}
Expand Down Expand Up @@ -396,7 +396,7 @@ private async Task ValidateAndSetConsistencyLevelAsync(RequestMessage requestMes
resourceType: requestMessage.ResourceType))
{
// ConsistencyLevel compatibility with back-end configuration will be done by RequestInvokeHandler
requestMessage.Headers.Add(HttpConstants.HttpHeaders.ConsistencyLevel, consistencyLevel.Value.ToString());
requestMessage.Headers.ConsistencyLevel = consistencyLevel.Value.ToString();
}
else
{
Expand Down
4 changes: 2 additions & 2 deletions Microsoft.Azure.Cosmos/src/Handler/RequestMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ internal DocumentServiceRequest ToDocumentServiceRequest()
resourceIdOrFullName: null,
resourceType: this.ResourceType,
body: this.Content,
headers: this.Headers.CosmosMessageHeaders,
headers: this.Headers.CosmosMessageHeaders.INameValueCollection,
isNameBased: false,
authorizationTokenType: AuthorizationTokenType.PrimaryMasterKey);
}
Expand All @@ -263,7 +263,7 @@ internal DocumentServiceRequest ToDocumentServiceRequest()
this.RequestUriString,
this.Content,
AuthorizationTokenType.PrimaryMasterKey,
this.Headers.CosmosMessageHeaders);
this.Headers.CosmosMessageHeaders.INameValueCollection);
}

if (this.UseGatewayMode.HasValue)
Expand Down
53 changes: 39 additions & 14 deletions Microsoft.Azure.Cosmos/src/Headers/CosmosMessageHeadersInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.Azure.Cosmos
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Collections;

internal abstract class CosmosMessageHeadersInternal : INameValueCollection
internal abstract class CosmosMessageHeadersInternal
{
public virtual string Authorization
{
Expand Down Expand Up @@ -141,6 +141,44 @@ public virtual string PageSize
set => this.SetProperty(HttpConstants.HttpHeaders.PageSize, value);
}

public virtual string ConsistencyLevel
{
get => this.GetValueOrDefault(HttpConstants.HttpHeaders.ConsistencyLevel);
set => this.SetProperty(HttpConstants.HttpHeaders.ConsistencyLevel, value);
}

public virtual string SDKSupportedCapabilities
{
get => this.GetValueOrDefault(HttpConstants.HttpHeaders.SDKSupportedCapabilities);
set => this.SetProperty(HttpConstants.HttpHeaders.SDKSupportedCapabilities, value);
}

public virtual string ContentSerializationFormat
{
get => this.GetValueOrDefault(HttpConstants.HttpHeaders.ContentSerializationFormat);
set => this.SetProperty(HttpConstants.HttpHeaders.ContentSerializationFormat, value);
}

public virtual string ReadFeedKeyType
{
get => this.GetValueOrDefault(HttpConstants.HttpHeaders.ReadFeedKeyType);
set => this.SetProperty(HttpConstants.HttpHeaders.ReadFeedKeyType, value);
}

public virtual string StartEpk
{
get => this.GetValueOrDefault(HttpConstants.HttpHeaders.StartEpk);
set => this.SetProperty(HttpConstants.HttpHeaders.StartEpk, value);
}

public virtual string EndEpk
{
get => this.GetValueOrDefault(HttpConstants.HttpHeaders.EndEpk);
set => this.SetProperty(HttpConstants.HttpHeaders.EndEpk, value);
}

public abstract INameValueCollection INameValueCollection { get; }

public virtual string this[string headerName]
{
get
Expand Down Expand Up @@ -170,18 +208,10 @@ public virtual string this[string headerName]

public abstract string[] AllKeys();

public abstract void Clear();

public abstract int Count();

public abstract INameValueCollection Clone();

public abstract string[] GetValues(string key);

public abstract IEnumerable<string> Keys();

public abstract NameValueCollection ToNameValueCollection();

protected void SetProperty(
string headerName,
string value)
Expand Down Expand Up @@ -245,10 +275,5 @@ public virtual void Add(INameValueCollection collection)
this.Set(key, collection[key]);
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
42 changes: 38 additions & 4 deletions Microsoft.Azure.Cosmos/src/Headers/Headers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Microsoft.Azure.Cosmos
/// <seealso cref="RequestMessage"/>
public class Headers : IEnumerable
{
private static readonly string sdkSupportedCapabilities = SDKSupportedCapabilitiesHelpers.GetSDKSupportedCapabilities().ToString(
internal static readonly string SDKSUPPORTEDCAPABILITIES = SDKSupportedCapabilitiesHelpers.GetSDKSupportedCapabilities().ToString(
CultureInfo.InvariantCulture);

internal virtual SubStatusCodes SubStatusCode
Expand Down Expand Up @@ -210,12 +210,48 @@ internal virtual string BackendRequestDurationMilliseconds
set => this.CosmosMessageHeaders.BackendRequestDurationMilliseconds = value;
}

internal virtual string ConsistencyLevel
{
get => this.CosmosMessageHeaders.ConsistencyLevel;
set => this.CosmosMessageHeaders.ConsistencyLevel = value;
}

internal virtual string SDKSupportedCapabilities
{
get => this.CosmosMessageHeaders.SDKSupportedCapabilities;
set => this.CosmosMessageHeaders.SDKSupportedCapabilities = value;
}

internal virtual string ContentSerializationFormat
{
get => this.CosmosMessageHeaders.ContentSerializationFormat;
set => this.CosmosMessageHeaders.ContentSerializationFormat = value;
}

internal virtual string ReadFeedKeyType
{
get => this.CosmosMessageHeaders.ReadFeedKeyType;
set => this.CosmosMessageHeaders.ReadFeedKeyType = value;
}

internal virtual string StartEpk
{
get => this.CosmosMessageHeaders.StartEpk;
set => this.CosmosMessageHeaders.StartEpk = value;
}

internal virtual string EndEpk
{
get => this.CosmosMessageHeaders.EndEpk;
set => this.CosmosMessageHeaders.EndEpk = value;
}

/// <summary>
/// Creates a new instance of <see cref="Headers"/>.
/// </summary>
public Headers()
{
this.CosmosMessageHeaders = new StoreRequestNameValueCollection();
this.CosmosMessageHeaders = new StoreRequestHeaders();
}

internal Headers(INameValueCollection nameValueCollection)
Expand Down Expand Up @@ -398,7 +434,5 @@ internal static SubStatusCodes GetSubStatusCodes(string value)

return null;
}

internal static string SDKSupportedCapabilities => Headers.sdkSupportedCapabilities;
}
}
17 changes: 12 additions & 5 deletions Microsoft.Azure.Cosmos/src/Headers/HttpResponseHeadersWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ namespace Microsoft.Azure.Cosmos
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Collections;

internal sealed class HttpResponseHeadersWrapper : CosmosMessageHeadersInternal
internal sealed class HttpResponseHeadersWrapper : CosmosMessageHeadersInternal, INameValueCollection
{
private readonly HttpResponseHeaders httpResponseHeaders;
private readonly HttpContentHeaders httpContentHeaders;
private readonly Lazy<DictionaryNameValueCollection> dictionaryNameValueCollection;

public override INameValueCollection INameValueCollection => this;

/// <summary>
/// HttpResponse can have 2 headers. These headers have restrictions on what values are allowed.
/// This optimizes to combine the 2 headers without iterating overall of them to duplicate it into a new
Expand Down Expand Up @@ -64,7 +66,7 @@ public override void Add(INameValueCollection collection)
this.dictionaryNameValueCollection.Value.Add(collection);
}

public override void Clear()
public void Clear()
{
this.httpResponseHeaders.Clear();

Expand All @@ -90,7 +92,7 @@ public override string[] AllKeys()
return this.Keys().ToArray();
}

public override INameValueCollection Clone()
public INameValueCollection Clone()
{
INameValueCollection headers = new DictionaryNameValueCollection();

Expand Down Expand Up @@ -175,7 +177,7 @@ public override string[] GetValues(string key)
return this.httpResponseHeaders.GetValues(key).ToArray();
}

public override IEnumerable<string> Keys()
public IEnumerable<string> Keys()
{
foreach (KeyValuePair<string, IEnumerable<string>> header in this.AllItems())
{
Expand Down Expand Up @@ -209,7 +211,7 @@ public override void Set(string key, string value)
this.Add(key, value);
}

public override NameValueCollection ToNameValueCollection()
public NameValueCollection ToNameValueCollection()
{
NameValueCollection nameValueCollection = new NameValueCollection();
foreach (KeyValuePair<string, IEnumerable<string>> header in this.AllItems())
Expand All @@ -224,5 +226,10 @@ private string JoinHeaders(IEnumerable<string> headerValues)
{
return headerValues == null ? null : string.Join(",", headerValues);
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
Loading

0 comments on commit cda62eb

Please sign in to comment.