public async Task NoSessionTokenCaptureForThrottledUpsertRequestsTest()
        {
            ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity();

            ResetSessionToken(this.Container);
            Assert.IsNull(await GetLSNFromSessionContainer(
                              this.Container, this.containerSettings, new PartitionKey(testItem.pk)));

            ItemResponse <ToDoActivity> response = await this.Container.CreateItemAsync <ToDoActivity>(item : testItem);

            Assert.IsNotNull(response);
            Assert.IsNotNull(response.Resource);
            Assert.IsNotNull(response.Diagnostics);
            string sessionTokenHeaderValue = response.Headers[HttpConstants.HttpHeaders.SessionToken];

            Assert.IsNotNull(sessionTokenHeaderValue);

            long?lsnAfterCreate = await GetLSNFromSessionContainer(
                this.Container, this.containerSettings, new PartitionKey(testItem.pk));

            Assert.IsNotNull(lsnAfterCreate);

            ResetSessionToken(this.Container);
            Assert.IsNull(await GetLSNFromSessionContainer(
                              this.Container, this.containerSettings, new PartitionKey(testItem.pk)));

            Container throttledContainer = TransportClientHelper.GetContainerWithIntercepter(
                this.database.Id,
                this.Container.Id,
                (uri, resourceOperation, documentServiceRequest) => { },
                useGatewayMode: false,
                (uri, resourceOperation, documentServiceRequest) =>
            {
                StoreResponse throttledResponse = TransportClientHelper.ReturnThrottledStoreResponseOnItemOperation(
                    uri, resourceOperation, documentServiceRequest, Guid.NewGuid(), string.Empty);

                throttledResponse.Headers.Add(HttpConstants.HttpHeaders.SessionToken, sessionTokenHeaderValue);

                return(throttledResponse);
            },
                this.cosmosClient.DocumentClient.sessionContainer);

            try
            {
                //Updated the taskNum field
                testItem.taskNum = 9001;
                response         = await throttledContainer.UpsertItemAsync(testItem, partitionKey : new Cosmos.PartitionKey(testItem.pk));
            }
            catch (CosmosException cosmosException)
            {
                Assert.AreEqual(HttpStatusCode.TooManyRequests, cosmosException.StatusCode);
            }

            long?lsnAfterThrottledRequest = await GetLSNFromSessionContainer(
                this.Container, this.containerSettings, new PartitionKey(testItem.pk));

            Assert.IsNull(lsnAfterThrottledRequest);
        }
        public async Task PointOperationRequestTimeoutDiagnostic(bool disableDiagnostics)
        {
            ItemRequestOptions requestOptions = new ItemRequestOptions();

            if (disableDiagnostics)
            {
                requestOptions.DiagnosticContextFactory = () => EmptyCosmosDiagnosticsContext.Singleton;
            }
            ;

            Guid      exceptionActivityId             = Guid.NewGuid();
            string    transportExceptionDescription   = "transportExceptionDescription" + Guid.NewGuid();
            Container containerWithTransportException = TransportClientHelper.GetContainerWithItemTransportException(
                this.database.Id,
                this.Container.Id,
                exceptionActivityId,
                transportExceptionDescription);

            //Checking point operation diagnostics on typed operations
            ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity();

            try
            {
                ItemResponse <ToDoActivity> createResponse = await containerWithTransportException.CreateItemAsync <ToDoActivity>(
                    item : testItem,
                    requestOptions : requestOptions);

                Assert.Fail("Should have thrown a request timeout exception");
            }
            catch (CosmosException ce) when(ce.StatusCode == System.Net.HttpStatusCode.RequestTimeout)
            {
                string exception = ce.ToString();

                Assert.IsNotNull(exception);
                Assert.IsTrue(exception.Contains(exceptionActivityId.ToString()));
                Assert.IsTrue(exception.Contains(transportExceptionDescription));

                string diagnosics = ce.Diagnostics.ToString();

                if (disableDiagnostics)
                {
                    Assert.IsTrue(string.IsNullOrEmpty(diagnosics));
                }
                else
                {
                    Assert.IsFalse(string.IsNullOrEmpty(diagnosics));
                    Assert.IsTrue(exception.Contains(diagnosics));
                    DiagnosticValidator.ValidatePointOperationDiagnostics(ce.DiagnosticsContext);
                }
            }
        }
 internal static Container GetContainerWithItemTransportException(
     string databaseId,
     string containerId,
     Guid activityId,
     string transportExceptionSourceDescription)
 {
     return(GetContainerWithIntercepter(
                databaseId,
                containerId,
                (uri, resourceOperation, request) => TransportClientHelper.ThrowTransportExceptionOnItemOperation(
                    uri,
                    resourceOperation,
                    request,
                    activityId,
                    transportExceptionSourceDescription)));
 }
