public void ValidateStoreResultSerialization() { HashSet <string> storeResultProperties = typeof(StoreResult).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(x => x.Name).ToHashSet <string>(); string datumKey = "ClientStats"; Trace trace = Trace.GetRootTrace("Test"); ClientSideRequestStatisticsTraceDatum datum = new ClientSideRequestStatisticsTraceDatum(DateTime.UtcNow); trace.AddDatum(datumKey, datum); StoreResult storeResult = new StoreResult( storeResponse: new StoreResponse(), exception: null, partitionKeyRangeId: 42.ToString(), lsn: 1337, quorumAckedLsn: 23, requestCharge: 3.14, currentReplicaSetSize: 4, currentWriteQuorum: 3, isValid: true, storePhysicalAddress: new Uri("http://storephysicaladdress.com"), globalCommittedLSN: 1234, numberOfReadRegions: 13, itemLSN: 15, sessionToken: new SimpleSessionToken(42), usingLocalLSN: true, activityId: Guid.Empty.ToString(), backendRequestDurationInMs: "4.2", retryAfterInMs: "42", transportRequestStats: TraceWriterBaselineTests.CreateTransportRequestStats()); StoreResponseStatistics storeResponseStatistics = new StoreResponseStatistics( DateTime.MinValue, DateTime.MaxValue, storeResult, ResourceType.Document, OperationType.Query, new Uri("http://someUri1.com")); ((List <StoreResponseStatistics>)datum.GetType().GetField("storeResponseStatistics", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(datum)).Add(storeResponseStatistics); CosmosTraceDiagnostics diagnostics = new CosmosTraceDiagnostics(trace); string json = diagnostics.ToString(); JObject jObject = JObject.Parse(json); JObject storeResultJObject = jObject["data"][datumKey]["StoreResponseStatistics"][0]["StoreResult"].ToObject <JObject>(); List <string> jsonPropertyNames = storeResultJObject.Properties().Select(p => p.Name).ToList(); storeResultProperties.Add("BELatencyInMs"); storeResultProperties.Remove(nameof(storeResult.BackendRequestDurationInMs)); storeResultProperties.Add("TransportException"); storeResultProperties.Remove(nameof(storeResult.Exception)); storeResultProperties.Add("transportRequestTimeline"); storeResultProperties.Remove(nameof(storeResult.TransportRequestStats)); foreach (string key in jsonPropertyNames) { Assert.IsTrue(storeResultProperties.Remove(key), $"Json contains key:{key} not a storeresult property"); } Assert.AreEqual(0, storeResultProperties.Count, $"Json is missing properties: {string.Join(';', storeResultProperties)}"); }
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 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."); }