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

[vNext] System.Text.Json as default serialization mechanism #1119

Merged
merged 72 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
3c823a0
Adding initial converters
ealsur Dec 9, 2019
4382ae4
initial set of converters
ealsur Dec 11, 2019
46553df
Adding more converters
ealsur Dec 13, 2019
0247be8
Removing deprecated options
ealsur Dec 18, 2019
532a3f0
Container and IndexPolicy converters
ealsur Dec 18, 2019
8632496
DatabaseProperties
ealsur Dec 18, 2019
3c06fd1
merge with master
ealsur Dec 18, 2019
9502d9b
PermissionProperties
ealsur Dec 19, 2019
a278d76
SprocProperties
ealsur Dec 19, 2019
ac96314
TriggerProperties
ealsur Dec 19, 2019
6929e87
UDFsProperties
ealsur Dec 19, 2019
133b3c6
ThroughputProperties
ealsur Dec 19, 2019
5b21ecb
UserProperties
ealsur Dec 19, 2019
4c274b7
Code reuse
ealsur Dec 19, 2019
7148898
Initial async changes for serializer
ealsur Dec 20, 2019
416e37d
Serializer sync
ealsur Dec 20, 2019
4c52193
Revert "Initial async changes for serializer"
ealsur Dec 20, 2019
7347203
Completing System.Text.Json migration
ealsur Dec 20, 2019
e83cb08
Obsolte
ealsur Dec 20, 2019
d5706cc
tests
ealsur Dec 20, 2019
b0adb4c
Error serialization
ealsur Dec 20, 2019
a1a6b4f
utility for stream
ealsur Dec 20, 2019
013a8ae
CosmosClientOptions converter
ealsur Dec 20, 2019
1b9324f
Cleanup
ealsur Dec 20, 2019
a5c290a
Fixing tests
ealsur Dec 20, 2019
0589763
Helper from BCL
ealsur Dec 23, 2019
7f413c8
Removing unused
ealsur Dec 23, 2019
fca080d
rented size
ealsur Dec 23, 2019
776d900
Fixing enums
ealsur Dec 23, 2019
c43fcd2
Enum parsing
ealsur Dec 23, 2019
9f7df17
System.Text.Json 4.7.0
ealsur Dec 23, 2019
4472ee2
Fixing tests
ealsur Dec 23, 2019
f72dc4e
OfferV2 converter
ealsur Dec 23, 2019
2d155ee
Utf8Writer overrides
ealsur Dec 26, 2019
156c846
Refactor to reuse in ResponseFactory
ealsur Dec 26, 2019
770b566
Offers
ealsur Dec 26, 2019
9239718
SqlQuerySpec support
ealsur Dec 26, 2019
65e8589
Converter fixes
ealsur Dec 26, 2019
b2c3317
Tests
ealsur Dec 26, 2019
9396721
QueryConfiguration serialization
ealsur Dec 26, 2019
d6b6ee3
Double converter
ealsur Dec 26, 2019
36380dd
Indenting
ealsur Dec 26, 2019
e22b715
Fixing tests
ealsur Dec 26, 2019
1f32f2c
WriteRaw
ealsur Dec 26, 2019
27d5976
Cleanup
ealsur Dec 26, 2019
67d565a
Removing Newtonsoft.Json dependency from estimator
ealsur Dec 27, 2019
13afa05
Fixing UTs
ealsur Dec 27, 2019
4044dd3
CF dependency
ealsur Dec 27, 2019
3e4b248
Lease serialziation
ealsur Dec 27, 2019
6226ae7
Fixing more tests
ealsur Dec 27, 2019
c78039c
LSN parsing
ealsur Dec 27, 2019
a0750db
Workaround for internal serialization types
ealsur Dec 27, 2019
73d9cec
Test fixes
ealsur Dec 27, 2019
36f202d
UTs
ealsur Dec 28, 2019
0dac165
Type serialization in test
ealsur Dec 28, 2019
2b0d61d
JToken System.Text.Json support
ealsur Dec 30, 2019
feb4465
AreEqual with JsonElement
ealsur Dec 30, 2019
abdf835
quarantine
ealsur Dec 30, 2019
85d3e30
Converters
ealsur Dec 30, 2019
1939490
Order of overload
ealsur Dec 30, 2019
7de7fc2
Wrong enum handling
ealsur Dec 30, 2019
b51f5b3
Enum fix
ealsur Dec 31, 2019
21bb376
Spatial converters
ealsur Dec 31, 2019
82551ea
Fixing spatial converters
ealsur Dec 31, 2019
14e505d
Updating contract
ealsur Dec 31, 2019
bac0064
Cached JsonEncodedStrings for performance
ealsur Dec 31, 2019
1e6a86c
Enum parsing helper
ealsur Dec 31, 2019
82c081c
changelog
ealsur Jan 2, 2020
bd3c1ed
Adding TextJsonUnixDateTimeConverter tests
ealsur Jan 2, 2020
cc7c665
TryGetBuffer
ealsur Jan 6, 2020
5ee6727
TryGetBuffer with BOM
ealsur Jan 6, 2020
17f8ce7
Adding TTL test with culture
ealsur Jan 6, 2020
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
18 changes: 12 additions & 6 deletions Microsoft.Azure.Cosmos/azuredata/Azure.Cosmos.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,24 @@
<Compile Include="..\src\SqlObjects\**\*.cs">
<Link>SqlObjects\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Fluent\Settings\*.cs" Exclude="..\src\Fluent\Settings\ContainerBuilder.cs">
<Compile Include="..\src\Fluent\Settings\*.cs" Exclude="..\src\Fluent\Settings\ContainerBuilder.cs;..\src\Fluent\Settings\ContainerDefinition.cs">
<Link>Fluent\Settings\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Json\**\*.cs">
<Link>Json\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Spatial\**\*.cs">
<Link>Spatial\%(RecursiveDir)\%(Filename)%(Extension)</Link>
<Compile Include="..\src\Serializer\DefaultJsonSerializationSettings.cs">
<Link>Serializer\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Serializer\CosmosJsonSerializerWrapper.cs">
<Link>Serializer\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Serializer\CosmosSerializationOptions.cs">
<Link>Serializer\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Serializer\CosmosSerializer.cs">
<Link>Serializer\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Serializer\CosmosSerializationFormatOptions.cs">
<Link>Serializer\%(Filename)%(Extension)</Link>
</Compile>
Expand All @@ -105,7 +111,7 @@
<Compile Include="..\src\CosmosElements\**\*.cs">
<Link>CosmosElements\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\ChangeFeedProcessor\**\*.cs" Exclude="..\src\ChangeFeedProcessor\FeedManagement\RemainingWorkEstimatorCore.cs;..\src\ChangeFeedProcessor\FeedProcessing\ChangeFeedObserverContextCore.cs;..\src\ChangeFeedProcessor\FeedProcessing\FeedProcessorCore.cs;..\src\ChangeFeedProcessor\Utils\CosmosContainerExtensions.cs;..\src\ChangeFeedProcessor\LeaseManagement\DocumentServiceLeaseContainerCosmos.cs;..\src\ChangeFeedProcessor\LeaseManagement\DocumentServiceLeaseUpdaterCosmos.cs;..\src\ChangeFeedProcessor\LeaseManagement\DocumentServiceLeaseStoreCosmos.cs">
<Compile Include="..\src\ChangeFeedProcessor\**\*.cs" Exclude="..\src\ChangeFeedProcessor\FeedManagement\RemainingWorkEstimatorCore.cs;..\src\ChangeFeedProcessor\FeedProcessing\ChangeFeedObserverContextCore.cs;..\src\ChangeFeedProcessor\FeedProcessing\FeedProcessorCore.cs;..\src\ChangeFeedProcessor\Utils\CosmosContainerExtensions.cs;..\src\ChangeFeedProcessor\LeaseManagement\DocumentServiceLeaseContainerCosmos.cs;..\src\ChangeFeedProcessor\LeaseManagement\DocumentServiceLeaseUpdaterCosmos.cs;..\src\ChangeFeedProcessor\LeaseManagement\DocumentServiceLeaseStoreCosmos.cs;..\src\ChangeFeedProcessor\LeaseManagement\DocumentServiceLeaseCore.cs">
<Link>ChangeFeedProcessor\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\src\Headers\*.cs" Exclude="..\src\Headers\Headers.cs;..\src\Headers\CosmosMessageHeadersInternal.cs">
Expand Down Expand Up @@ -226,7 +232,7 @@