Example #4
0
        public async Task CheckCancellationWithTransportIntercepterTestAsync()
        {
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            Container withCancellationToken = TransportClientHelper.GetContainerWithIntercepter(
                this.database.Id,
                this.Container.Id,
                (uri, resourceOperation, documentServiceRequest) =>
            {
                if (documentServiceRequest.ResourceType == Documents.ResourceType.Document)
                {
                    cancellationTokenSource.Cancel();
                    cancellationTokenSource.Token.ThrowIfCancellationRequested();
                }
            });

            await this.CheckCancellationTokenTestAsync(withCancellationToken, cancellationTokenSource.Token);
        }
Example #5
0
        public async Task CheckCancellationWithTransportIntercepterTestAsync()
        {
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            Container withCancellationToken = TransportClientHelper.GetContainerWithIntercepter(
                this.database.Id,
                this.Container.Id,
                (uri, resourceOperation, documentServiceRequest) =>
            {
                if (documentServiceRequest.ResourceType == Documents.ResourceType.Document)
                {
                    cancellationTokenSource.Cancel();
                }
            },
                useGatewayMode: false,
                (uri, resourceOperation, documentServiceRequest)
                => TransportClientHelper.ReturnThrottledStoreResponseOnItemOperation(uri, resourceOperation, documentServiceRequest, Guid.NewGuid(), string.Empty));

            await this.CheckCancellationTokenTestAsync(withCancellationToken, cancellationTokenSource.Token);
        }
