public async Task CreateDropItemTest()
        {
            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);
            long?lsnAfterCreate = await GetLSNFromSessionContainer(
                this.Container, this.containerSettings, new PartitionKey(testItem.pk));

            Assert.IsNotNull(lsnAfterCreate);
            CosmosTraceDiagnostics diagnostics = (CosmosTraceDiagnostics)response.Diagnostics;

            Assert.IsFalse(diagnostics.IsGoneExceptionHit());
            Assert.IsFalse(string.IsNullOrEmpty(diagnostics.ToString()));
            Assert.IsTrue(diagnostics.GetClientElapsedTime() > TimeSpan.Zero);

            response = await this.Container.ReadItemAsync <ToDoActivity>(testItem.id, new Cosmos.PartitionKey(testItem.pk));

            Assert.IsNotNull(response);
            Assert.IsNotNull(response.Resource);
            Assert.IsNotNull(response.Diagnostics);
            Assert.IsFalse(string.IsNullOrEmpty(response.Diagnostics.ToString()));
            Assert.IsTrue(response.Diagnostics.GetClientElapsedTime() > TimeSpan.Zero);
            long?lsnAfterRead = await GetLSNFromSessionContainer(
                this.Container, this.containerSettings, new PartitionKey(testItem.pk));

            Assert.IsNotNull(lsnAfterRead);
            Assert.AreEqual(lsnAfterCreate.Value, lsnAfterRead.Value);

            Assert.IsNotNull(response.Headers.GetHeaderValue <string>(Documents.HttpConstants.HttpHeaders.MaxResourceQuota));
            Assert.IsNotNull(response.Headers.GetHeaderValue <string>(Documents.HttpConstants.HttpHeaders.CurrentResourceQuotaUsage));
            ItemResponse <ToDoActivity> deleteResponse = await this.Container.DeleteItemAsync <ToDoActivity>(partitionKey : new Cosmos.PartitionKey(testItem.pk), id : testItem.id);

            Assert.IsNotNull(deleteResponse);
            Assert.IsNotNull(response.Diagnostics);
            Assert.IsFalse(string.IsNullOrEmpty(response.Diagnostics.ToString()));
            Assert.IsTrue(response.Diagnostics.GetClientElapsedTime() > TimeSpan.Zero);
            long?lsnAfterDelete = await GetLSNFromSessionContainer(
                this.Container, this.containerSettings, new PartitionKey(testItem.pk));

            Assert.IsNotNull(lsnAfterDelete);
            Assert.IsTrue(lsnAfterDelete.Value > lsnAfterCreate.Value);
        }
示例#2
0
        public static void LogDiagnostics(
            ILogger logger,
            string operationName,
            TimeSpan timerContextLatency,
            CTLConfig config,
            CosmosDiagnostics cosmosDiagnostics)
        {
            if (timerContextLatency > config.DiagnosticsThresholdDurationAsTimespan)
            {
                logger.LogInformation($"{operationName}; LatencyInMs:{timerContextLatency.TotalMilliseconds}; request took more than latency threshold {config.DiagnosticsThresholdDuration}, diagnostics: {cosmosDiagnostics}");
            }

            CosmosTraceDiagnostics traceDiagnostics = (CosmosTraceDiagnostics)cosmosDiagnostics;

            if (traceDiagnostics.IsGoneExceptionHit())
            {
                logger.LogInformation($"{operationName}; LatencyInMs:{timerContextLatency.TotalMilliseconds}; request contains 410(GoneExceptions), diagnostics:{cosmosDiagnostics}");
                return;
            }
        }
        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.");
        }