<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="System.Text.Json" Version="4.7.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="16.4.16" PrivateAssets="All" />

Expand All @@ -236,7 +242,7 @@

<!--HybridRow & Azure.Core Dependencies-->
<PackageReference Include="System.Memory" Version="4.5.3" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.2" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ namespace Azure.Cosmos.ChangeFeed
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Azure.Cosmos.Serialization;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Core.Trace;
using Microsoft.Azure.Documents;
using Newtonsoft.Json.Linq;

internal sealed class RemainingWorkEstimatorCore : RemainingWorkEstimator
{
private const char PKRangeIdSeparator = ':';
private const char SegmentSeparator = '#';
private const string LSNPropertyName = "_lsn";
private static readonly CosmosSerializer DefaultSerializer = new CosmosJsonDotNetSerializer();
private static readonly JsonEncodedText LSNPropertyName = JsonEncodedText.Encode("_lsn");
private static readonly CosmosSerializer DefaultSerializer = new CosmosTextJsonSerializer();
private readonly Func<string, string, bool, FeedIterator> feedCreator;
private readonly DocumentServiceLeaseContainer leaseContainer;
private readonly int degreeOfParallelism;
Expand Down Expand Up @@ -125,26 +125,26 @@ internal static string ExtractLsnFromSessionToken(string sessionToken)
return segments[1];
}

