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

CosmosOperationCanceledException: Adds serializable functionality #3433

Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections;
using System.Diagnostics;
using System.Drawing.Printing;
NaluTripician marked this conversation as resolved.
Show resolved Hide resolved
using System.Runtime.Serialization;
using System.Threading;
using global::Azure.Core.Pipeline;
using Microsoft.Azure.Cosmos.ChangeFeed.LeaseManagement;
using Microsoft.Azure.Cosmos.Diagnostics;
using Microsoft.Azure.Cosmos.Telemetry;
using Microsoft.Azure.Cosmos.Telemetry.Diagnostics;
using Microsoft.Azure.Cosmos.Tracing;
using Newtonsoft.Json;

/// <summary>
/// The exception that is thrown in a thread upon cancellation of an operation that
/// the thread was executing. This extends the OperationCanceledException to include the
/// diagnostics of the operation that was canceled.
/// </summary>
/// </summary>
[Serializable]
public class CosmosOperationCanceledException : OperationCanceledException
{
private readonly OperationCanceledException originalException;
Expand Down Expand Up @@ -63,6 +69,22 @@ internal CosmosOperationCanceledException(
this.lazyMessage = this.CreateLazyMessage();
}

/// <summary>
/// Initializes a new instance of the <see cref="CosmosOperationCanceledException"/> class.
/// </summary>
/// <param name="info">The SerializationInfo object that holds serialized object data for the exception being thrown.</param>
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
protected CosmosOperationCanceledException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
this.originalException = (OperationCanceledException)info.GetValue("originalException", typeof(OperationCanceledException));
this.tokenCancellationRequested = (bool)info.GetValue("tokenCancellationRequested", typeof(bool));

this.toStringMessage = this.CreateToStringMessagDeserialized();
this.lazyMessage = this.CreateLazyMessageDeserialized();
this.Diagnostics = new CosmosTraceDiagnostics(NoOpTrace.Singleton);
}

/// <inheritdoc/>
public override string Source
{
Expand Down Expand Up @@ -112,6 +134,15 @@ private Lazy<string> CreateToStringMessage()
return new Lazy<string>(() => $"{this.originalException}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}");
}

private Lazy<string> CreateLazyMessageDeserialized()
{
return new Lazy<string>(() => $"{this.originalException.Message}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Note that CosmosDiagnostics cannot be serialized. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}");
}
private Lazy<string> CreateToStringMessagDeserialized()
{
return new Lazy<string>(() => $"{this.originalException}{Environment.NewLine}Cancellation Token has expired: {this.tokenCancellationRequested}. Note that CosmosDiagnostics cannot be serialized. Learn more at: https://aka.ms/cosmosdb-tsg-request-timeout{Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}");
NaluTripician marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
/// RecordOtelAttributes
/// </summary>
Expand All @@ -123,5 +154,17 @@ internal static void RecordOtelAttributes(CosmosOperationCanceledException excep
scope.AddAttribute(OpenTelemetryAttributeKeys.RequestDiagnostics, exception.Diagnostics);
scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionMessage, exception.GetBaseException().Message);
}

/// <summary>
/// Sets the System.Runtime.Serialization.SerializationInfo with information about the exception.
/// </summary>
/// <param name="info">The SerializationInfo object that holds serialized object data for the exception being thrown.</param>
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("originalException", this.originalException);
info.AddValue("tokenCancellationRequested", this.tokenCancellationRequested);
NaluTripician marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.Azure.Cosmos.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Diagnostics;
using Microsoft.Azure.Cosmos.Tracing; //what is this? for the no op trace
using Microsoft.IdentityModel.Tokens;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;

[TestClass]
public class CosmosOperationCanceledTests
{

[TestMethod]
public void SerializationValidation()
{
//create test exception
CosmosOperationCanceledException originalException = new CosmosOperationCanceledException(
new OperationCanceledException("error message"),
new CosmosTraceDiagnostics(NoOpTrace.Singleton));

//serialize exception
string serialized = JsonConvert.SerializeObject(originalException);

CosmosOperationCanceledException deserializedExceptoin =
JsonConvert.DeserializeObject<CosmosOperationCanceledException>(serialized);

//Asserts
Assert.AreEqual(originalException.GetBaseException().Message, deserializedExceptoin.GetBaseException().Message);
Assert.AreEqual(originalException.GetBaseException().ToString(), deserializedExceptoin.GetBaseException().ToString());
Assert.AreEqual(originalException.GetBaseException().HResult, deserializedExceptoin.GetBaseException().HResult);
}
}
}