private static void RenderResult(TransactionalBatchResponse results, int resultCount) { Console.WriteLine(); Console.WriteLine("Result View------------------------------------------"); Console.WriteLine($"Overall Status: {results.IsSuccessStatusCode}"); if (!results.IsSuccessStatusCode) { Console.WriteLine($"Error Message: {results.ErrorMessage}"); } Console.WriteLine($"Status Code: {results.StatusCode}"); Console.WriteLine($"Activity Id: {results.ActivityId}"); for (int i = 0; i < resultCount; i++) { Console.WriteLine($"\tSub Task #{i + 1}------------------------------------------"); var operationResult = results.GetOperationResultAtIndex <dynamic>(i); Console.WriteLine($"\t Status Code: {operationResult.StatusCode}"); Console.WriteLine($"\t ETag: {operationResult.ETag}"); Console.WriteLine($"\t Resource: {operationResult.Resource}"); Console.WriteLine($"\t Status: {operationResult.IsSuccessStatusCode}"); Console.WriteLine($"\tSub Task #{i + 1} End---------------------------------------"); Console.WriteLine(); } Console.WriteLine("Result View End---------------------------------------"); Console.WriteLine("Press Enter to continue..."); Console.ReadLine(); Console.WriteLine(); }
public async Task BatchSingleServerResponseAsync() { List <TransactionalBatchOperationResult> expectedResults = new List <TransactionalBatchOperationResult>(); CosmosJsonDotNetSerializer jsonSerializer = new CosmosJsonDotNetSerializer(); TestItem testItem = new TestItem("tst"); Stream itemStream = jsonSerializer.ToStream <TestItem>(testItem); MemoryStream resourceStream = itemStream as MemoryStream; if (resourceStream == null) { await itemStream.CopyToAsync(resourceStream); resourceStream.Position = 0; } expectedResults.Add( new TransactionalBatchOperationResult(HttpStatusCode.OK) { ETag = "theETag", SubStatusCode = (SubStatusCodes)1100, ResourceStream = resourceStream }); expectedResults.Add(new TransactionalBatchOperationResult(HttpStatusCode.Conflict)); double requestCharge = 3.6; TestHandler testHandler = new TestHandler(async(request, cancellationToken) => { ResponseMessage responseMessage = new ResponseMessage(HttpStatusCode.OK, requestMessage: null, errorMessage: null) { Content = await new BatchResponsePayloadWriter(expectedResults).GeneratePayloadAsync() }; responseMessage.Headers.RequestCharge = requestCharge; return(responseMessage); }); Container container = BatchUnitTests.GetContainer(testHandler); TransactionalBatchResponse batchResponse = await new BatchCore((ContainerInternal)container, new Cosmos.PartitionKey(BatchUnitTests.PartitionKey1)) .ReadItem("id1") .ReadItem("id2") .ExecuteAsync(); Assert.AreEqual(HttpStatusCode.OK, batchResponse.StatusCode); Assert.AreEqual(requestCharge, batchResponse.RequestCharge); TransactionalBatchOperationResult <TestItem> result0 = batchResponse.GetOperationResultAtIndex <TestItem>(0); Assert.AreEqual(expectedResults[0].StatusCode, result0.StatusCode); Assert.AreEqual(expectedResults[0].SubStatusCode, result0.SubStatusCode); Assert.AreEqual(expectedResults[0].ETag, result0.ETag); Assert.AreEqual(testItem, result0.Resource); Assert.AreEqual(expectedResults[1].StatusCode, batchResponse[1].StatusCode); Assert.AreEqual(SubStatusCodes.Unknown, batchResponse[1].SubStatusCode); Assert.IsNull(batchResponse[1].ETag); Assert.IsNull(batchResponse[1].ResourceStream); }
=> string.IsNullOrWhiteSpace(metadata.ETag); // As metadata has already been updated we can only use ETag to see if its a new stream. private static IStreamMetadata GetMetadataFromResponse(TransactionalBatchResponse response) { var result = response.GetOperationResultAtIndex <StreamMetadata>(0); var metadata = result.Resource; metadata.ETag = result.ETag; return(metadata); }
private void ValidateResponse( TransactionalBatchResponse response, int noResponseItemCount) { Assert.IsNotNull(response); Assert.IsTrue(response.IsSuccessStatusCode); Assert.IsTrue(response.RequestCharge > 0); Assert.IsNotNull(response.ActivityId); int count = 0; foreach (TransactionalBatchOperationResult itemResponse in response) { count++; if (count == noResponseItemCount) { break; } Assert.IsTrue(itemResponse.IsSuccessStatusCode); Assert.IsTrue(itemResponse.StatusCode == HttpStatusCode.OK || itemResponse.StatusCode == HttpStatusCode.Created); Assert.IsNull(itemResponse.ResourceStream); Assert.IsTrue(itemResponse.RequestCharge > 0); } for (int i = 0; i < response.Count && i < noResponseItemCount; i++) { TransactionalBatchOperationResult <ToDoActivity> itemResponse = response.GetOperationResultAtIndex <ToDoActivity>(i); Assert.IsNull(itemResponse.Resource); Assert.IsNull(itemResponse.ResourceStream); Assert.IsTrue(response.RequestCharge > 0); Assert.IsNotNull(response.ActivityId); } for (int i = noResponseItemCount; i < response.Count; i++) { TransactionalBatchOperationResult <ToDoActivity> itemResponse = response.GetOperationResultAtIndex <ToDoActivity>(i); Assert.IsNotNull(itemResponse.Resource); Assert.IsNotNull(itemResponse.ResourceStream); Assert.IsTrue(response.RequestCharge > 0); Assert.IsNotNull(response.ActivityId); } }
internal static IEnumerable <T> GetOperationResultResources <T>(this TransactionalBatchResponse batchResponse) { if (!batchResponse.IsSuccessStatusCode) { yield break; } for (int i = 0; i < batchResponse.Count; i++) { var operationResult = batchResponse.GetOperationResultAtIndex <T>(i); yield return(operationResult.IsSuccessStatusCode ? operationResult.Resource : default);
public async Task BatchReadsOnlyAsync() { Container container = BatchTestBase.JsonContainer; await this.CreateJsonTestDocsAsync(container); TransactionalBatchResponse batchResponse = await new BatchCore((ContainerCore)container, BatchTestBase.GetPartitionKey(this.PartitionKey1)) .ReadItem(this.TestDocPk1ExistingA.Id) .ReadItem(this.TestDocPk1ExistingB.Id) .ReadItem(this.TestDocPk1ExistingC.Id) .ExecuteAsync(); BatchSinglePartitionKeyTests.VerifyBatchProcessed(batchResponse, numberOfOperations: 3); Assert.AreEqual(HttpStatusCode.OK, batchResponse[0].StatusCode); Assert.AreEqual(HttpStatusCode.OK, batchResponse[1].StatusCode); Assert.AreEqual(HttpStatusCode.OK, batchResponse[2].StatusCode); Assert.AreEqual(this.TestDocPk1ExistingA, batchResponse.GetOperationResultAtIndex <TestDoc>(0).Resource); Assert.AreEqual(this.TestDocPk1ExistingB, batchResponse.GetOperationResultAtIndex <TestDoc>(1).Resource); Assert.AreEqual(this.TestDocPk1ExistingC, batchResponse.GetOperationResultAtIndex <TestDoc>(2).Resource); }
private async Task AddItemsToContainerAsync() { try { /* Classic Insertion * foreach (var shop in CreateShopList()) * { * var resp = await container.CreateItemAsync<Shop>(shop); * Console.WriteLine($"Created {shop.Name} Client Time: {resp.Diagnostics.GetClientElapsedTime().TotalSeconds} \n"); * } */ // ACID - Limitations (Transaction only scopes a single partition!) // - Payload cannot exceed 2MB as per the Azure Cosmos DB request size limit // - Maximum execution time is 5 seconds // - Limit of 100 operations per TransactionalBatch to make sure the performance is as expected and within SLAs. using (TransactionalBatchResponse batchResponse = await InsertAllDocuments( container.CreateTransactionalBatch(new PartitionKey("Austria"))) .ExecuteAsync()) { if (!batchResponse.IsSuccessStatusCode) { Console.WriteLine("Batch execution failed!\n"); // Handle and log exception } else { // Look up interested results - eg. via typed access on operation results Console.WriteLine($"Transactionbatch completed with a charge of {batchResponse.RequestCharge} RUs."); for (int i = 0; i < batchResponse.Count; i++) { TransactionalBatchOperationResult <Shop> opResult = batchResponse.GetOperationResultAtIndex <Shop>(i); Console.WriteLine($"Shop {opResult.Resource.Name} created with {opResult.ETag} ETAG."); } } } } catch (CosmosException ex) when(ex.StatusCode == HttpStatusCode.Conflict) { Console.WriteLine("Items already existing\n"); } }
private static async Task RunDemoAsync() { Container gamesContainer = Program.database.GetContainer(containerId); // This code demonstrates interactions by a multi-player game service that hosts games with the database to save game state. // In this fictional game, players move about the 10x10 map and try to find balls. // The objective is to collect 2 balls of the same color, or a golden ball if one appears. // After 5 minutes, if the game is not complete, the player with highest number of balls wins. int gameId = 420; int playerCount = 3; int ballCount = 4; List <GameBall> balls = new List <GameBall>(); List <GameParticipant> players = new List <GameParticipant>(); Console.WriteLine("At the start of the game, the balls are added on the map, and the players are added ..."); // The below batch request is used to create the game balls and participants in an atomic fashion. TransactionalBatchResponse gameStartResponse = await gamesContainer.CreateTransactionalBatch(new PartitionKey(gameId)) .CreateItem <GameBall>(GameBall.Create(gameId, Color.Red, 4, 2)) .CreateItem <GameBall>(GameBall.Create(gameId, Color.Blue, 6, 4)) .CreateItem <GameBall>(GameBall.Create(gameId, Color.Blue, 8, 7)) .CreateItem <GameBall>(GameBall.Create(gameId, Color.Red, 8, 8)) .CreateItem <GameParticipant>(GameParticipant.Create(gameId, "alice")) .CreateItem <GameParticipant>(GameParticipant.Create(gameId, "bob")) .CreateItem <GameParticipant>(GameParticipant.Create(gameId, "carla")) .ExecuteAsync(); GameParticipant alice, bob, carla; GameBall firstBlueBall, secondRedBall; using (gameStartResponse) { // Batch requests do not throw exceptions on execution failures as long as the request is valid, so we need to check the response status explicitly. // A HTTP 200 (OK) StatusCode on the batch response indicates that all operations succeeded. // An example later demonstrates a failure case. if (!gameStartResponse.IsSuccessStatusCode) { // Log and handle failure LogFailure(gameStartResponse); return; } // Refresh in-memory state from response. // The TransactionalBatchResponse has a list of TransactionalBatchOperationResult, one for each operation within the batch request in the order // the operations were added to the TransactionalBatch. for (int index = 0; index < ballCount; index++) { // The GetOperationResultAtIndex method returns the result of the operation at the given index with a Resource deserialized to the provided type. TransactionalBatchOperationResult <GameBall> gameBallResult = gameStartResponse.GetOperationResultAtIndex <GameBall>(index); balls.Add(gameBallResult.Resource); } firstBlueBall = balls[1]; secondRedBall = balls[3]; for (int index = ballCount; index < gameStartResponse.Count; index++) { players.Add(gameStartResponse.GetOperationResultAtIndex <GameParticipant>(index).Resource); } alice = players.Single(p => p.Nickname == "alice"); bob = players.Single(p => p.Nickname == "bob"); carla = players.Single(p => p.Nickname == "carla"); } PrintState(players, balls); Console.WriteLine("Alice goes to 6, 4 and finds a blue ball ..."); alice.BlueCount++; // Upserts maybe used to replace items or create them if they are not already present. // An existing item maybe replaced along with concurrency checks the ETag returned in the responses of earlier requests on the item // or without these checks if they are not required. // Item deletes may also be a part of batch requests. TransactionalBatchResponse aliceFoundBallResponse = await gamesContainer.CreateTransactionalBatch(new PartitionKey(gameId)) .UpsertItem <ParticipantLastActive>(ParticipantLastActive.Create(gameId, "alice")) .ReplaceItem <GameParticipant>(alice.Nickname, alice, new TransactionalBatchItemRequestOptions { IfMatchEtag = alice.ETag }) .DeleteItem(firstBlueBall.Id) .ExecuteAsync(); using (aliceFoundBallResponse) { if (!aliceFoundBallResponse.IsSuccessStatusCode) { // Log and handle failure alice.BlueCount--; LogFailure(aliceFoundBallResponse); return; } // Refresh in-memory state from response. balls.Remove(firstBlueBall); // We only update the etag as we have the rest of the state we care about here already as needed. alice.ETag = aliceFoundBallResponse[1].ETag; } PrintState(players, balls); Console.WriteLine("Bob goes to 8, 8 and finds a red ball ..."); bob.RedCount++; // Stream variants for all batch operations that accept an item are also available for use when the item is available as a Stream. Stream bobIsActiveStream = ParticipantLastActive.CreateStream(gameId, "bob"); Stream bobAsStream = Program.AsStream(bob); using (bobIsActiveStream) using (bobAsStream) { TransactionalBatchResponse bobFoundBallResponse = await gamesContainer.CreateTransactionalBatch(new PartitionKey(gameId)) .UpsertItemStream(bobIsActiveStream) .ReplaceItemStream(bob.Nickname, bobAsStream, new TransactionalBatchItemRequestOptions { IfMatchEtag = bob.ETag }) .DeleteItem(secondRedBall.Id) .ExecuteAsync(); using (bobFoundBallResponse) { if (!bobFoundBallResponse.IsSuccessStatusCode) { // Log and handle failure. bob.RedCount--; LogFailure(bobFoundBallResponse); return; } // Refresh in-memory state from response. balls.Remove(secondRedBall); // The resultant item for each operation is also available as a Stream that can be used for example if the response is just // going to be transferred to some other system. Stream updatedPlayerAsStream = bobFoundBallResponse[1].ResourceStream; bob = Program.FromStream <GameParticipant>(updatedPlayerAsStream); } } PrintState(players, balls); Console.WriteLine("A golden ball appears near each of the players to select an instant winner ..."); TransactionalBatchResponse goldenBallResponse = await gamesContainer.CreateTransactionalBatch(new PartitionKey(gameId)) .CreateItem <GameBall>(GameBall.Create(gameId, Color.Gold, 2, 2)) .CreateItem <GameBall>(GameBall.Create(gameId, Color.Gold, 6, 3)) // oops - there is already a ball at 8, 7 .CreateItem <GameBall>(GameBall.Create(gameId, Color.Gold, 8, 7)) .ExecuteAsync(); using (goldenBallResponse) { // If an operation within the TransactionalBatch fails during execution, the TransactionalBatchResponse will have a status code of the failing operation. // The TransactionalBatchOperationResult entries within the response can be read to get details about the specific operation that failed. // The failing operation (for example if we have a conflict because we are trying to create an item // that already exists) will have the StatusCode on its corresponding TransactionalBatchOperationResult set to the actual failure status // (HttpStatusCode.Conflict in this example). All other result entries will have a status code of HTTP 424 Failed Dependency. // In case any operation within a TransactionalBatch fails, no changes from the batch will be committed. // Other status codes such as HTTP 429 (Too Many Requests) and HTTP 5xx on server errors may also be returned on the TransactionalBatchResponse. if (!goldenBallResponse.IsSuccessStatusCode) { if (goldenBallResponse.StatusCode == HttpStatusCode.Conflict) { for (int index = 0; index < goldenBallResponse.Count; index++) { TransactionalBatchOperationResult operationResult = goldenBallResponse[index]; if ((int)operationResult.StatusCode == 424) { // This operation failed because it was in a TransactionalBatch along with another operation where the latter was the actual cause of failure. continue; } else if (operationResult.StatusCode == HttpStatusCode.Conflict) { Console.WriteLine("Creation of the {0}rd golden ball failed because there was already an existing ball at that position.", index + 1); } } } else { // Log and handle other failures LogFailure(goldenBallResponse); return; } } } PrintState(players, balls); Console.WriteLine("We need to end the game now; determining the winner as the player with highest balls ..."); // Batch requests may also be used to atomically read multiple items with the same partition key. TransactionalBatchResponse playersResponse = await gamesContainer.CreateTransactionalBatch(new PartitionKey(gameId)) .ReadItem(alice.Nickname) .ReadItem(bob.Nickname) .ReadItem(carla.Nickname) .ExecuteAsync(); GameParticipant winner = null; bool isTied = false; using (playersResponse) { if (!playersResponse.IsSuccessStatusCode) { // Log and handle failure LogFailure(playersResponse); return; } for (int index = 0; index < playerCount; index++) { GameParticipant current; if (index == 0) { // The item returned can be made available as the required POCO type using GetOperationResultAtIndex. // A single batch request can be used to read items that can be deserialized to different POCOs as well. current = playersResponse.GetOperationResultAtIndex <GameParticipant>(index).Resource; } else { // The item returned can also instead be accessed directly as a Stream (for example to pass as-is to another component). Stream aliceInfo = playersResponse[index].ResourceStream; current = Program.FromStream <GameParticipant>(aliceInfo); } if (winner == null || current.TotalCount > winner.TotalCount) { winner = current; isTied = false; } else if (current.TotalCount == winner.TotalCount) { isTied = true; } } } if (!isTied) { Console.WriteLine($"{winner.Nickname} has won the game!\n"); } else { Console.WriteLine("The game is a tie; there is no clear winner.\n"); } }
static async Task Main(string[] args) { IConfiguration configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .Build(); CosmosClient cosmosClient = new CosmosClientBuilder(configuration.GetConnectionString("Cosmos")) .WithApplicationName("OnDotNetRocks") .Build(); Container container = await Program.InitializeContainerAsync(cosmosClient); Show theShow; Episode episode1; Episode episode2; Episode episode3; const string showName = "OnDotNet"; Console.WriteLine("Creating show and episodes, press ENTER to continue..."); Console.ReadLine(); TransactionalBatchResponse createShowResponse = await container.CreateTransactionalBatch(new PartitionKey(showName)) .CreateItem <Show>(Show.Create(showName)) .CreateItem <Episode>(Episode.Create(showName, "Cosmos DB SDK Features 1")) .CreateItem <Episode>(Episode.Create(showName, "Cosmos DB SDK Features 2")) .CreateItem <Episode>(Episode.Create(showName, "Cosmos DB SDK Features 3")) .ExecuteAsync(); using (createShowResponse) { if (!createShowResponse.IsSuccessStatusCode) { // Log and handle failure LogFailure(createShowResponse); return; } TransactionalBatchOperationResult <Show> showResult = createShowResponse.GetOperationResultAtIndex <Show>(0); theShow = showResult.Resource; TransactionalBatchOperationResult <Episode> episode1Result = createShowResponse.GetOperationResultAtIndex <Episode>(1); episode1 = episode1Result.Resource; TransactionalBatchOperationResult <Episode> episode2Result = createShowResponse.GetOperationResultAtIndex <Episode>(2); episode2 = episode2Result.Resource; TransactionalBatchOperationResult <Episode> episode3Result = createShowResponse.GetOperationResultAtIndex <Episode>(3); episode3 = episode3Result.Resource; } Console.WriteLine($"Current Show LastUpdate {theShow.LastUpdated}"); await Task.Delay(2000); theShow.LastUpdated = DateTime.UtcNow; Console.WriteLine($"Trying to update Show with date {theShow.LastUpdated}, press ENTER to continue..."); Console.ReadLine(); TransactionalBatchResponse createDuplicatedShow = await container.CreateTransactionalBatch(new PartitionKey(showName)) .ReplaceItem <Show>(theShow.Id, theShow) .CreateItem <Episode>(Episode.Create(showName, "Cosmos DB SDK Features 2")) .ExecuteAsync(); using (createDuplicatedShow) { TransactionalBatchOperationResult <Show> showResult = createDuplicatedShow.GetOperationResultAtIndex <Show>(0); Console.WriteLine($"Batch failed > Show Replace resulted in: {showResult.StatusCode}"); TransactionalBatchOperationResult <Episode> episodeResult = createDuplicatedShow.GetOperationResultAtIndex <Episode>(1); Console.WriteLine($"Batch failed > Episode Create resulted in: {episodeResult.StatusCode}"); } Show storedShow = await container.ReadItemAsync <Show>(theShow.Id, new PartitionKey(showName)); Console.WriteLine($"Stored Show date after batch fail: {storedShow.LastUpdated}"); Console.WriteLine("Updating show and episodes, press ENTER to continue..."); Console.ReadLine(); theShow.LastUpdated = DateTime.UtcNow; episode1.AirDate = DateTime.UtcNow.AddDays(7); episode2.AirDate = DateTime.UtcNow.AddDays(14); TransactionalBatchResponse updateAirDate = await container.CreateTransactionalBatch(new PartitionKey(showName)) .ReplaceItem <Show>(theShow.Id, theShow) .ReplaceItem <Episode>(episode1.Id, episode1) .ReplaceItem <Episode>(episode2.Id, episode2) .DeleteItem(episode3.Id) .ExecuteAsync(); using (updateAirDate) { if (!updateAirDate.IsSuccessStatusCode) { // Log and handle failure LogFailure(updateAirDate); return; } } storedShow = await container.ReadItemAsync <Show>(theShow.Id, new PartitionKey(showName)); Console.WriteLine($"Stored Show date {storedShow.LastUpdated}"); Episode storedEpisode1 = await container.ReadItemAsync <Episode>(episode1.Id, new PartitionKey(showName)); Console.WriteLine($"Stored Episode 1 date {storedEpisode1.AirDate}"); Episode storedEpisode2 = await container.ReadItemAsync <Episode>(episode2.Id, new PartitionKey(showName)); Console.WriteLine($"Stored Episode 2 date {storedEpisode2.AirDate}"); try { await container.ReadItemAsync <Episode>(episode3.Id, new PartitionKey(showName)); } catch (CosmosException ex) when(ex.StatusCode == HttpStatusCode.NotFound) { Console.WriteLine("Episode 3 was deleted."); } }