private static string GetFirstItemLSN(Collection<JObject> items)
private static long GetFirstItemLSN(Collection<JsonElement> items)
{
JObject item = RemainingWorkEstimatorCore.GetFirstItem(items);
if (item == null)
JsonElement? item = RemainingWorkEstimatorCore.GetFirstItem(items);
if (!item.HasValue)
{
return null;
return 0;
}

if (item.TryGetValue(LSNPropertyName, StringComparison.OrdinalIgnoreCase, out JToken property))
if (item.Value.TryGetProperty(LSNPropertyName.EncodedUtf8Bytes, out JsonElement property))
{
return property.Value<string>();
return property.GetInt64();
}

DefaultTrace.TraceWarning("Change Feed response item does not include LSN.");
return null;
return 0;
}

private static JObject GetFirstItem(Collection<JObject> response)
private static JsonElement? GetFirstItem(Collection<JsonElement> response)
{
using (IEnumerator<JObject> e = response.GetEnumerator())
using (IEnumerator<JsonElement> e = response.GetEnumerator())
{
while (e.MoveNext())
{
Expand All @@ -167,14 +167,14 @@ private static long TryConvertToNumber(string number)
return parsed;
}

private static Collection<JObject> GetItemsFromResponse(Response response)
private static Collection<JsonElement> GetItemsFromResponse(Response response)
{
if (response.ContentStream == null)
{
return new Collection<JObject>();
return new Collection<JsonElement>();
}

return RemainingWorkEstimatorCore.DefaultSerializer.FromStream<CosmosFeedResponseUtil<JObject>>(response.ContentStream).Data;
return RemainingWorkEstimatorCore.DefaultSerializer.FromStream<CosmosFeedResponseUtil<JsonElement>>(response.ContentStream).Data;
}

private async Task<long> GetRemainingWorkAsync(DocumentServiceLease existingLease, CancellationToken cancellationToken)
Expand All @@ -196,9 +196,9 @@ private async Task<long> GetRemainingWorkAsync(DocumentServiceLease existingLeas

response.Headers.TryGetValue(HttpConstants.HttpHeaders.SessionToken, out string sessionToken);
long parsedLSNFromSessionToken = RemainingWorkEstimatorCore.TryConvertToNumber(ExtractLsnFromSessionToken(sessionToken));
Collection<JObject> items = RemainingWorkEstimatorCore.GetItemsFromResponse(response);
Collection<JsonElement> items = RemainingWorkEstimatorCore.GetItemsFromResponse(response);
long lastQueryLSN = items.Count > 0
? RemainingWorkEstimatorCore.TryConvertToNumber(RemainingWorkEstimatorCore.GetFirstItemLSN(items)) - 1
? RemainingWorkEstimatorCore.GetFirstItemLSN(items) - 1
: parsedLSNFromSessionToken;
if (lastQueryLSN < 0)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
ealsur marked this conversation as resolved.
Show resolved Hide resolved
//------------------------------------------------------------
namespace Azure.Cosmos.ChangeFeed
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json.Serialization;

[Serializable]
internal sealed class DocumentServiceLeaseCore : DocumentServiceLease
{
private static readonly DateTime UnixStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);

public DocumentServiceLeaseCore()
{
}

public DocumentServiceLeaseCore(DocumentServiceLeaseCore other)
{
this.LeaseId = other.LeaseId;
this.LeaseToken = other.LeaseToken;
this.Owner = other.Owner;
this.ContinuationToken = other.ContinuationToken;
this.ETag = other.ETag;
this.TS = other.TS;
this.ExplicitTimestamp = other.ExplicitTimestamp;
this.Properties = other.Properties;
}

[JsonPropertyName("id")]
public string LeaseId { get; set; }

[JsonIgnore]
public override string Id => this.LeaseId;

[JsonPropertyName("_etag")]
public string ETag { get; set; }

[JsonPropertyName("LeaseToken")]
public string LeaseToken { get; set; }

[JsonPropertyName("PartitionId")]
private string PartitionId
{
set
{
this.LeaseToken = value;
}
}

[JsonIgnore]
public override string CurrentLeaseToken => this.LeaseToken;

[JsonPropertyName("Owner")]
public override string Owner { get; set; }

/// <summary>
/// Gets or sets the current value for the offset in the stream.
/// </summary>
[JsonPropertyName("ContinuationToken")]
public override string ContinuationToken { get; set; }

[JsonIgnore]
public override DateTime Timestamp
{
get { return this.ExplicitTimestamp ?? UnixStartTime.AddSeconds(this.TS); }
set { this.ExplicitTimestamp = value; }
}

[JsonIgnore]
public override string ConcurrencyToken => this.ETag;

[JsonPropertyName("properties")]
public override Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();

[JsonPropertyName("timestamp")]
private DateTime? ExplicitTimestamp { get; set; }

[JsonPropertyName("_ts")]
private long TS { get; set; }

public override string ToString()
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} Owner='{1}' Continuation={2} Timestamp(local)={3} Timestamp(server)={4}",
this.Id,
this.Owner,
this.ContinuationToken,
this.Timestamp.ToUniversalTime(),
UnixStartTime.AddSeconds(this.TS).ToUniversalTime());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Azure.Cosmos.ChangeFeed
{
using System;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Newtonsoft.Json;

/// <summary>
/// Implementation of <see cref="DocumentServiceLeaseStore"/> for state in Azure Cosmos DB
Expand Down Expand Up @@ -96,10 +96,10 @@ private string GetStoreLockName()

private class LockDocument
{
[JsonProperty("id")]
[JsonPropertyName("id")]
public string Id { get; set; }

[JsonProperty("ttl")]
[JsonPropertyName("ttl")]
public int TimeToLive { get; set; }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Azure.Cosmos.ChangeFeed

internal static class CosmosContainerExtensions
{
public static readonly CosmosSerializer DefaultJsonSerializer = new CosmosJsonDotNetSerializer();
public static readonly CosmosSerializer DefaultJsonSerializer = new CosmosTextJsonSerializer();

public static async Task<T> TryGetItemAsync<T>(
this CosmosContainer container,
Expand Down
2 changes: 1 addition & 1 deletion Microsoft.Azure.Cosmos/azuredata/CosmosClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ internal void Init(
defaultJsonSerializer: this.ClientOptions.PropertiesSerializer,
userJsonSerializer: userSerializer);

CosmosSerializer sqlQuerySpecSerializer = CosmosSqlQuerySpecJsonConverter.CreateSqlQuerySpecSerializer(
CosmosSerializer sqlQuerySpecSerializer = TextJsonCosmosSqlQuerySpecConverter.CreateSqlQuerySpecSerializer(
cosmosSerializer: userSerializer,
propertiesSerializer: this.ClientOptions.PropertiesSerializer);

Expand Down
53 changes: 6 additions & 47 deletions Microsoft.Azure.Cosmos/azuredata/CosmosClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ namespace Azure.Cosmos
using System.Data.Common;
using System.Linq;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
using Azure.Core;
using Azure.Cosmos.Fluent;
using Azure.Cosmos.Serialization;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Newtonsoft.Json;

/// <summary>
/// Defines all the configurable options that the CosmosClient requires.
Expand All @@ -32,6 +33,7 @@ namespace Azure.Cosmos
///
/// CosmosClient client = new CosmosClient("endpoint", "key", clientOptions);
/// </example>
[JsonConverter(typeof(TextJsonCosmosClientOptionsConverter))]
public class CosmosClientOptions : ClientOptions
{
/// <summary>
Expand All @@ -52,7 +54,7 @@ public class CosmosClientOptions : ClientOptions
/// <summary>
/// Default request timeout
/// </summary>
private static readonly CosmosSerializer propertiesSerializer = new CosmosJsonSerializerWrapper(new CosmosJsonDotNetSerializer());
private static readonly CosmosSerializer propertiesSerializer = new CosmosJsonSerializerWrapper(new CosmosTextJsonSerializer());

private int gatewayModeMaxConnectionLimit;
private CosmosSerializationOptions serializerOptions;
Expand Down Expand Up @@ -272,7 +274,6 @@ public int? MaxTcpConnectionsPerEndpoint
/// <summary>
/// (Gateway/Https) Get or set the proxy information used for web requests.
/// </summary>
[JsonIgnore]
public IWebProxy WebProxy
{
get => this.webProxy;
Expand Down Expand Up @@ -330,7 +331,6 @@ public CosmosSerializationOptions SerializerOptions
///
/// CosmosClient client = new CosmosClient("endpoint", "key", clientOptions);
/// </example>
[JsonConverter(typeof(ClientOptionJsonConverter))]
public CosmosSerializer Serializer
{
get => this.serializer;
Expand Down Expand Up @@ -375,7 +375,6 @@ public CosmosSerializer Serializer
/// The default serializer is always used for all system owned types like DatabaseProperties.
/// The default serializer is used for user types if no UserJsonSerializer is specified
/// </summary>
[JsonConverter(typeof(ClientOptionJsonConverter))]
internal CosmosSerializer PropertiesSerializer => CosmosClientOptions.propertiesSerializer;

internal Collection<RequestHandler> CustomHandlers { get; }
Expand Down Expand Up @@ -493,7 +492,7 @@ internal CosmosSerializer GetCosmosSerializerWithWrapperOrDefault()
{
if (this.SerializerOptions != null)
{
CosmosJsonDotNetSerializer cosmosJsonDotNetSerializer = new CosmosJsonDotNetSerializer(this.SerializerOptions);
CosmosTextJsonSerializer cosmosJsonDotNetSerializer = new CosmosTextJsonSerializer(this.SerializerOptions);
return new CosmosJsonSerializerWrapper(cosmosJsonDotNetSerializer);
}
else
Expand Down Expand Up @@ -666,47 +665,7 @@ private void ValidateDirectTCPSettings()
/// <returns>Returns a JSON string of the current configuration.</returns>
internal string GetSerializedConfiguration()
{
return JsonConvert.SerializeObject(this);
}

/// <summary>
/// The complex object passed in by the user can contain objects that can not be serialized. Instead just log the types.
/// </summary>
private class ClientOptionJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Collection<RequestHandler> handlers = value as Collection<RequestHandler>;
if (handlers != null)
{
writer.WriteValue(string.Join(":", handlers.Select(x => x.GetType())));
return;
}

CosmosJsonSerializerWrapper cosmosJsonSerializerWrapper = value as CosmosJsonSerializerWrapper;
if (value is CosmosJsonSerializerWrapper)
{
writer.WriteValue(cosmosJsonSerializerWrapper.InternalJsonSerializer.GetType().ToString());
}

CosmosSerializer cosmosSerializer = value as CosmosSerializer;
if (cosmosSerializer is CosmosSerializer)
{
writer.WriteValue(cosmosSerializer.GetType().ToString());
}
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}

public override bool CanRead => false;

public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
return JsonSerializer.Serialize(this);
}
}
}
Loading