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

Throughput fix for throwing on not found and using custom serializer #772

Merged
merged 9 commits into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions Microsoft.Azure.Cosmos/src/Resource/CosmosResponseFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,44 @@ internal CosmosResponseFactory(
this.cosmosSerializer = userJsonSerializer;
}

internal FeedResponse<T> CreateQueryFeedResponseWithPropertySerializer<T>(
ResponseMessage cosmosResponseMessage)
{
return this.CreateQueryFeedResponseHelper<T>(
cosmosResponseMessage,
true);
}

internal FeedResponse<T> CreateQueryFeedResponse<T>(
ResponseMessage cosmosResponseMessage)
{
return this.CreateQueryFeedResponseHelper<T>(
cosmosResponseMessage,
false);
}

private FeedResponse<T> CreateQueryFeedResponseHelper<T>(
ResponseMessage cosmosResponseMessage,
bool usePropertySerializer)
{
//Throw the exception
cosmosResponseMessage.EnsureSuccessStatusCode();

// The property serializer should be used for internal
// query operations like throughput since user serializer can break the logic
CosmosSerializer serializer = usePropertySerializer ? this.propertiesSerializer : this.cosmosSerializer;

QueryResponse queryResponse = cosmosResponseMessage as QueryResponse;
if (queryResponse != null)
{
return QueryResponse<T>.CreateResponse<T>(
cosmosQueryResponse: queryResponse,
jsonSerializer: this.cosmosSerializer);
jsonSerializer: serializer);
}

return ReadFeedResponse<T>.CreateResponse<T>(
cosmosResponseMessage,
this.cosmosSerializer);
serializer);
}

