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); }
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."); }