From d0bee5dd05cf98bf2007824f87c4e605298ae596 Mon Sep 17 00:00:00 2001 From: Matias Quaranta Date: Wed, 16 Dec 2020 22:00:34 -0300 Subject: [PATCH] [Internal] Release: Adds 3.15.1 release branch (#2074) Cherry-picking commits for the release #2069 #2047 #2071 #2042 Version bump from #2072 --- Directory.Build.props | 2 +- .../contracts/API_3.15.1.txt | 1219 +++++++++++++++++ .../src/Batch/BatchAsyncContainerExecutor.cs | 18 +- .../src/Batch/ItemBatchOperationContext.cs | 7 +- .../BulkPartitionKeyRangeGoneRetryPolicy.cs | 81 +- ...angeFeedStartFromRequestOptionPopulator.cs | 2 +- .../CosmosDiagnosticsSerializerVisitor.cs | 3 + .../TransientHttpClientRetryPolicy.cs | 3 +- Microsoft.Azure.Cosmos/src/Util/Extensions.cs | 2 +- .../ChangeFeed/DynamicTests.cs | 54 + .../Utils/DiagnosticValidators.cs | 1 + .../Batch/BatchAsyncBatcherTests.cs | 178 +++ .../Batch/BatchAsyncContainerExecutorTests.cs | 28 + .../Batch/BatchAsyncOperationContextTests.cs | 73 +- ...lkPartitionKeyRangeGoneRetryPolicyTests.cs | 70 +- .../CosmosDiagnosticsUnitTests.cs | 43 +- changelog.md | 8 + 17 files changed, 1734 insertions(+), 58 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/contracts/API_3.15.1.txt diff --git a/Directory.Build.props b/Directory.Build.props index fd1f70ab7e..c9e57b0b16 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 3.15.0 + 3.15.1 3.15.2 3.15.2 1.0.0-preview6 diff --git a/Microsoft.Azure.Cosmos/contracts/API_3.15.1.txt b/Microsoft.Azure.Cosmos/contracts/API_3.15.1.txt new file mode 100644 index 0000000000..207af006b6 --- /dev/null +++ b/Microsoft.Azure.Cosmos/contracts/API_3.15.1.txt @@ -0,0 +1,1219 @@ +namespace Microsoft.Azure.Cosmos +{ + public class AccountConsistency + { + public AccountConsistency(); + public ConsistencyLevel DefaultConsistencyLevel { get; } + public int MaxStalenessIntervalInSeconds { get; } + public int MaxStalenessPrefix { get; } + } + public class AccountProperties + { + public AccountConsistency Consistency { get; } + public string ETag { get; } + public string Id { get; } + public IEnumerable ReadableRegions { get; } + public IEnumerable WritableRegions { get; } + } + public class AccountRegion + { + public AccountRegion(); + public string Endpoint { get; } + public string Name { get; } + } + public sealed class BoundingBoxProperties + { + public BoundingBoxProperties(); + public double Xmax { get; set; } + public double Xmin { get; set; } + public double Ymax { get; set; } + public double Ymin { get; set; } + } + public abstract class ChangeFeedProcessor + { + protected ChangeFeedProcessor(); + public abstract Task StartAsync(); + public abstract Task StopAsync(); + } + public class ChangeFeedProcessorBuilder + { + public ChangeFeedProcessor Build(); + public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); + public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable acquireInterval=default(Nullable), Nullable expirationInterval=default(Nullable), Nullable renewInterval=default(Nullable)); + public ChangeFeedProcessorBuilder WithLeaseContainer(Container leaseContainer); + public ChangeFeedProcessorBuilder WithMaxItems(int maxItemCount); + public ChangeFeedProcessorBuilder WithPollInterval(TimeSpan pollInterval); + public ChangeFeedProcessorBuilder WithStartTime(DateTime startTime); + } + public sealed class CompositePath + { + public CompositePath(); + public CompositePathSortOrder Order { get; set; } + public string Path { get; set; } + } + public enum CompositePathSortOrder + { + Ascending = 0, + Descending = 1, + } + public class ConflictProperties + { + public ConflictProperties(); + public string Id { get; } + public OperationKind OperationKind { get; } + public string SelfLink { get; } + } + public enum ConflictResolutionMode + { + Custom = 1, + LastWriterWins = 0, + } + public class ConflictResolutionPolicy + { + public ConflictResolutionPolicy(); + public ConflictResolutionMode Mode { get; set; } + public string ResolutionPath { get; set; } + public string ResolutionProcedure { get; set; } + } + public abstract class Conflicts + { + protected Conflicts(); + public abstract Task DeleteAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetConflictQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetConflictQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract T ReadConflictContent(ConflictProperties conflict); + public abstract Task> ReadCurrentAsync(ConflictProperties conflict, PartitionKey partitionKey, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum ConnectionMode + { + Direct = 1, + Gateway = 0, + } + public enum ConsistencyLevel + { + BoundedStaleness = 1, + ConsistentPrefix = 4, + Eventual = 3, + Session = 2, + Strong = 0, + } + public abstract class Container + { + protected Container(); + public abstract Conflicts Conflicts { get; } + public abstract Database Database { get; } + public abstract string Id { get; } + public abstract Scripts Scripts { get; } + public abstract Task> CreateItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch CreateTransactionalBatch(PartitionKey partitionKey); + public abstract Task DeleteContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> DeleteItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ChangeFeedProcessorBuilder GetChangeFeedEstimatorBuilder(string processorName, Container.ChangesEstimationHandler estimationDelegate, Nullable estimationPeriod=default(Nullable)); + public abstract ChangeFeedProcessorBuilder GetChangeFeedProcessorBuilder(string processorName, Container.ChangesHandler onChangesDelegate); + public abstract IOrderedQueryable GetItemLinqQueryable(bool allowSynchronousQueryExecution=false, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetItemQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadContainerAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadContainerStreamAsync(ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadItemAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadItemStreamAsync(string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceContainerStreamAsync(ContainerProperties containerProperties, ContainerRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReplaceItemAsync(T item, string id, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> UpsertItemAsync(T item, Nullable partitionKey=default(Nullable), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public delegate Task ChangesEstimationHandler(long estimatedPendingChanges, CancellationToken cancellationToken); + public delegate Task ChangesHandler(IReadOnlyCollection changes, CancellationToken cancellationToken); + } + public class ContainerProperties + { + public ContainerProperties(); + public ContainerProperties(string id, string partitionKeyPath); + public Nullable AnalyticalStoreTimeToLiveInSeconds { get; set; } + public ConflictResolutionPolicy ConflictResolutionPolicy { get; set; } + public Nullable DefaultTimeToLive { get; set; } + public string ETag { get; } + public GeospatialConfig GeospatialConfig { get; set; } + public string Id { get; set; } + public IndexingPolicy IndexingPolicy { get; set; } + public Nullable LastModified { get; } + public Nullable PartitionKeyDefinitionVersion { get; set; } + public string PartitionKeyPath { get; set; } + public string SelfLink { get; } + public string TimeToLivePropertyPath { get; set; } + public UniqueKeyPolicy UniqueKeyPolicy { get; set; } + } + public class ContainerRequestOptions : RequestOptions + { + public ContainerRequestOptions(); + public bool PopulateQuotaInfo { get; set; } + } + public class ContainerResponse : Response + { + protected ContainerResponse(); + public override string ActivityId { get; } + public virtual Container Container { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override ContainerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Container (ContainerResponse response); + } + public class CosmosClient : IDisposable + { + protected CosmosClient(); + public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null); + public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null); + public virtual CosmosClientOptions ClientOptions { get; } + public virtual Uri Endpoint { get; } + public virtual Task CreateDatabaseAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseIfNotExistsAsync(string id, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual Container GetContainer(string databaseId, string containerId); + public virtual Database GetDatabase(string id); + public virtual FeedIterator GetDatabaseQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual FeedIterator GetDatabaseQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public virtual Task ReadAccountAsync(); + } + public class CosmosClientOptions + { + public CosmosClientOptions(); + public bool AllowBulkExecution { get; set; } + public string ApplicationName { get; set; } + public IReadOnlyList ApplicationPreferredRegions { get; set; } + public string ApplicationRegion { get; set; } + public ConnectionMode ConnectionMode { get; set; } + public Nullable ConsistencyLevel { get; set; } + public Collection CustomHandlers { get; } + public bool EnableTcpConnectionEndpointRediscovery { get; set; } + public int GatewayModeMaxConnectionLimit { get; set; } + public Func HttpClientFactory { get; set; } + public Nullable IdleTcpConnectionTimeout { get; set; } + public bool LimitToEndpoint { get; set; } + public Nullable MaxRequestsPerTcpConnection { get; set; } + public Nullable MaxRetryAttemptsOnRateLimitedRequests { get; set; } + public Nullable MaxRetryWaitTimeOnRateLimitedRequests { get; set; } + public Nullable MaxTcpConnectionsPerEndpoint { get; set; } + public Nullable OpenTcpConnectionTimeout { get; set; } + public Nullable PortReuseMode { get; set; } + public TimeSpan RequestTimeout { get; set; } + public CosmosSerializer Serializer { get; set; } + public CosmosSerializationOptions SerializerOptions { get; set; } + public IWebProxy WebProxy { get; set; } + } + public abstract class CosmosDiagnostics + { + protected CosmosDiagnostics(); + public virtual TimeSpan GetClientElapsedTime(); + public abstract override string ToString(); + } + public class CosmosException : Exception + { + public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge); + public virtual string ActivityId { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual Headers Headers { get; } + public virtual double RequestCharge { get; } + public virtual string ResponseBody { get; } + public virtual Nullable RetryAfter { get; } + public override string StackTrace { get; } + public virtual HttpStatusCode StatusCode { get; } + public virtual int SubStatusCode { get; } + public override string ToString(); + public virtual bool TryGetHeader(string headerName, out string value); + } + public class CosmosOperationCanceledException : OperationCanceledException + { + public CosmosOperationCanceledException(OperationCanceledException originalException, CosmosDiagnostics diagnostics); + public override IDictionary Data { get; } + public CosmosDiagnostics Diagnostics { get; } + public override string HelpLink { get; set; } + public override string Message { get; } + public override string Source { get; set; } + public override string StackTrace { get; } + public override Exception GetBaseException(); + public override string ToString(); + } + public enum CosmosPropertyNamingPolicy + { + CamelCase = 1, + Default = 0, + } + public sealed class CosmosSerializationOptions + { + public CosmosSerializationOptions(); + public bool IgnoreNullValues { get; set; } + public bool Indented { get; set; } + public CosmosPropertyNamingPolicy PropertyNamingPolicy { get; set; } + } + public abstract class CosmosSerializer + { + protected CosmosSerializer(); + public abstract T FromStream(Stream stream); + public abstract Stream ToStream(T input); + } + public abstract class Database + { + protected Database(); + public abstract CosmosClient Client { get; } + public abstract string Id { get; } + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerIfNotExistsAsync(string id, string partitionKeyPath, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateContainerStreamAsync(ContainerProperties containerProperties, Nullable throughput=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract ContainerBuilder DefineContainer(string name, string partitionKeyPath); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Container GetContainer(string id); + public abstract FeedIterator GetContainerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetContainerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract User GetUser(string id); + public abstract FeedIterator GetUserQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadStreamAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadThroughputAsync(RequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ReadThroughputAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertUserAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class DatabaseProperties + { + public DatabaseProperties(); + public DatabaseProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class DatabaseResponse : Response + { + protected DatabaseResponse(); + public override string ActivityId { get; } + public virtual Database Database { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override DatabaseProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Database (DatabaseResponse response); + } + public enum DataType + { + LineString = 3, + MultiPolygon = 5, + Number = 0, + Point = 2, + Polygon = 4, + String = 1, + } + public sealed class ExcludedPath + { + public ExcludedPath(); + public string Path { get; set; } + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedIterator : IDisposable + { + protected FeedIterator(); + public abstract bool HasMoreResults { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public abstract Task> ReadNextAsync(CancellationToken cancellationToken=default(CancellationToken)); + } + public abstract class FeedResponse : IEnumerable, IEnumerable + { + protected FeedResponse(); + public override string ActivityId { get; } + public abstract string ContinuationToken { get; } + public abstract int Count { get; } + public override string ETag { get; } + public override double RequestCharge { get; } + public abstract IEnumerator GetEnumerator(); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public sealed class GeospatialConfig + { + public GeospatialConfig(); + public GeospatialConfig(GeospatialType geospatialType); + public GeospatialType GeospatialType { get; set; } + } + public enum GeospatialType + { + Geography = 0, + Geometry = 1, + } + public class Headers : IEnumerable + { + public Headers(); + public virtual string ActivityId { get; } + public virtual string ContentLength { get; set; } + public virtual string ContentType { get; } + public virtual string ContinuationToken { get; } + public virtual string ETag { get; } + public virtual string this[string headerName] { get; set; } + public virtual string Location { get; } + public virtual double RequestCharge { get; } + public virtual string Session { get; } + public virtual void Add(string headerName, IEnumerable values); + public virtual void Add(string headerName, string value); + public virtual string[] AllKeys(); + public virtual string Get(string headerName); + public virtual IEnumerator GetEnumerator(); + public virtual T GetHeaderValue(string headerName); + public virtual string GetValueOrDefault(string headerName); + public virtual void Remove(string headerName); + public virtual void Set(string headerName, string value); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + public virtual bool TryGetValue(string headerName, out string value); + } + public sealed class IncludedPath + { + public IncludedPath(); + public string Path { get; set; } + } + public enum IndexingDirective + { + Default = 0, + Exclude = 2, + Include = 1, + } + public enum IndexingMode + { + Consistent = 0, + Lazy = 1, + None = 2, + } + public sealed class IndexingPolicy + { + public IndexingPolicy(); + public bool Automatic { get; set; } + public Collection> CompositeIndexes { get; } + public Collection ExcludedPaths { get; } + public Collection IncludedPaths { get; } + public IndexingMode IndexingMode { get; set; } + public Collection SpatialIndexes { get; } + } + public enum IndexKind + { + Hash = 0, + Range = 1, + Spatial = 2, + } + public class ItemRequestOptions : RequestOptions + { + public ItemRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + public IEnumerable PostTriggers { get; set; } + public IEnumerable PreTriggers { get; set; } + public string SessionToken { get; set; } + } + public class ItemResponse : Response + { + protected ItemResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public override HttpStatusCode StatusCode { get; } + } + public enum OperationKind + { + Create = 1, + Delete = 3, + Invalid = 0, + Read = 4, + Replace = 2, + } + public struct PartitionKey : IEquatable + { + public static readonly PartitionKey None; + public static readonly PartitionKey Null; + public static readonly string SystemKeyName; + public static readonly string SystemKeyPath; + public PartitionKey(bool partitionKeyValue); + public PartitionKey(double partitionKeyValue); + public PartitionKey(string partitionKeyValue); + public bool Equals(PartitionKey other); + public override bool Equals(object obj); + public override int GetHashCode(); + public static bool operator ==(PartitionKey left, PartitionKey right); + public static bool operator !=(PartitionKey left, PartitionKey right); + public override string ToString(); + } + public enum PartitionKeyDefinitionVersion + { + V1 = 1, + V2 = 2, + } + public abstract class Permission + { + protected Permission(); + public abstract string Id { get; } + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadAsync(Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public enum PermissionMode : byte + { + All = (byte)2, + Read = (byte)1, + } + public class PermissionProperties + { + public PermissionProperties(string id, PermissionMode permissionMode, Container container, PartitionKey resourcePartitionKey, string itemId); + public PermissionProperties(string id, PermissionMode permissionMode, Container container, Nullable resourcePartitionKey=default(Nullable)); + public string ETag { get; } + public string Id { get; } + public Nullable LastModified { get; } + public PermissionMode PermissionMode { get; } + public Nullable ResourcePartitionKey { get; set; } + public string ResourceUri { get; } + public string SelfLink { get; } + public string Token { get; } + } + public class PermissionResponse : Response + { + protected PermissionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public virtual Permission Permission { get; } + public override double RequestCharge { get; } + public override PermissionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator Permission (PermissionResponse response); + } + public enum PortReuseMode + { + PrivatePortPool = 1, + ReuseUnicastPort = 0, + } + public class QueryDefinition + { + public QueryDefinition(string query); + public string QueryText { get; } + public QueryDefinition WithParameter(string name, object value); + } + public class QueryRequestOptions : RequestOptions + { + public QueryRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public Nullable EnableLowPrecisionOrderBy { get; set; } + public Nullable EnableScanInQuery { get; set; } + public Nullable MaxBufferedItemCount { get; set; } + public Nullable MaxConcurrency { get; set; } + public Nullable MaxItemCount { get; set; } + public Nullable PartitionKey { get; set; } + public Nullable ResponseContinuationTokenLimitInKb { get; set; } + public string SessionToken { get; set; } + } + public static class Regions + { + public const string AustraliaCentral = "Australia Central"; + public const string AustraliaCentral2 = "Australia Central 2"; + public const string AustraliaEast = "Australia East"; + public const string AustraliaSoutheast = "Australia Southeast"; + public const string BrazilSouth = "Brazil South"; + public const string BrazilSoutheast = "Brazil Southeast"; + public const string CanadaCentral = "Canada Central"; + public const string CanadaEast = "Canada East"; + public const string CentralIndia = "Central India"; + public const string CentralUS = "Central US"; + public const string CentralUSEUAP = "Central US EUAP"; + public const string ChinaEast = "China East"; + public const string ChinaEast2 = "China East 2"; + public const string ChinaNorth = "China North"; + public const string ChinaNorth2 = "China North 2"; + public const string EastAsia = "East Asia"; + public const string EastUS = "East US"; + public const string EastUS2 = "East US 2"; + public const string EastUS2EUAP = "East US 2 EUAP"; + public const string FranceCentral = "France Central"; + public const string FranceSouth = "France South"; + public const string GermanyCentral = "Germany Central"; + public const string GermanyNorth = "Germany North"; + public const string GermanyNortheast = "Germany Northeast"; + public const string GermanyWestCentral = "Germany West Central"; + public const string JapanEast = "Japan East"; + public const string JapanWest = "Japan West"; + public const string JioIndiaCentral = "Jio India Central"; + public const string JioIndiaWest = "Jio India West"; + public const string KoreaCentral = "Korea Central"; + public const string KoreaSouth = "Korea South"; + public const string NorthCentralUS = "North Central US"; + public const string NorthEurope = "North Europe"; + public const string NorwayEast = "Norway East"; + public const string NorwayWest = "Norway West"; + public const string SouthAfricaNorth = "South Africa North"; + public const string SouthAfricaWest = "South Africa West"; + public const string SouthCentralUS = "South Central US"; + public const string SoutheastAsia = "Southeast Asia"; + public const string SouthIndia = "South India"; + public const string SwitzerlandNorth = "Switzerland North"; + public const string SwitzerlandWest = "Switzerland West"; + public const string UAECentral = "UAE Central"; + public const string UAENorth = "UAE North"; + public const string UKSouth = "UK South"; + public const string UKWest = "UK West"; + public const string USDoDCentral = "USDoD Central"; + public const string USDoDEast = "USDoD East"; + public const string USGovArizona = "USGov Arizona"; + public const string USGovTexas = "USGov Texas"; + public const string USGovVirginia = "USGov Virginia"; + public const string USNatEast = "USNat East"; + public const string USNatWest = "USNat West"; + public const string USSecEast = "USSec East"; + public const string USSecWest = "USSec West"; + public const string WestCentralUS = "West Central US"; + public const string WestEurope = "West Europe"; + public const string WestIndia = "West India"; + public const string WestUS = "West US"; + public const string WestUS2 = "West US 2"; + public const string WestUS3 = "West US 3"; + } + public abstract class RequestHandler + { + protected RequestHandler(); + public RequestHandler InnerHandler { get; set; } + public virtual Task SendAsync(RequestMessage request, CancellationToken cancellationToken); + } + public class RequestMessage : IDisposable + { + public RequestMessage(); + public RequestMessage(HttpMethod method, Uri requestUri); + public virtual Stream Content { get; set; } + public virtual Headers Headers { get; } + public virtual HttpMethod Method { get; } + public virtual Dictionary Properties { get; } + public virtual Uri RequestUri { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + } + public class RequestOptions + { + public RequestOptions(); + public string IfMatchEtag { get; set; } + public string IfNoneMatchEtag { get; set; } + public IReadOnlyDictionary Properties { get; set; } + } + public class ResponseMessage : IDisposable + { + public ResponseMessage(); + public ResponseMessage(HttpStatusCode statusCode, RequestMessage requestMessage=null, string errorMessage=null); + public virtual Stream Content { get; set; } + public virtual string ContinuationToken { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual RequestMessage RequestMessage { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual ResponseMessage EnsureSuccessStatusCode(); + } + public abstract class Response + { + protected Response(); + public abstract string ActivityId { get; } + public abstract CosmosDiagnostics Diagnostics { get; } + public abstract string ETag { get; } + public abstract Headers Headers { get; } + public abstract double RequestCharge { get; } + public abstract T Resource { get; } + public abstract HttpStatusCode StatusCode { get; } + public static implicit operator T (Response response); + } + public sealed class SpatialPath + { + public SpatialPath(); + public BoundingBoxProperties BoundingBox { get; set; } + public string Path { get; set; } + public Collection SpatialTypes { get; } + } + public enum SpatialType + { + LineString = 1, + MultiPolygon = 3, + Point = 0, + Polygon = 2, + } + public class ThroughputProperties + { + public Nullable AutoscaleMaxThroughput { get; } + public string ETag { get; } + public Nullable LastModified { get; } + public string SelfLink { get; } + public Nullable Throughput { get; } + public static ThroughputProperties CreateAutoscaleThroughput(int autoscaleMaxThroughput); + public static ThroughputProperties CreateManualThroughput(int throughput); + } + public class ThroughputResponse : Response + { + protected ThroughputResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public Nullable IsReplacePending { get; } + public Nullable MinThroughput { get; } + public override double RequestCharge { get; } + public override ThroughputProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator ThroughputProperties (ThroughputResponse response); + } + public abstract class TransactionalBatch + { + protected TransactionalBatch(); + public abstract TransactionalBatch CreateItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch CreateItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch DeleteItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract Task ExecuteAsync(TransactionalBatchRequestOptions requestOptions, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteAsync(CancellationToken cancellationToken=default(CancellationToken)); + public abstract TransactionalBatch ReadItem(string id, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItemStream(string id, Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch ReplaceItem(string id, T item, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItemStream(Stream streamPayload, TransactionalBatchItemRequestOptions requestOptions=null); + public abstract TransactionalBatch UpsertItem(T item, TransactionalBatchItemRequestOptions requestOptions=null); + } + public class TransactionalBatchItemRequestOptions : RequestOptions + { + public TransactionalBatchItemRequestOptions(); + public Nullable EnableContentResponseOnWrite { get; set; } + public Nullable IndexingDirective { get; set; } + } + public class TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual string ETag { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual Stream ResourceStream { get; } + public virtual TimeSpan RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + } + public class TransactionalBatchOperationResult : TransactionalBatchOperationResult + { + protected TransactionalBatchOperationResult(); + public virtual T Resource { get; set; } + } + public class TransactionalBatchRequestOptions : RequestOptions + { + public TransactionalBatchRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + } + public class TransactionalBatchResponse : IDisposable, IEnumerable, IEnumerable, IReadOnlyCollection, IReadOnlyList + { + protected TransactionalBatchResponse(); + public virtual string ActivityId { get; } + public virtual int Count { get; } + public virtual CosmosDiagnostics Diagnostics { get; } + public virtual string ErrorMessage { get; } + public virtual Headers Headers { get; } + public virtual bool IsSuccessStatusCode { get; } + public virtual TransactionalBatchOperationResult this[int index] { get; } + public virtual double RequestCharge { get; } + public virtual Nullable RetryAfter { get; } + public virtual HttpStatusCode StatusCode { get; } + public void Dispose(); + protected virtual void Dispose(bool disposing); + public virtual IEnumerator GetEnumerator(); + public virtual TransactionalBatchOperationResult GetOperationResultAtIndex(int index); + IEnumerator System.Collections.IEnumerable.GetEnumerator(); + } + public class UniqueKey + { + public UniqueKey(); + public Collection Paths { get; } + } + public sealed class UniqueKeyPolicy + { + public UniqueKeyPolicy(); + public Collection UniqueKeys { get; } + } + public abstract class User + { + protected User(); + public abstract string Id { get; } + public abstract Task CreatePermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Permission GetPermission(string id); + public abstract FeedIterator GetPermissionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetPermissionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadAsync(RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceAsync(UserProperties userProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task UpsertPermissionAsync(PermissionProperties permissionProperties, Nullable tokenExpiryInSeconds=default(Nullable), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class UserProperties + { + protected UserProperties(); + public UserProperties(string id); + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class UserResponse : Response + { + protected UserResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public virtual User User { get; } + public static implicit operator User (UserResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Fluent +{ + public class CompositeIndexDefinition + { + public T Attach(); + public CompositeIndexDefinition Path(string path); + public CompositeIndexDefinition Path(string path, CompositePathSortOrder sortOrder); + } + public class ConflictResolutionDefinition + { + public ContainerBuilder Attach(); + public ConflictResolutionDefinition WithCustomStoredProcedureResolution(string conflictResolutionProcedure); + public ConflictResolutionDefinition WithLastWriterWinsResolution(string conflictResolutionPath); + } + public class ContainerBuilder : ContainerDefinition + { + protected ContainerBuilder(); + public new ContainerProperties Build(); + public Task CreateAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken=default(CancellationToken)); + public Task CreateIfNotExistsAsync(Nullable throughput=default(Nullable), CancellationToken cancellationToken=default(CancellationToken)); + public ConflictResolutionDefinition WithConflictResolution(); + public UniqueKeyDefinition WithUniqueKey(); + } + public abstract class ContainerDefinition where T : ContainerDefinition + { + public ContainerDefinition(); + public ContainerProperties Build(); + public T WithDefaultTimeToLive(int defaultTtlInSeconds); + public T WithDefaultTimeToLive(TimeSpan defaultTtlTimeSpan); + public IndexingPolicyDefinition WithIndexingPolicy(); + public T WithPartitionKeyDefinitionVersion(PartitionKeyDefinitionVersion partitionKeyDefinitionVersion); + public T WithTimeToLivePropertyPath(string propertyPath); + } + public class CosmosClientBuilder + { + public CosmosClientBuilder(string connectionString); + public CosmosClientBuilder(string accountEndpoint, string authKeyOrResourceToken); + public CosmosClientBuilder AddCustomHandlers(params RequestHandler[] customHandlers); + public CosmosClient Build(); + public CosmosClientBuilder WithApplicationName(string applicationName); + public CosmosClientBuilder WithApplicationPreferredRegions(IReadOnlyList applicationPreferredRegions); + public CosmosClientBuilder WithApplicationRegion(string applicationRegion); + public CosmosClientBuilder WithBulkExecution(bool enabled); + public CosmosClientBuilder WithConnectionModeDirect(); + public CosmosClientBuilder WithConnectionModeDirect(Nullable idleTcpConnectionTimeout=default(Nullable), Nullable openTcpConnectionTimeout=default(Nullable), Nullable maxRequestsPerTcpConnection=default(Nullable), Nullable maxTcpConnectionsPerEndpoint=default(Nullable), Nullable portReuseMode=default(Nullable), Nullable enableTcpConnectionEndpointRediscovery=default(Nullable)); + public CosmosClientBuilder WithConnectionModeGateway(Nullable maxConnectionLimit=default(Nullable), IWebProxy webProxy=null); + public CosmosClientBuilder WithConsistencyLevel(ConsistencyLevel consistencyLevel); + public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer); + public CosmosClientBuilder WithHttpClientFactory(Func httpClientFactory); + public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); + public CosmosClientBuilder WithRequestTimeout(TimeSpan requestTimeout); + public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosmosSerializerOptions); + public CosmosClientBuilder WithThrottlingRetryOptions(TimeSpan maxRetryWaitTimeOnThrottledRequests, int maxRetryAttemptsOnThrottledRequests); + } + public class IndexingPolicyDefinition + { + public IndexingPolicyDefinition(); + public T Attach(); + public IndexingPolicyDefinition WithAutomaticIndexing(bool enabled); + public CompositeIndexDefinition> WithCompositeIndex(); + public PathsDefinition> WithExcludedPaths(); + public PathsDefinition> WithIncludedPaths(); + public IndexingPolicyDefinition WithIndexingMode(IndexingMode indexingMode); + public SpatialIndexDefinition> WithSpatialIndex(); + } + public class PathsDefinition + { + public T Attach(); + public PathsDefinition Path(string path); + } + public class SpatialIndexDefinition + { + public T Attach(); + public SpatialIndexDefinition Path(string path); + public SpatialIndexDefinition Path(string path, params SpatialType[] spatialTypes); + } + public class UniqueKeyDefinition + { + public ContainerBuilder Attach(); + public UniqueKeyDefinition Path(string path); + } +} +namespace Microsoft.Azure.Cosmos.Linq +{ + public static class CosmosLinqExtensions + { + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> AverageAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> AverageAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> CountAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static bool IsDefined(this object obj); + public static bool IsNull(this object obj); + public static bool IsPrimitive(this object obj); + public static Task> MaxAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> MinAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task>> SumAsync(this IQueryable> source, CancellationToken cancellationToken=default(CancellationToken)); + public static Task> SumAsync(this IQueryable source, CancellationToken cancellationToken=default(CancellationToken)); + public static FeedIterator ToFeedIterator(this IQueryable query); + public static QueryDefinition ToQueryDefinition(this IQueryable query); + public static FeedIterator ToStreamIterator(this IQueryable query); + } +} +namespace Microsoft.Azure.Cosmos.Scripts +{ + public abstract class Scripts + { + protected Scripts(); + public abstract Task CreateStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task CreateUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task DeleteUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task> ExecuteStoredProcedureAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, PartitionKey partitionKey, dynamic parameters, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ExecuteStoredProcedureStreamAsync(string storedProcedureId, Stream streamPayload, PartitionKey partitionKey, StoredProcedureRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract FeedIterator GetStoredProcedureQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetStoredProcedureQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetTriggerQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(QueryDefinition queryDefinition, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract FeedIterator GetUserDefinedFunctionQueryStreamIterator(string queryText=null, string continuationToken=null, QueryRequestOptions requestOptions=null); + public abstract Task ReadStoredProcedureAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadTriggerAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReadUserDefinedFunctionAsync(string id, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceStoredProcedureAsync(StoredProcedureProperties storedProcedureProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceTriggerAsync(TriggerProperties triggerProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + public abstract Task ReplaceUserDefinedFunctionAsync(UserDefinedFunctionProperties userDefinedFunctionProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); + } + public class StoredProcedureExecuteResponse : Response + { + protected StoredProcedureExecuteResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override T Resource { get; } + public virtual string ScriptLog { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + } + public class StoredProcedureProperties + { + public StoredProcedureProperties(); + public StoredProcedureProperties(string id, string body); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public Nullable LastModified { get; } + public string SelfLink { get; } + } + public class StoredProcedureRequestOptions : RequestOptions + { + public StoredProcedureRequestOptions(); + public Nullable ConsistencyLevel { get; set; } + public bool EnableScriptLogging { get; set; } + public string SessionToken { get; set; } + } + public class StoredProcedureResponse : Response + { + protected StoredProcedureResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override StoredProcedureProperties Resource { get; } + public virtual string SessionToken { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator StoredProcedureProperties (StoredProcedureResponse response); + } + public enum TriggerOperation : short + { + All = (short)0, + Create = (short)1, + Delete = (short)3, + Replace = (short)4, + Update = (short)2, + } + public class TriggerProperties + { + public TriggerProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + public TriggerOperation TriggerOperation { get; set; } + public TriggerType TriggerType { get; set; } + } + public class TriggerResponse : Response + { + protected TriggerResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override TriggerProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator TriggerProperties (TriggerResponse response); + } + public enum TriggerType : byte + { + Post = (byte)1, + Pre = (byte)0, + } + public class UserDefinedFunctionProperties + { + public UserDefinedFunctionProperties(); + public string Body { get; set; } + public string ETag { get; } + public string Id { get; set; } + public string SelfLink { get; } + } + public class UserDefinedFunctionResponse : Response + { + protected UserDefinedFunctionResponse(); + public override string ActivityId { get; } + public override CosmosDiagnostics Diagnostics { get; } + public override string ETag { get; } + public override Headers Headers { get; } + public override double RequestCharge { get; } + public override UserDefinedFunctionProperties Resource { get; } + public override HttpStatusCode StatusCode { get; } + public static implicit operator UserDefinedFunctionProperties (UserDefinedFunctionResponse response); + } +} +namespace Microsoft.Azure.Cosmos.Spatial +{ + public sealed class BoundingBox : IEquatable + { + public BoundingBox(Position min, Position max); + public Position Max { get; } + public Position Min { get; } + public bool Equals(BoundingBox other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public abstract class Crs + { + protected Crs(CrsType type); + public static Crs Default { get; } + public CrsType Type { get; } + public static Crs Unspecified { get; } + public static LinkedCrs Linked(string href); + public static LinkedCrs Linked(string href, string type); + public static NamedCrs Named(string name); + } + public enum CrsType + { + Linked = 1, + Named = 0, + Unspecified = 2, + } + public abstract class Geometry + { + protected Geometry(GeometryType type, GeometryParams geometryParams); + public IDictionary AdditionalProperties { get; } + public BoundingBox BoundingBox { get; } + public Crs Crs { get; } + public GeometryType Type { get; } + public double Distance(Geometry to); + public override bool Equals(object obj); + public override int GetHashCode(); + public bool Intersects(Geometry geometry2); + public bool IsValid(); + public GeometryValidationResult IsValidDetailed(); + public bool Within(Geometry outer); + } + public class GeometryParams + { + public GeometryParams(); + public IDictionary AdditionalProperties { get; set; } + public BoundingBox BoundingBox { get; set; } + public Crs Crs { get; set; } + } + public enum GeometryShape + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public enum GeometryType + { + GeometryCollection = 6, + LineString = 2, + MultiLineString = 3, + MultiPoint = 1, + MultiPolygon = 5, + Point = 0, + Polygon = 4, + } + public class GeometryValidationResult + { + public GeometryValidationResult(); + public bool IsValid { get; } + public string Reason { get; } + } + public sealed class LinearRing : IEquatable + { + public LinearRing(IList coordinates); + public ReadOnlyCollection Positions { get; } + public bool Equals(LinearRing other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LineString : Geometry, IEquatable + { + public LineString(IList coordinates); + public LineString(IList coordinates, GeometryParams geometryParams); + public ReadOnlyCollection Positions { get; } + public bool Equals(LineString other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class LinkedCrs : Crs, IEquatable + { + public string Href { get; } + public string HrefType { get; } + public bool Equals(LinkedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class MultiPolygon : Geometry, IEquatable + { + public MultiPolygon(IList polygons); + public MultiPolygon(IList polygons, GeometryParams geometryParams); + public ReadOnlyCollection Polygons { get; } + public bool Equals(MultiPolygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class NamedCrs : Crs, IEquatable + { + public string Name { get; } + public bool Equals(NamedCrs other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Point : Geometry, IEquatable + { + public Point(Position position); + public Point(Position position, GeometryParams geometryParams); + public Point(double longitude, double latitude); + public Position Position { get; } + public bool Equals(Point other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Polygon : Geometry, IEquatable + { + public Polygon(IList rings); + public Polygon(IList rings, GeometryParams geometryParams); + public Polygon(IList externalRingPositions); + public ReadOnlyCollection Rings { get; } + public bool Equals(Polygon other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class PolygonCoordinates : IEquatable + { + public PolygonCoordinates(IList rings); + public ReadOnlyCollection Rings { get; } + public bool Equals(PolygonCoordinates other); + public override bool Equals(object obj); + public override int GetHashCode(); + } + public sealed class Position : IEquatable + { + public Position(IList coordinates); + public Position(double longitude, double latitude); + public Position(double longitude, double latitude, Nullable altitude); + public Nullable Altitude { get; } + public ReadOnlyCollection Coordinates { get; } + public double Latitude { get; } + public double Longitude { get; } + public bool Equals(Position other); + public override bool Equals(object obj); + public override int GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs index 20f70f92a4..b3fd29a634 100644 --- a/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs +++ b/Microsoft.Azure.Cosmos/src/Batch/BatchAsyncContainerExecutor.cs @@ -25,7 +25,6 @@ namespace Microsoft.Azure.Cosmos /// internal class BatchAsyncContainerExecutor : IDisposable { - private const int DefaultDispatchTimerInSeconds = 1; private const int TimerWheelBucketCount = 20; private static readonly TimeSpan TimerWheelResolution = TimeSpan.FromMilliseconds(50); @@ -52,11 +51,6 @@ public BatchAsyncContainerExecutor( int maxServerRequestOperationCount, int maxServerRequestBodyLength) { - if (cosmosContainer == null) - { - throw new ArgumentNullException(nameof(cosmosContainer)); - } - if (maxServerRequestOperationCount < 1) { throw new ArgumentOutOfRangeException(nameof(maxServerRequestOperationCount)); @@ -67,7 +61,7 @@ public BatchAsyncContainerExecutor( throw new ArgumentOutOfRangeException(nameof(maxServerRequestBodyLength)); } - this.cosmosContainer = cosmosContainer; + this.cosmosContainer = cosmosContainer ?? throw new ArgumentNullException(nameof(cosmosContainer)); this.cosmosClientContext = cosmosClientContext; this.maxServerRequestBodyLength = maxServerRequestBodyLength; this.maxServerRequestOperationCount = maxServerRequestOperationCount; @@ -89,7 +83,7 @@ public virtual async Task AddAsync( string resolvedPartitionKeyRangeId = await this.ResolvePartitionKeyRangeIdAsync(operation, cancellationToken).ConfigureAwait(false); BatchAsyncStreamer streamer = this.GetOrAddStreamerForPartitionKeyRange(resolvedPartitionKeyRangeId); - ItemBatchOperationContext context = new ItemBatchOperationContext(resolvedPartitionKeyRangeId, BatchAsyncContainerExecutor.GetRetryPolicy(this.retryOptions)); + ItemBatchOperationContext context = new ItemBatchOperationContext(resolvedPartitionKeyRangeId, BatchAsyncContainerExecutor.GetRetryPolicy(this.cosmosContainer, this.retryOptions)); operation.AttachContext(context); streamer.Add(operation); return await context.OperationTask; @@ -136,10 +130,13 @@ internal virtual async Task ValidateOperationAsync( await operation.MaterializeResourceAsync(this.cosmosClientContext.SerializerCore, cancellationToken); } - private static IDocumentClientRetryPolicy GetRetryPolicy(RetryOptions retryOptions) + private static IDocumentClientRetryPolicy GetRetryPolicy( + ContainerInternal containerInternal, + RetryOptions retryOptions) { return new BulkPartitionKeyRangeGoneRetryPolicy( - new ResourceThrottleRetryPolicy( + containerInternal, + new ResourceThrottleRetryPolicy( retryOptions.MaxRetryAttemptsOnThrottledRequests, retryOptions.MaxRetryWaitTimeInSeconds)); } @@ -185,6 +182,7 @@ private async Task ReBatchAsync( CancellationToken cancellationToken) { string resolvedPartitionKeyRangeId = await this.ResolvePartitionKeyRangeIdAsync(operation, cancellationToken).ConfigureAwait(false); + operation.Context.ReRouteOperation(resolvedPartitionKeyRangeId); BatchAsyncStreamer streamer = this.GetOrAddStreamerForPartitionKeyRange(resolvedPartitionKeyRangeId); streamer.Add(operation); } diff --git a/Microsoft.Azure.Cosmos/src/Batch/ItemBatchOperationContext.cs b/Microsoft.Azure.Cosmos/src/Batch/ItemBatchOperationContext.cs index f384b04497..eabf539789 100644 --- a/Microsoft.Azure.Cosmos/src/Batch/ItemBatchOperationContext.cs +++ b/Microsoft.Azure.Cosmos/src/Batch/ItemBatchOperationContext.cs @@ -15,7 +15,7 @@ namespace Microsoft.Azure.Cosmos /// internal class ItemBatchOperationContext : IDisposable { - public string PartitionKeyRangeId { get; } + public string PartitionKeyRangeId { get; private set; } public BatchAsyncBatcher CurrentBatcher { get; set; } @@ -74,6 +74,11 @@ public void Fail( this.Dispose(); } + public void ReRouteOperation(string newPartitionKeyRangeId) + { + this.PartitionKeyRangeId = newPartitionKeyRangeId; + } + public void Dispose() { this.CurrentBatcher = null; diff --git a/Microsoft.Azure.Cosmos/src/BulkPartitionKeyRangeGoneRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/BulkPartitionKeyRangeGoneRetryPolicy.cs index b0133ee944..a72b18b66b 100644 --- a/Microsoft.Azure.Cosmos/src/BulkPartitionKeyRangeGoneRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/BulkPartitionKeyRangeGoneRetryPolicy.cs @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos using System.Net; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; /// @@ -17,59 +18,61 @@ namespace Microsoft.Azure.Cosmos /// internal sealed class BulkPartitionKeyRangeGoneRetryPolicy : IDocumentClientRetryPolicy { - private const int MaxRetries = 1; - private readonly IDocumentClientRetryPolicy nextRetryPolicy; + private readonly ContainerInternal container; - private int retriesAttempted; - - public BulkPartitionKeyRangeGoneRetryPolicy(IDocumentClientRetryPolicy nextRetryPolicy) + public BulkPartitionKeyRangeGoneRetryPolicy( + ContainerInternal container, + IDocumentClientRetryPolicy nextRetryPolicy) { + this.container = container ?? throw new ArgumentNullException(nameof(container)); this.nextRetryPolicy = nextRetryPolicy; } - public Task ShouldRetryAsync( + public async Task ShouldRetryAsync( Exception exception, CancellationToken cancellationToken) { - DocumentClientException clientException = exception as DocumentClientException; - - ShouldRetryResult shouldRetryResult = this.ShouldRetryInternal( - clientException?.StatusCode, - clientException?.GetSubStatus(), - clientException?.ResourceAddress); - - if (shouldRetryResult != null) + if (exception is CosmosException clientException) { - return Task.FromResult(shouldRetryResult); + ShouldRetryResult shouldRetryResult = await this.ShouldRetryInternalAsync( + clientException.StatusCode, + (SubStatusCodes)clientException.SubStatusCode, + cancellationToken); + + if (shouldRetryResult != null) + { + return shouldRetryResult; + } + + if (this.nextRetryPolicy == null) + { + return ShouldRetryResult.NoRetry(); + } } - if (this.nextRetryPolicy == null) - { - return Task.FromResult(ShouldRetryResult.NoRetry()); - } - - return this.nextRetryPolicy.ShouldRetryAsync(exception, cancellationToken); + return await this.nextRetryPolicy.ShouldRetryAsync(exception, cancellationToken); } - public Task ShouldRetryAsync( + public async Task ShouldRetryAsync( ResponseMessage cosmosResponseMessage, CancellationToken cancellationToken) { - ShouldRetryResult shouldRetryResult = this.ShouldRetryInternal(cosmosResponseMessage?.StatusCode, + ShouldRetryResult shouldRetryResult = await this.ShouldRetryInternalAsync( + cosmosResponseMessage?.StatusCode, cosmosResponseMessage?.Headers.SubStatusCode, - cosmosResponseMessage?.GetResourceAddress()); + cancellationToken); if (shouldRetryResult != null) { - return Task.FromResult(shouldRetryResult); + return shouldRetryResult; } if (this.nextRetryPolicy == null) { - return Task.FromResult(ShouldRetryResult.NoRetry()); + return ShouldRetryResult.NoRetry(); } - return this.nextRetryPolicy.ShouldRetryAsync(cosmosResponseMessage, cancellationToken); + return await this.nextRetryPolicy.ShouldRetryAsync(cosmosResponseMessage, cancellationToken); } public void OnBeforeSendRequest(DocumentServiceRequest request) @@ -77,17 +80,27 @@ public void OnBeforeSendRequest(DocumentServiceRequest request) this.nextRetryPolicy.OnBeforeSendRequest(request); } - private ShouldRetryResult ShouldRetryInternal( + private async Task ShouldRetryInternalAsync( HttpStatusCode? statusCode, SubStatusCodes? subStatusCode, - string resourceIdOrFullName) + CancellationToken cancellationToken) { - if (statusCode == HttpStatusCode.Gone - && (subStatusCode == SubStatusCodes.PartitionKeyRangeGone || subStatusCode == SubStatusCodes.NameCacheIsStale) - && this.retriesAttempted < MaxRetries) + if (statusCode == HttpStatusCode.Gone) { - this.retriesAttempted++; - return ShouldRetryResult.RetryAfter(TimeSpan.Zero); + if (subStatusCode == SubStatusCodes.PartitionKeyRangeGone + || subStatusCode == SubStatusCodes.CompletingSplit + || subStatusCode == SubStatusCodes.CompletingPartitionMigration) + { + PartitionKeyRangeCache partitionKeyRangeCache = await this.container.ClientContext.DocumentClient.GetPartitionKeyRangeCacheAsync(); + string containerRid = await this.container.GetRIDAsync(cancellationToken: cancellationToken); + await partitionKeyRangeCache.TryGetOverlappingRangesAsync(containerRid, FeedRangeEpk.FullRange.Range, forceRefresh: true); + return ShouldRetryResult.RetryAfter(TimeSpan.Zero); + } + + if (subStatusCode == SubStatusCodes.NameCacheIsStale) + { + return ShouldRetryResult.RetryAfter(TimeSpan.Zero); + } } return null; diff --git a/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedStartFromRequestOptionPopulator.cs b/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedStartFromRequestOptionPopulator.cs index d902ebd740..399b6ec09c 100644 --- a/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedStartFromRequestOptionPopulator.cs +++ b/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedStartFromRequestOptionPopulator.cs @@ -41,7 +41,7 @@ public override void Visit(ChangeFeedStartFromTime startFromTime) { this.requestMessage.Headers.Add( HttpConstants.HttpHeaders.IfModifiedSince, - startFromTime.StartTime.ToString("o", CultureInfo.InvariantCulture)); + startFromTime.StartTime.ToString("r", CultureInfo.InvariantCulture)); } startFromTime.FeedRange.Accept(FeedRangeRequestMessagePopulatorVisitor.Singleton, this.requestMessage); diff --git a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs index 44d42d6781..6d6a4b9780 100644 --- a/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs +++ b/Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnosticsSerializerVisitor.cs @@ -93,6 +93,9 @@ public override void Visit(CosmosDiagnosticsContext cosmosDiagnosticsContext) this.jsonWriter.WritePropertyName("FailedRequestCount"); this.jsonWriter.WriteValue(cosmosDiagnosticsContext.GetFailedResponseCount()); + this.jsonWriter.WritePropertyName("Operation"); + this.jsonWriter.WriteValue(cosmosDiagnosticsContext.OperationName); + this.jsonWriter.WriteEndObject(); this.jsonWriter.WritePropertyName("Context"); diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/TransientHttpClientRetryPolicy.cs b/Microsoft.Azure.Cosmos/src/HttpClient/TransientHttpClientRetryPolicy.cs index 09df225ac4..2630ec49ea 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/TransientHttpClientRetryPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/TransientHttpClientRetryPolicy.cs @@ -47,10 +47,11 @@ public Task ShouldRetryAsync( CancellationToken cancellationToken) { HttpRequestMessage httpRequestMessage = this.getHttpReqestMessage(); + this.diagnosticsContext.AddDiagnosticsInternal( new PointOperationStatistics( activityId: Trace.CorrelationManager.ActivityId.ToString(), - statusCode: HttpStatusCode.InternalServerError, + statusCode: HttpStatusCode.RequestTimeout, subStatusCode: SubStatusCodes.Unknown, responseTimeUtc: DateTime.UtcNow, requestCharge: 0, diff --git a/Microsoft.Azure.Cosmos/src/Util/Extensions.cs b/Microsoft.Azure.Cosmos/src/Util/Extensions.cs index de278e7677..d88c786aa0 100644 --- a/Microsoft.Azure.Cosmos/src/Util/Extensions.cs +++ b/Microsoft.Azure.Cosmos/src/Util/Extensions.cs @@ -129,7 +129,7 @@ internal static ResponseMessage ToCosmosResponseMessage(this DocumentClientExcep PointOperationStatistics pointOperationStatistics = new PointOperationStatistics( activityId: cosmosException.Headers.ActivityId, statusCode: cosmosException.StatusCode, - subStatusCode: (int)SubStatusCodes.Unknown, + subStatusCode: cosmosException.Headers.SubStatusCode, responseTimeUtc: DateTime.UtcNow, requestCharge: cosmosException.Headers.RequestCharge, errorMessage: documentClientException.ToString(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ChangeFeed/DynamicTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ChangeFeed/DynamicTests.cs index b2e31db4e3..701f41a90b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ChangeFeed/DynamicTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ChangeFeed/DynamicTests.cs @@ -238,5 +238,59 @@ public async Task TestWithStartTime_Beginning() Assert.IsTrue(isStartOk, "Timed out waiting for docs to process"); Assert.AreEqual("doc0.doc1.doc2.doc3.doc4.", accumulator); } + + [TestMethod] + public async Task TestWithStartTime_CustomTime() + { + int partitionKey = 0; + + foreach (int id in Enumerable.Range(0, 5)) + { + await this.Container.CreateItemAsync(new { id = $"doc{id}", pk = partitionKey }); + } + + await Task.Delay(1000); + + DateTime now = DateTime.UtcNow; + + await Task.Delay(1000); + + foreach (int id in Enumerable.Range(5, 5)) + { + await this.Container.CreateItemAsync(new { id = $"doc{id}", pk = partitionKey }); + } + + ManualResetEvent allDocsProcessed = new ManualResetEvent(false); + + int processedDocCount = 0; + string accumulator = string.Empty; + ChangeFeedProcessor processor = this.Container + .GetChangeFeedProcessorBuilder("test", (IReadOnlyCollection docs, CancellationToken token) => + { + Assert.IsTrue(docs.Count > 0); + processedDocCount += docs.Count; + foreach (dynamic doc in docs) + { + accumulator += doc.id.ToString() + "."; + } + + if (processedDocCount == 5) + { + allDocsProcessed.Set(); + } + + return Task.CompletedTask; + }) + .WithStartTime(now) + .WithInstanceName("random") + .WithLeaseContainer(this.LeaseContainer).Build(); + + await processor.StartAsync(); + // Letting processor initialize and pickup changes + bool isStartOk = allDocsProcessed.WaitOne(10 * BaseChangeFeedClientHelper.ChangeFeedSetupTime); + await processor.StopAsync(); + Assert.IsTrue(isStartOk, "Timed out waiting for docs to process"); + Assert.AreEqual("doc5.doc6.doc7.doc8.doc9.", accumulator); + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs index a837f70e85..1d0356ece5 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/DiagnosticValidators.cs @@ -62,6 +62,7 @@ internal static void ValidateCosmosDiagnosticsContext( Assert.IsNotNull(jObject["DiagnosticVersion"].ToString()); JToken summary = jObject["Summary"]; Assert.IsNotNull(summary["UserAgent"].ToString()); + Assert.IsNotNull(summary["Operation"].ToString()); Assert.IsTrue(summary["UserAgent"].ToString().Contains("cosmos-netstandard-sdk")); Assert.IsNotNull(summary["StartUtc"].ToString()); Assert.IsNotNull(summary["TotalElapsedTimeInMs"].ToString()); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs index 4cc4052629..d2aa6ec6aa 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncBatcherTests.cs @@ -11,6 +11,7 @@ namespace Microsoft.Azure.Cosmos.Tests using System.Net; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -118,6 +119,90 @@ private BatchAsyncBatcherExecuteDelegate ExecutorWithSplit return new PartitionKeyRangeBatchExecutionResult(request.PartitionKeyRangeId, request.Operations, batchresponse); }; + private BatchAsyncBatcherExecuteDelegate ExecutorWithCompletingSplit + = async (PartitionKeyRangeServerBatchRequest request, CancellationToken cancellationToken) => + { + List results = new List(); + ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; + int index = 0; + foreach (ItemBatchOperation operation in request.Operations) + { + results.Add( + new TransactionalBatchOperationResult(HttpStatusCode.Gone) + { + ETag = operation.Id, + SubStatusCode = SubStatusCodes.CompletingSplit + }); + + arrayOperations[index++] = operation; + } + + MemoryStream responseContent = await new BatchResponsePayloadWriter(results).GeneratePayloadAsync(); + + SinglePartitionKeyServerBatchRequest batchRequest = await SinglePartitionKeyServerBatchRequest.CreateAsync( + partitionKey: null, + operations: new ArraySegment(arrayOperations), + serializerCore: MockCosmosUtil.Serializer, + cancellationToken: cancellationToken); + + ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.Gone) + { + Content = responseContent + }; + responseMessage.Headers.SubStatusCode = SubStatusCodes.PartitionKeyRangeGone; + + TransactionalBatchResponse batchresponse = await TransactionalBatchResponse.FromResponseMessageAsync( + responseMessage, + batchRequest, + MockCosmosUtil.Serializer, + true, + CancellationToken.None); + + return new PartitionKeyRangeBatchExecutionResult(request.PartitionKeyRangeId, request.Operations, batchresponse); + }; + + private BatchAsyncBatcherExecuteDelegate ExecutorWithCompletingPartitionMigration + = async (PartitionKeyRangeServerBatchRequest request, CancellationToken cancellationToken) => + { + List results = new List(); + ItemBatchOperation[] arrayOperations = new ItemBatchOperation[request.Operations.Count]; + int index = 0; + foreach (ItemBatchOperation operation in request.Operations) + { + results.Add( + new TransactionalBatchOperationResult(HttpStatusCode.Gone) + { + ETag = operation.Id, + SubStatusCode = SubStatusCodes.CompletingSplit + }); + + arrayOperations[index++] = operation; + } + + MemoryStream responseContent = await new BatchResponsePayloadWriter(results).GeneratePayloadAsync(); + + SinglePartitionKeyServerBatchRequest batchRequest = await SinglePartitionKeyServerBatchRequest.CreateAsync( + partitionKey: null, + operations: new ArraySegment(arrayOperations), + serializerCore: MockCosmosUtil.Serializer, + cancellationToken: cancellationToken); + + ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.Gone) + { + Content = responseContent + }; + responseMessage.Headers.SubStatusCode = SubStatusCodes.CompletingPartitionMigration; + + TransactionalBatchResponse batchresponse = await TransactionalBatchResponse.FromResponseMessageAsync( + responseMessage, + batchRequest, + MockCosmosUtil.Serializer, + true, + CancellationToken.None); + + return new PartitionKeyRangeBatchExecutionResult(request.PartitionKeyRangeId, request.Operations, batchresponse); + }; + // The response will include all but 2 operation responses private BatchAsyncBatcherExecuteDelegate ExecutorWithLessResponses = async (PartitionKeyRangeServerBatchRequest request, CancellationToken cancellationToken) => @@ -364,9 +449,11 @@ public async Task CannotAddToDispatchedBatch() public async Task RetrierGetsCalledOnSplit() { IDocumentClientRetryPolicy retryPolicy1 = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), new ResourceThrottleRetryPolicy(1)); IDocumentClientRetryPolicy retryPolicy2 = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), new ResourceThrottleRetryPolicy(1)); ItemBatchOperation operation1 = this.CreateItemBatchOperation(); @@ -387,6 +474,64 @@ public async Task RetrierGetsCalledOnSplit() Assert.IsNull(operation2.DiagnosticsContext, "Batch operations do not have diagnostics per operation"); } + [TestMethod] + public async Task RetrierGetsCalledOnCompletingSplit() + { + IDocumentClientRetryPolicy retryPolicy1 = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + + IDocumentClientRetryPolicy retryPolicy2 = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + + ItemBatchOperation operation1 = this.CreateItemBatchOperation(); + ItemBatchOperation operation2 = this.CreateItemBatchOperation(); + operation1.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy1)); + operation2.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy2)); + + Mock retryDelegate = new Mock(); + + BatchAsyncBatcher batchAsyncBatcher = new BatchAsyncBatcher(2, 1000, MockCosmosUtil.Serializer, this.ExecutorWithCompletingSplit, retryDelegate.Object); + Assert.IsTrue(batchAsyncBatcher.TryAdd(operation1)); + Assert.IsTrue(batchAsyncBatcher.TryAdd(operation2)); + await batchAsyncBatcher.DispatchAsync(metric); + retryDelegate.Verify(a => a(It.Is(o => o == operation1), It.IsAny()), Times.Once); + retryDelegate.Verify(a => a(It.Is(o => o == operation2), It.IsAny()), Times.Once); + retryDelegate.Verify(a => a(It.IsAny(), It.IsAny()), Times.Exactly(2)); + Assert.IsNull(operation1.DiagnosticsContext, "Batch operations do not have diagnostics per operation"); + Assert.IsNull(operation2.DiagnosticsContext, "Batch operations do not have diagnostics per operation"); + } + + [TestMethod] + public async Task RetrierGetsCalledOnCompletingPartitionMigration() + { + IDocumentClientRetryPolicy retryPolicy1 = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + + IDocumentClientRetryPolicy retryPolicy2 = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + + ItemBatchOperation operation1 = this.CreateItemBatchOperation(); + ItemBatchOperation operation2 = this.CreateItemBatchOperation(); + operation1.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy1)); + operation2.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy2)); + + Mock retryDelegate = new Mock(); + + BatchAsyncBatcher batchAsyncBatcher = new BatchAsyncBatcher(2, 1000, MockCosmosUtil.Serializer, this.ExecutorWithCompletingPartitionMigration, retryDelegate.Object); + Assert.IsTrue(batchAsyncBatcher.TryAdd(operation1)); + Assert.IsTrue(batchAsyncBatcher.TryAdd(operation2)); + await batchAsyncBatcher.DispatchAsync(metric); + retryDelegate.Verify(a => a(It.Is(o => o == operation1), It.IsAny()), Times.Once); + retryDelegate.Verify(a => a(It.Is(o => o == operation2), It.IsAny()), Times.Once); + retryDelegate.Verify(a => a(It.IsAny(), It.IsAny()), Times.Exactly(2)); + Assert.IsNull(operation1.DiagnosticsContext, "Batch operations do not have diagnostics per operation"); + Assert.IsNull(operation2.DiagnosticsContext, "Batch operations do not have diagnostics per operation"); + } + [TestMethod] public async Task RetrierGetsCalledOnOverFlow() { @@ -407,6 +552,16 @@ public async Task RetrierGetsCalledOnOverFlow() retryDelegate.Verify(a => a(It.IsAny(), It.IsAny()), Times.Once); } + private static ContainerInternal GetSplitEnabledContainer() + { + Mock container = new Mock(); + container.Setup(c => c.GetRIDAsync(It.IsAny())).ReturnsAsync(Guid.NewGuid().ToString()); + Mock context = new Mock(); + container.Setup(c => c.ClientContext).Returns(context.Object); + context.Setup(c => c.DocumentClient).Returns(new ClientWithSplitDetection()); + return container.Object; + } + private class BatchAsyncBatcherThatOverflows : BatchAsyncBatcher { public BatchAsyncBatcherThatOverflows( @@ -427,5 +582,28 @@ internal override async Task>(serverRequest, new ArraySegment(serverRequest.Operations.ToArray(), 1, 1)); } } + + private class ClientWithSplitDetection : MockDocumentClient + { + private readonly Mock partitionKeyRangeCache; + + public ClientWithSplitDetection() + { + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null); + this.partitionKeyRangeCache.Setup( + m => m.TryGetOverlappingRangesAsync( + It.IsAny(), + It.IsAny>(), + It.Is(b => b == true) // Mocking only the refresh, if it doesn't get called, the test fails + ) + ).Returns((string collectionRid, Documents.Routing.Range range, bool forceRefresh) => Task.FromResult>(this.ResolveOverlapingPartitionKeyRanges(collectionRid, range, forceRefresh))); + } + + internal override Task GetPartitionKeyRangeCacheAsync() + { + return Task.FromResult(this.partitionKeyRangeCache.Object); + } + + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs index eb514d979b..c5a3b0de51 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncContainerExecutorTests.cs @@ -49,6 +49,11 @@ public async Task RetryOnSplit() Mock mockContainer = new Mock(); mockContainer.Setup(x => x.LinkUri).Returns(link); mockContainer.Setup(x => x.GetPartitionKeyDefinitionAsync(It.IsAny())).Returns(Task.FromResult(new PartitionKeyDefinition() { Paths = new Collection() { "/id" } })); + mockContainer.Setup(c => c.GetRIDAsync(It.IsAny())).ReturnsAsync(Guid.NewGuid().ToString()); + Mock context = new Mock(); + mockContainer.Setup(c => c.ClientContext).Returns(context.Object); + context.Setup(c => c.DocumentClient).Returns(new ClientWithSplitDetection()); + CollectionRoutingMap routingMap = CollectionRoutingMap.TryCreateCompleteRoutingMap( new[] @@ -440,5 +445,28 @@ private class MyDocument public bool Updated { get; set; } } + + private class ClientWithSplitDetection : MockDocumentClient + { + private readonly Mock partitionKeyRangeCache; + + public ClientWithSplitDetection() + { + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null); + this.partitionKeyRangeCache.Setup( + m => m.TryGetOverlappingRangesAsync( + It.IsAny(), + It.IsAny>(), + It.Is(b => b == true) // Mocking only the refresh, if it doesn't get called, the test fails + ) + ).Returns((string collectionRid, Documents.Routing.Range range, bool forceRefresh) => Task.FromResult>(this.ResolveOverlapingPartitionKeyRanges(collectionRid, range, forceRefresh))); + } + + internal override Task GetPartitionKeyRangeCacheAsync() + { + return Task.FromResult(this.partitionKeyRangeCache.Object); + } + + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs index 3c39194076..f631a052c2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Batch/BatchAsyncOperationContextTests.cs @@ -5,11 +5,14 @@ namespace Microsoft.Azure.Cosmos.Tests { using System; + using System.Collections.Generic; using System.Net; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; [TestClass] public class BatchAsyncOperationContextTests @@ -84,7 +87,7 @@ public async Task ShouldRetry_NoPolicy() TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.OK); ItemBatchOperation operation = new ItemBatchOperation(OperationType.Create, 0, Cosmos.PartitionKey.Null); operation.AttachContext(new ItemBatchOperationContext(string.Empty)); - ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default(CancellationToken)); + ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default); Assert.IsFalse(shouldRetryResult.ShouldRetry); } @@ -92,11 +95,12 @@ public async Task ShouldRetry_NoPolicy() public async Task ShouldRetry_WithPolicy_OnSuccess() { IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + Mock.Of(), new ResourceThrottleRetryPolicy(1)); TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.OK); ItemBatchOperation operation = new ItemBatchOperation(OperationType.Create, 0, Cosmos.PartitionKey.Null); operation.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy)); - ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default(CancellationToken)); + ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default); Assert.IsFalse(shouldRetryResult.ShouldRetry); } @@ -104,11 +108,12 @@ public async Task ShouldRetry_WithPolicy_OnSuccess() public async Task ShouldRetry_WithPolicy_On429() { IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + Mock.Of(), new ResourceThrottleRetryPolicy(1)); TransactionalBatchOperationResult result = new TransactionalBatchOperationResult((HttpStatusCode)StatusCodes.TooManyRequests); ItemBatchOperation operation = new ItemBatchOperation(OperationType.Create, 0, Cosmos.PartitionKey.Null); operation.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy)); - ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default(CancellationToken)); + ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default); Assert.IsTrue(shouldRetryResult.ShouldRetry); } @@ -116,12 +121,72 @@ public async Task ShouldRetry_WithPolicy_On429() public async Task ShouldRetry_WithPolicy_OnSplit() { IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), new ResourceThrottleRetryPolicy(1)); TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.Gone) { SubStatusCode = SubStatusCodes.PartitionKeyRangeGone }; ItemBatchOperation operation = new ItemBatchOperation(OperationType.Create, 0, Cosmos.PartitionKey.Null); operation.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy)); - ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default(CancellationToken)); + ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default); Assert.IsTrue(shouldRetryResult.ShouldRetry); } + + [TestMethod] + public async Task ShouldRetry_WithPolicy_OnCompletingSplit() + { + IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.Gone) { SubStatusCode = SubStatusCodes.CompletingSplit }; + ItemBatchOperation operation = new ItemBatchOperation(OperationType.Create, 0, Cosmos.PartitionKey.Null); + operation.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy)); + ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default); + Assert.IsTrue(shouldRetryResult.ShouldRetry); + } + + [TestMethod] + public async Task ShouldRetry_WithPolicy_OnCompletingPartitionMigration() + { + IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.Gone) { SubStatusCode = SubStatusCodes.CompletingPartitionMigration }; + ItemBatchOperation operation = new ItemBatchOperation(OperationType.Create, 0, Cosmos.PartitionKey.Null); + operation.AttachContext(new ItemBatchOperationContext(string.Empty, retryPolicy)); + ShouldRetryResult shouldRetryResult = await operation.Context.ShouldRetryAsync(result, default); + Assert.IsTrue(shouldRetryResult.ShouldRetry); + } + + private static ContainerInternal GetSplitEnabledContainer() + { + Mock container = new Mock(); + container.Setup(c => c.GetRIDAsync(It.IsAny())).ReturnsAsync(Guid.NewGuid().ToString()); + Mock context = new Mock(); + container.Setup(c => c.ClientContext).Returns(context.Object); + context.Setup(c => c.DocumentClient).Returns(new ClientWithSplitDetection()); + return container.Object; + } + + private class ClientWithSplitDetection : MockDocumentClient + { + private readonly Mock partitionKeyRangeCache; + + public ClientWithSplitDetection() + { + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null); + this.partitionKeyRangeCache.Setup( + m => m.TryGetOverlappingRangesAsync( + It.IsAny(), + It.IsAny>(), + It.Is(b => b == true) // Mocking only the refresh, if it doesn't get called, the test fails + ) + ).Returns((string collectionRid, Documents.Routing.Range range, bool forceRefresh) => Task.FromResult>(this.ResolveOverlapingPartitionKeyRanges(collectionRid, range, forceRefresh))); + } + + internal override Task GetPartitionKeyRangeCacheAsync() + { + return Task.FromResult(this.partitionKeyRangeCache.Object); + } + + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs index e758ac957f..aeb295c7de 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BulkPartitionKeyRangeGoneRetryPolicyTests.cs @@ -4,11 +4,15 @@ namespace Microsoft.Azure.Cosmos.Tests { + using System; + using System.Collections.Generic; using System.Net; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Routing; using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; [TestClass] public class BulkPartitionKeyRangeGoneRetryPolicyTests @@ -17,10 +21,11 @@ public class BulkPartitionKeyRangeGoneRetryPolicyTests public async Task NotRetryOnSuccess() { IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + Mock.Of(), new ResourceThrottleRetryPolicy(1)); TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.OK); - ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default(CancellationToken)); + ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default); Assert.IsFalse(shouldRetryResult.ShouldRetry); } @@ -28,10 +33,11 @@ public async Task NotRetryOnSuccess() public async Task RetriesOn429() { IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + Mock.Of(), new ResourceThrottleRetryPolicy(1)); TransactionalBatchOperationResult result = new TransactionalBatchOperationResult((HttpStatusCode)StatusCodes.TooManyRequests); - ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default(CancellationToken)); + ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default); Assert.IsTrue(shouldRetryResult.ShouldRetry); } @@ -39,11 +45,69 @@ public async Task RetriesOn429() public async Task RetriesOnSplits() { IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), new ResourceThrottleRetryPolicy(1)); TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.Gone) { SubStatusCode = SubStatusCodes.PartitionKeyRangeGone }; - ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default(CancellationToken)); + ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default); Assert.IsTrue(shouldRetryResult.ShouldRetry); } + + [TestMethod] + public async Task RetriesOnCompletingSplits() + { + IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + + TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.Gone) { SubStatusCode = SubStatusCodes.CompletingSplit }; + ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default); + Assert.IsTrue(shouldRetryResult.ShouldRetry); + } + + [TestMethod] + public async Task RetriesOnCompletingPartitionMigrationSplits() + { + IDocumentClientRetryPolicy retryPolicy = new BulkPartitionKeyRangeGoneRetryPolicy( + GetSplitEnabledContainer(), + new ResourceThrottleRetryPolicy(1)); + + TransactionalBatchOperationResult result = new TransactionalBatchOperationResult(HttpStatusCode.Gone) { SubStatusCode = SubStatusCodes.CompletingPartitionMigration }; + ShouldRetryResult shouldRetryResult = await retryPolicy.ShouldRetryAsync(result.ToResponseMessage(), default); + Assert.IsTrue(shouldRetryResult.ShouldRetry); + } + + private static ContainerInternal GetSplitEnabledContainer() + { + Mock container = new Mock(); + container.Setup(c => c.GetRIDAsync(It.IsAny())).ReturnsAsync(Guid.NewGuid().ToString()); + Mock context = new Mock(); + container.Setup(c => c.ClientContext).Returns(context.Object); + context.Setup(c => c.DocumentClient).Returns(new ClientWithSplitDetection()); + return container.Object; + } + + private class ClientWithSplitDetection : MockDocumentClient + { + private readonly Mock partitionKeyRangeCache; + + public ClientWithSplitDetection() + { + this.partitionKeyRangeCache = new Mock(MockBehavior.Strict, null, null, null); + this.partitionKeyRangeCache.Setup( + m => m.TryGetOverlappingRangesAsync( + It.IsAny(), + It.IsAny>(), + It.Is(b => b == true) // Mocking only the refresh, if it doesn't get called, the test fails + ) + ).Returns((string collectionRid, Documents.Routing.Range range, bool forceRefresh) => Task.FromResult>(this.ResolveOverlapingPartitionKeyRanges(collectionRid, range, forceRefresh))); + } + + internal override Task GetPartitionKeyRangeCacheAsync() + { + return Task.FromResult(this.partitionKeyRangeCache.Object); + } + + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs index 212cc1c62b..ad5de0a196 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosDiagnosticsUnitTests.cs @@ -13,7 +13,9 @@ namespace Microsoft.Azure.Cosmos.Tests using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Cosmos.Handlers; using Microsoft.Azure.Documents; + using Microsoft.Azure.Documents.Collections; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Newtonsoft.Json.Linq; @@ -41,10 +43,47 @@ public void ValidateActivityScope() Trace.CorrelationManager.ActivityId = previousActivityId; } + [TestMethod] + public void ValidateTransportHandlerLogging() + { + DocumentClientException dce = new DocumentClientException( + "test", + null, + new StoreResponseNameValueCollection(), + HttpStatusCode.Gone, + SubStatusCodes.PartitionKeyRangeGone, + new Uri("htts://localhost.com")); + + CosmosDiagnosticsContext diagnosticsContext = new CosmosDiagnosticsContextCore(); + + RequestMessage requestMessage = new RequestMessage( + HttpMethod.Get, + "/dbs/test/colls/abc/docs/123", + diagnosticsContext); + + ResponseMessage response = dce.ToCosmosResponseMessage(requestMessage); + + Assert.AreEqual(HttpStatusCode.Gone, response.StatusCode); + Assert.AreEqual(SubStatusCodes.PartitionKeyRangeGone, response.Headers.SubStatusCode); + + bool visited = false; + foreach (CosmosDiagnosticsInternal cosmosDiagnosticsInternal in diagnosticsContext) + { + if (cosmosDiagnosticsInternal is PointOperationStatistics operationStatistics) + { + visited = true; + Assert.AreEqual(operationStatistics.StatusCode, HttpStatusCode.Gone); + Assert.AreEqual(operationStatistics.SubStatusCode, SubStatusCodes.PartitionKeyRangeGone); + } + } + + Assert.IsTrue(visited, "PointOperationStatistics was not found in the diagnostics."); + } + [TestMethod] public async Task ValidateActivityId() { - using CosmosClient cosmosClient = MockCosmosUtil.CreateMockCosmosClient(); + using CosmosClient cosmosClient = MockCosmosUtil.CreateMockCosmosClient(); CosmosClientContext clientContext = ClientContextCore.Create( cosmosClient, new MockDocumentClient(), @@ -157,7 +196,7 @@ public void ValidateDiagnosticsContext() string result = cosmosDiagnostics.ToString(); - string regex = @"\{""DiagnosticVersion"":""2"",""Summary"":\{""StartUtc"":"".+Z"",""TotalElapsedTimeInMs"":.+,""UserAgent"":""MyCustomUserAgentString"",""TotalRequestCount"":2,""FailedRequestCount"":1\},""Context"":\[\{""Id"":""ValidateScope"",""ElapsedTimeInMs"":.+\},\{""Id"":""PointOperationStatistics"",""ActivityId"":""692ab2f2-41ba-486b-aad7-8c7c6c52379f"",""ResponseTimeUtc"":"".+Z"",""StatusCode"":429,""SubStatusCode"":0,""RequestCharge"":42.0,""RequestUri"":""http://MockUri.com"",""RequestSessionToken"":null,""ResponseSessionToken"":null\},\{""Id"":""SuccessScope"",""ElapsedTimeInMs"":.+\},\{""Id"":""PointOperationStatistics"",""ActivityId"":""de09baab-71a4-4897-a163-470711c93ed3"",""ResponseTimeUtc"":"".+Z"",""StatusCode"":200,""SubStatusCode"":0,""RequestCharge"":42.0,""RequestUri"":""http://MockUri.com"",""RequestSessionToken"":null,""ResponseSessionToken"":null\}\]\}"; + string regex = @"\{""DiagnosticVersion"":""2"",""Summary"":\{""StartUtc"":"".+Z"",""TotalElapsedTimeInMs"":.+,""UserAgent"":""MyCustomUserAgentString"",""TotalRequestCount"":2,""FailedRequestCount"":1,""Operation"":""ValidateDiagnosticsContext""\},""Context"":\[\{""Id"":""ValidateScope"",""ElapsedTimeInMs"":.+\},\{""Id"":""PointOperationStatistics"",""ActivityId"":""692ab2f2-41ba-486b-aad7-8c7c6c52379f"",""ResponseTimeUtc"":"".+Z"",""StatusCode"":429,""SubStatusCode"":0,""RequestCharge"":42.0,""RequestUri"":""http://MockUri.com"",""RequestSessionToken"":null,""ResponseSessionToken"":null\},\{""Id"":""SuccessScope"",""ElapsedTimeInMs"":.+\},\{""Id"":""PointOperationStatistics"",""ActivityId"":""de09baab-71a4-4897-a163-470711c93ed3"",""ResponseTimeUtc"":"".+Z"",""StatusCode"":200,""SubStatusCode"":0,""RequestCharge"":42.0,""RequestUri"":""http://MockUri.com"",""RequestSessionToken"":null,""ResponseSessionToken"":null\}\]\}"; Assert.IsTrue(Regex.IsMatch(result, regex), $"regex: {regex} result: {result}"); JToken jToken = JToken.Parse(result); diff --git a/changelog.md b/changelog.md index 05b1d67867..4b131e48e6 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,14 @@ Preview features are treated as a separate branch and will not be included in th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +### [3.15.1](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.15.1) - 2020-12-16 + +#### Fixed +- [#2069](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/2069) Bulk: Fixes incorrect routing on split +- [#2047](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/2047) Diagnostics: Adds operation name to summary +- [#2042](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/2042) Change Feed Processor: Fixes StartTime not being correctly applied +- [#2071](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/2071) Diagnostics: Fixes substatuscode when recording internal DocumentClientException + ### [3.15.0](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.15.0) - 2020-11-17 #### Added