internal Task<ItemResponse<T>> CreateItemResponseAsync<T>(
Expand Down Expand Up @@ -112,7 +133,10 @@ internal Task<DatabaseResponse> CreateDatabaseResponseAsync(
{
return this.ProcessMessageAsync(cosmosResponseMessageTask, (cosmosResponseMessage) =>
{
DatabaseProperties databaseProperties = this.ToObjectInternal<DatabaseProperties>(cosmosResponseMessage, this.propertiesSerializer);
DatabaseProperties databaseProperties = this.ToObjectInternal<DatabaseProperties>(
cosmosResponseMessage,
this.propertiesSerializer);

return new DatabaseResponse(
cosmosResponseMessage.StatusCode,
cosmosResponseMessage.Headers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public override Task<DatabaseResponse> DeleteAsync(
CancellationToken cancellationToken = default(CancellationToken))
{
ThroughputResponse response = await this.ReadThroughputAsync(null, cancellationToken);
return response.Resource?.Throughput;
return response?.Resource?.Throughput;
}

public async override Task<ThroughputResponse> ReadThroughputAsync(
Expand Down
27 changes: 13 additions & 14 deletions Microsoft.Azure.Cosmos/src/Resource/Offer/CosmosOffers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Microsoft.Azure.Cosmos
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
Expand All @@ -29,6 +28,10 @@ internal async Task<ThroughputResponse> ReadThroughputAsync(
CancellationToken cancellationToken = default(CancellationToken))
{
OfferV2 offerV2 = await this.GetOfferV2Async(targetRID, cancellationToken);
if (offerV2 == null)
{
return null;
}

return await this.GetThroughputResponseAsync(
streamPayload: null,
Expand Down Expand Up @@ -73,11 +76,6 @@ internal async Task<OfferV2> GetOfferV2Async(
queryDefinition);
OfferV2 offerV2 = await this.SingleOrDefaultAsync<OfferV2>(databaseStreamIterator);

if (offerV2 == null)
{
throw new CosmosException(HttpStatusCode.NotFound, "Throughput is not configured");
}

return offerV2;
}

Expand All @@ -87,15 +85,15 @@ internal virtual FeedIterator<T> GetOfferQueryIterator<T>(
QueryRequestOptions requestOptions = null,
CancellationToken cancellationToken = default(CancellationToken))
{
FeedIterator databaseStreamIterator = GetOfferQueryStreamIterator(
FeedIterator databaseStreamIterator = this.GetOfferQueryStreamIterator(
queryDefinition,
continuationToken,
requestOptions,
cancellationToken);

return new FeedIteratorCore<T>(
databaseStreamIterator,
this.ClientContext.ResponseFactory.CreateQueryFeedResponse<T>);
this.ClientContext.ResponseFactory.CreateQueryFeedResponseWithPropertySerializer<T>);
}

internal virtual FeedIterator GetOfferQueryStreamIterator(
Expand All @@ -105,12 +103,13 @@ internal virtual FeedIterator GetOfferQueryStreamIterator(
CancellationToken cancellationToken = default(CancellationToken))
{
return new FeedIteratorCore(
this.ClientContext,
this.OfferRootUri,
ResourceType.Offer,
queryDefinition,
continuationToken,
requestOptions);
clientContext: this.ClientContext,
resourceLink: this.OfferRootUri,
resourceType: ResourceType.Offer,
queryDefinition: queryDefinition,
continuationToken: continuationToken,
options: requestOptions,
usePropertySerializer: true);
}

private CosmosOfferResult GetThroughputIfExists(Offer offer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal class FeedIteratorCore : FeedIterator
private readonly Uri resourceLink;
private readonly ResourceType resourceType;
private readonly SqlQuerySpec querySpec;
private readonly bool usePropertySerializer;
private bool hasMoreResultsInternal;

internal FeedIteratorCore(
Expand All @@ -29,14 +30,16 @@ internal FeedIteratorCore(
ResourceType resourceType,
QueryDefinition queryDefinition,
string continuationToken,
QueryRequestOptions options)
QueryRequestOptions options,
bool usePropertySerializer = false)
{
this.resourceLink = resourceLink;
this.clientContext = clientContext;
this.resourceType = resourceType;
this.querySpec = queryDefinition?.ToSqlQuerySpec();
this.continuationToken = continuationToken;
this.requestOptions = options;
this.usePropertySerializer = usePropertySerializer;
this.hasMoreResultsInternal = true;
}

Expand All @@ -63,7 +66,13 @@ internal FeedIteratorCore(
OperationType operation = OperationType.ReadFeed;
if (this.querySpec != null)
{
stream = this.clientContext.SqlQuerySpecSerializer.ToStream(querySpec);
// Use property serializer is for internal query operations like throughput
// that should not use custom serializer
CosmosSerializer serializer = this.usePropertySerializer ?
this.clientContext.PropertiesSerializer :
this.clientContext.SqlQuerySpecSerializer;

stream = serializer.ToStream(this.querySpec);
operation = OperationType.Query;
}

Expand All @@ -77,7 +86,7 @@ internal FeedIteratorCore(
streamPayload: stream,
requestEnricher: request =>
{
QueryRequestOptions.FillContinuationToken(request, continuationToken);
QueryRequestOptions.FillContinuationToken(request, this.continuationToken);
if (this.querySpec != null)
{
request.Headers.Add(HttpConstants.HttpHeaders.ContentType, MediaTypes.QueryJson);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,59 @@ public async Task Cleanup()
}

[TestMethod]
public async Task TestCustomJsonSerializer()
public async Task TestCustomJsonSerializerWithOffers()
{
int toStreamCount = 0;
int fromStreamCount = 0;

CosmosSerializerHelper mockJsonSerializer = new CosmosSerializerHelper(
null,
(x) => fromStreamCount++,
(x) => toStreamCount++);

//Create a new cosmos client with the mocked cosmos json serializer
CosmosClient client = TestCommon.CreateCosmosClient(
(cosmosClientBuilder) => cosmosClientBuilder.WithCustomSerializer(mockJsonSerializer));
Database databaseNoOffer = client.GetDatabase(this.database.Id);
int? dbThroughPut = await databaseNoOffer.ReadThroughputAsync();

Assert.AreEqual(0, toStreamCount);
Assert.AreEqual(0, fromStreamCount);
Assert.IsNull(dbThroughPut);

Container containerWithOffer = databaseNoOffer.GetContainer(this.container.Id);
int? containerThroughPut = await containerWithOffer.ReadThroughputAsync();

Assert.AreEqual(0, toStreamCount);
Assert.AreEqual(0, fromStreamCount);
Assert.IsNotNull(containerThroughPut);
Assert.IsTrue(containerThroughPut > 100);

await containerWithOffer.ReplaceThroughputAsync(containerThroughPut.Value+100);

Assert.AreEqual(0, toStreamCount);
Assert.AreEqual(0, fromStreamCount);

int? updatedContainerThroughPut = await containerWithOffer.ReadThroughputAsync();

Assert.AreEqual(0, toStreamCount);
Assert.AreEqual(0, fromStreamCount);
Assert.IsNotNull(containerThroughPut);
Assert.AreEqual(containerThroughPut+100, updatedContainerThroughPut);
}

[TestMethod]
public async Task TestCustomJsonSerializer()
{
int toStreamCount = 0;
int fromStreamCount = 0;

Mock<CosmosSerializer> mockJsonSerializer = new Mock<CosmosSerializer>();

//The item object will be serialized with the custom json serializer.
ToDoActivity testItem = CreateRandomToDoActivity();
ToDoActivity testItem = this.CreateRandomToDoActivity();
mockJsonSerializer.Setup(x => x.ToStream<ToDoActivity>(It.IsAny<ToDoActivity>()))
.Callback(()=> toStreamCount++)
.Callback(() => toStreamCount++)
.Returns(TestCommon.Serializer.ToStream<ToDoActivity>(testItem));

mockJsonSerializer.Setup(x => x.FromStream<ToDoActivity>(It.IsAny<Stream>()))
Expand Down Expand Up @@ -83,15 +125,15 @@ public async Task DefaultNullValueHandling()
cost = double.MaxValue
};

await container.UpsertItemAsync(document);
await this.container.UpsertItemAsync(document);

ResponseMessage cosmosResponseMessage = await container.ReadItemStreamAsync(document.id, new PartitionKey(document.status));
ResponseMessage cosmosResponseMessage = await this.container.ReadItemStreamAsync(document.id, new PartitionKey(document.status));
StreamReader reader = new StreamReader(cosmosResponseMessage.Content);
string text = reader.ReadToEnd();

Assert.IsTrue(text.IndexOf(nameof(document.description)) > -1, "Stored item doesn't contains null attributes");
}

private ToDoActivity CreateRandomToDoActivity(string pk = null)
{
if (string.IsNullOrEmpty(pk))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,32 @@

namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
{
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.IO;
using Newtonsoft.Json;

/// <summary>
/// Placeholder for VST Logger.
/// </summary>
internal class CosmosSerializerHelper : CosmosSerializer
{
private CosmosSerializer cosmosSerializer = TestCommon.Serializer;
private Action<dynamic> fromStreamCallback;
private Action<dynamic> toStreamCallBack;
private readonly CosmosSerializer cosmosSerializer = TestCommon.Serializer;
private readonly Action<dynamic> fromStreamCallback;
private readonly Action<dynamic> toStreamCallBack;

public CosmosSerializerHelper(
JsonSerializerSettings jsonSerializerSettings,
Action<dynamic> fromStreamCallback,
Action<dynamic> toStreamCallBack)
{
if(jsonSerializerSettings == null)
if (jsonSerializerSettings == null)
{
cosmosSerializer = TestCommon.Serializer;
this.cosmosSerializer = TestCommon.Serializer;
}
else
{
cosmosSerializer = new CosmosJsonDotNetSerializer(jsonSerializerSettings);
this.cosmosSerializer = new CosmosJsonDotNetSerializer(jsonSerializerSettings);
}

this.fromStreamCallback = fromStreamCallback;
Expand All @@ -54,14 +54,17 @@ public sealed class FormatNumbersAsTextConverter : JsonConverter
{
public override bool CanRead => false;
public override bool CanWrite => true;
public override bool CanConvert(Type type) => type == typeof(int) || type == typeof(double);
public override bool CanConvert(Type type)
{
return type == typeof(int) || type == typeof(double);
}

public override void WriteJson(
JsonWriter writer,
object value,
JsonWriter writer,
object value,
JsonSerializer serializer)
{
if(value.GetType() == typeof(int))
if (value.GetType() == typeof(int))
{
int number = (int)value;
writer.WriteValue(number.ToString(CultureInfo.InvariantCulture));
Expand All @@ -71,13 +74,13 @@ public override void WriteJson(
double number = (double)value;
writer.WriteValue(number.ToString(CultureInfo.InvariantCulture));
}

}

public override object ReadJson(
JsonReader reader,
Type type,
object existingValue,
JsonReader reader,
Type type,
object existingValue,
JsonSerializer serializer)
{
throw new NotSupportedException();
Expand Down