Example #6
0
        internal static Container GetContainerWithItemTransportException(
            string databaseId,
            string containerId,
            Guid activityId,
            string transportExceptionSourceDescription)
        {
            CosmosClient clientWithIntercepter = TestCommon.CreateCosmosClient(
                builder =>
            {
                builder.WithTransportClientHandlerFactory(transportClient => new TransportClientWrapper(
                                                              transportClient,
                                                              (uri, resourceOperation, request) => TransportClientHelper.ThrowTransportExceptionOnItemOperation(
                                                                  uri,
                                                                  resourceOperation,
                                                                  request,
                                                                  activityId,
                                                                  transportExceptionSourceDescription)));
            });

            return(clientWithIntercepter.GetContainer(databaseId, containerId));
        }
        public async Task PointOperationForbiddenDiagnostic()
        {
            List <int> stringLength = new List <int>();

            foreach (int maxCount in new int[] { 1, 2, 4 })
            {
                int count = 0;
                List <(string, string)> activityIdAndErrorMessage = new List <(string, string)>(maxCount);
                Guid   transportExceptionActivityId = Guid.NewGuid();
                string transportErrorMessage        = $"TransportErrorMessage{Guid.NewGuid()}";
                Guid   activityIdScope = Guid.Empty;
                Action <Uri, Documents.ResourceOperation, Documents.DocumentServiceRequest> interceptor =
                    (uri, operation, request) =>
                {
                    Assert.AreNotEqual(Trace.CorrelationManager.ActivityId, Guid.Empty, "Activity scope should be set");

                    if (request.ResourceType == Documents.ResourceType.Document)
                    {
                        if (activityIdScope == Guid.Empty)
                        {
                            activityIdScope = Trace.CorrelationManager.ActivityId;
                        }
                        else
                        {
                            Assert.AreEqual(Trace.CorrelationManager.ActivityId, activityIdScope, "Activity scope should match on retries");
                        }

                        if (count >= maxCount)
                        {
                            TransportClientHelper.ThrowTransportExceptionOnItemOperation(
                                uri,
                                operation,
                                request,
                                transportExceptionActivityId,
                                transportErrorMessage);
                        }

                        count++;
                        string activityId   = Guid.NewGuid().ToString();
                        string errorMessage = $"Error{Guid.NewGuid()}";

                        activityIdAndErrorMessage.Add((activityId, errorMessage));
                        TransportClientHelper.ThrowForbiddendExceptionOnItemOperation(
                            uri,
                            request,
                            activityId,
                            errorMessage);
                    }
                };

                Container containerWithTransportException = TransportClientHelper.GetContainerWithIntercepter(
                    this.database.Id,
                    this.Container.Id,
                    interceptor);
                //Checking point operation diagnostics on typed operations
                ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity();

                try
                {
                    ItemResponse <ToDoActivity> createResponse = await containerWithTransportException.CreateItemAsync <ToDoActivity>(
                        item : testItem);

                    Assert.Fail("Should have thrown a request timeout exception");
                }
                catch (CosmosException ce) when(ce.StatusCode == System.Net.HttpStatusCode.RequestTimeout)
                {
                    string exceptionToString = ce.ToString();

                    Assert.IsNotNull(exceptionToString);
                    stringLength.Add(exceptionToString.Length);

                    // Request timeout info will be in the exception message and in the diagnostic info.
                    Assert.AreEqual(2, Regex.Matches(exceptionToString, transportExceptionActivityId.ToString()).Count);
                    Assert.AreEqual(2, Regex.Matches(exceptionToString, transportErrorMessage).Count);

                    // Check to make sure the diagnostics does not include duplicate info.
                    foreach ((string activityId, string errorMessage) in activityIdAndErrorMessage)
                    {
                        Assert.AreEqual(1, Regex.Matches(exceptionToString, activityId).Count);
                        Assert.AreEqual(1, Regex.Matches(exceptionToString, errorMessage).Count);
                    }
                }
            }

            // Check if the exception message is not growing exponentially
            Assert.IsTrue(stringLength.Count > 2);
            for (int i = 0; i < stringLength.Count - 1; i++)
            {
                int currLength = stringLength[i];
                int nextLength = stringLength[i + 1];
                Assert.IsTrue(nextLength < currLength * 2,
                              $"The diagnostic string is growing faster than linear. Length: {currLength}, Next Length: {nextLength}");
            }
        }
        public async Task PointOperationThrottledDiagnostic(bool disableDiagnostics)
        {
            string errorMessage        = "Mock throttle exception" + Guid.NewGuid().ToString();
            Guid   exceptionActivityId = Guid.NewGuid();
            // Set a small retry count to reduce test time
            CosmosClient throttleClient = TestCommon.CreateCosmosClient(builder =>
                                                                        builder.WithThrottlingRetryOptions(TimeSpan.FromSeconds(5), 5)
                                                                        .WithTransportClientHandlerFactory(transportClient => new TransportClientWrapper(
                                                                                                               transportClient,
                                                                                                               (uri, resourceOperation, request) => TransportClientHelper.ReturnThrottledStoreResponseOnItemOperation(
                                                                                                                   uri,
                                                                                                                   resourceOperation,
                                                                                                                   request,
                                                                                                                   exceptionActivityId,
                                                                                                                   errorMessage)))
                                                                        );

            ItemRequestOptions requestOptions = new ItemRequestOptions();

            if (disableDiagnostics)
            {
                requestOptions.DiagnosticContextFactory = () => EmptyCosmosDiagnosticsContext.Singleton;
            }
            ;

            Container containerWithThrottleException = throttleClient.GetContainer(
                this.database.Id,
                this.Container.Id);

            //Checking point operation diagnostics on typed operations
            ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity();

            try
            {
                ItemResponse <ToDoActivity> createResponse = await containerWithThrottleException.CreateItemAsync <ToDoActivity>(
                    item : testItem,
                    requestOptions : requestOptions);

                Assert.Fail("Should have thrown a request timeout exception");
            }
            catch (CosmosException ce) when((int)ce.StatusCode == (int)Documents.StatusCodes.TooManyRequests)
            {
                string exception = ce.ToString();

                Assert.IsNotNull(exception);
                Assert.IsTrue(exception.Contains(exceptionActivityId.ToString()));
                Assert.IsTrue(exception.Contains(errorMessage));

                string diagnosics = ce.Diagnostics.ToString();

                if (disableDiagnostics)
                {
                    Assert.IsTrue(string.IsNullOrEmpty(diagnosics));
                }
                else
                {
                    Assert.IsFalse(string.IsNullOrEmpty(diagnosics));
                    Assert.IsTrue(exception.Contains(diagnosics));
                    DiagnosticValidator.ValidatePointOperationDiagnostics(ce.DiagnosticsContext);
                }
            }
        }
        public async Task InvalidSessionTokenAfterContainerRecreationAndCollectionCacheRefreshReproTest()
        {
            // ingestionClinet is dedicated client simulating the writes / container recreation in
            // the separate process - like Spark job
            using CosmosClient ingestionClient = TestCommon.CreateCosmosClient();
            Cosmos.Database ingestionDatabase = ingestionClient.GetDatabase(this.database.Id);

            ContainerProperties multiPartitionContainerSettings =
                new ContainerProperties(id: Guid.NewGuid().ToString(), partitionKeyPath: "/pk");
            Container ingestionContainer =
                await ingestionDatabase.CreateContainerAsync(multiPartitionContainerSettings);

            const int itemCountToBeIngested = 10;
            string    pk        = Guid.NewGuid().ToString("N");
            long?     latestLsn = null;

            Console.WriteLine("INGEST DOCUMENTS");
            for (int i = 0; i < itemCountToBeIngested; i++)
            {
                ToDoActivity testItem = ToDoActivity.CreateRandomToDoActivity();
                testItem.pk = pk;

                ItemResponse <ToDoActivity> response =
                    await ingestionContainer.CreateItemAsync <ToDoActivity>(item : testItem);

                Assert.IsNotNull(response);
                Assert.IsNotNull(response.Resource);
                Assert.IsNotNull(response.Diagnostics);
                long?lsnAfterCreate = await GetLSNFromSessionContainer(
                    ingestionContainer, multiPartitionContainerSettings, new PartitionKey(pk));

                Assert.IsNotNull(lsnAfterCreate);
                Assert.IsTrue(latestLsn == null || lsnAfterCreate.Value > latestLsn.Value);
                latestLsn = lsnAfterCreate;
                CosmosTraceDiagnostics diagnostics = (CosmosTraceDiagnostics)response.Diagnostics;
                Assert.IsFalse(diagnostics.IsGoneExceptionHit());
                Assert.IsFalse(string.IsNullOrEmpty(diagnostics.ToString()));
                Assert.IsTrue(diagnostics.GetClientElapsedTime() > TimeSpan.Zero);
            }

            // Dedciated query client used only for queries simulating the customer's app
            string    lastRequestedSessionToken = null;
            Container queryContainer            = TransportClientHelper.GetContainerWithIntercepter(
                this.database.Id,
                ingestionContainer.Id,
                (uri, operation, request) =>
            {
                if (request.ResourceType == ResourceType.Document &&
                    request.OperationType == OperationType.Query)
                {
                    lastRequestedSessionToken = request.Headers[HttpConstants.HttpHeaders.SessionToken];
                }
            },
                false,
                null);

            long?lsnAfterQueryOnOldContainer = null;

            // Issueing two queries - first won't use session tokens yet
            // second will provide session tokens captured from first request in the request to the backend
            for (int i = 0; i < 2; i++)
            {
                Console.WriteLine("RUN QUERY ON OLD CONTAINER ({0})", i);
                using FeedIterator <JObject> queryIteratorOldContainer = queryContainer.GetItemQueryIterator <JObject>(
                          new QueryDefinition("Select c.id FROM c"),
                          continuationToken: null,
                          new QueryRequestOptions
                {
                    ConsistencyLevel = Cosmos.ConsistencyLevel.Session,
                    PartitionKey     = new Cosmos.PartitionKey(pk)
                });
                int itemCountOldContainer = 0;
                while (queryIteratorOldContainer.HasMoreResults)
                {
                    FeedResponse <JObject> response = await queryIteratorOldContainer.ReadNextAsync();

                    if (i == 0)
                    {
                        string diagnosticString = response.Diagnostics.ToString();
                        Assert.IsTrue(diagnosticString.Contains("PKRangeCache Info("));
                        JObject diagnosticJobject = JObject.Parse(diagnosticString);
                        JToken  actualToken       = diagnosticJobject.SelectToken("$.children[0].children[?(@.name=='Get Partition Key Ranges')].children[?(@.name=='Try Get Overlapping Ranges')].data");
                        JToken  actualNode        = actualToken.Children().First().First();

                        Assert.IsTrue(actualNode["Previous Continuation Token"].ToString().Length == 0);
                        Assert.IsTrue(actualNode["Continuation Token"].ToString().Length > 0);
                    }

                    itemCountOldContainer += response.Count;
                }

                Assert.AreEqual(itemCountToBeIngested, itemCountOldContainer);
                lsnAfterQueryOnOldContainer = await GetLSNFromSessionContainer(
                    queryContainer, multiPartitionContainerSettings, new PartitionKey(pk));

                Assert.IsNotNull(lsnAfterQueryOnOldContainer);
                Assert.AreEqual(latestLsn.Value, lsnAfterQueryOnOldContainer.Value);
                if (i == 0)
                {
                    Assert.IsNull(lastRequestedSessionToken);
                }
                else
                {
                    Assert.IsNotNull(lastRequestedSessionToken);
                    Assert.AreEqual(latestLsn.Value, SessionTokenHelper.Parse(lastRequestedSessionToken).LSN);
                }
            }

            Console.WriteLine(
                "DELETE CONTAINER {0}",
                (await queryContainer.ReadContainerAsync()).Resource.ResourceId);
            await ingestionContainer.DeleteContainerAsync();

            Console.WriteLine("RECREATING CONTAINER...");
            ContainerResponse ingestionContainerResponse =
                await ingestionDatabase.CreateContainerAsync(multiPartitionContainerSettings);

            ingestionContainer = ingestionContainerResponse.Container;

            string responseSessionTokenValue =
                ingestionContainerResponse.Headers[HttpConstants.HttpHeaders.SessionToken];
            long?lsnAfterRecreatingContainerFromIngestionClient = responseSessionTokenValue != null?
                                                                  SessionTokenHelper.Parse(responseSessionTokenValue).LSN : null;

            Console.WriteLine(
                "RECREATED CONTAINER with new CollectionRid: {0} - LSN: {1}",
                ingestionContainerResponse.Resource.ResourceId,
                lsnAfterRecreatingContainerFromIngestionClient);

            // validates that the query container still uses the LSN captured from the old LSN
            long?lsnAfterCreatingNewContainerFromQueryClient = await GetLSNFromSessionContainer(
                queryContainer, multiPartitionContainerSettings, new PartitionKey(pk));

            Assert.IsNotNull(lsnAfterCreatingNewContainerFromQueryClient);
            Assert.AreEqual(latestLsn.Value, lsnAfterCreatingNewContainerFromQueryClient.Value);

            Console.WriteLine("GET FEED RANGES");
            // this will force a CollectionCache refresh - because no pk ranegs can be identified
            // for the old container anymore
            _ = await queryContainer.GetFeedRangesAsync();


            Console.WriteLine("RUN QUERY ON NEW CONTAINER");
            int itemCountNewContainer = 0;

            using FeedIterator <JObject> queryIteratorNewContainer = queryContainer.GetItemQueryIterator <JObject>(
                      new QueryDefinition("Select c.id FROM c"),
                      continuationToken: null,
                      new QueryRequestOptions
            {
                ConsistencyLevel = Cosmos.ConsistencyLevel.Session,
                PartitionKey     = new Cosmos.PartitionKey(pk),
            });
            Console.WriteLine("Query iterator created");
            while (queryIteratorNewContainer.HasMoreResults)
            {
                Console.WriteLine("Retrieving first page");
                try
                {
                    FeedResponse <JObject> response = await queryIteratorNewContainer.ReadNextAsync();

                    Console.WriteLine("Request Diagnostics for query against new container: {0}",
                                      response.Diagnostics.ToString());
                    itemCountNewContainer += response.Count;
                }
                catch (CosmosException cosmosException)
                {
                    Console.WriteLine("COSMOS EXCEPTION: {0}", cosmosException);
                    throw;
                }
            }

            Assert.AreEqual(0, itemCountNewContainer);
            long?lsnAfterQueryOnNewContainer = await GetLSNFromSessionContainer(
                queryContainer, multiPartitionContainerSettings, new PartitionKey(pk));

            Assert.IsNotNull(lsnAfterQueryOnNewContainer);
            Assert.IsTrue(
                lastRequestedSessionToken == null ||
                SessionTokenHelper.Parse(lastRequestedSessionToken).LSN ==
                lsnAfterRecreatingContainerFromIngestionClient,
                $"The requested session token {lastRequestedSessionToken} on the last query request should be null " +
                $"or have LSN '{lsnAfterRecreatingContainerFromIngestionClient}' (which is the LSN after " +
                "re-creating the container) if the session cache or the new CollectionName to Rid mapping was " +
                "correctly populated in the SessionCache.");
        }