/
Saga Pattern

Saga Pattern

Problem

  • Data consistency is a major issue in distributed transaction scenarios.

  • Cross-service data consistency requires a cross-service transaction management strategy : transactions must be atomic, consistent, isolated and durable (ACID).

    • If a transaction (step) fails, the pattern will execute compensating transactions that counteract the preceding transactions.

ย 

Solution

  • Orchestration is a way to coordinate sagas where a centralized controller tells the saga participants waht local transactions to execute.

    • Orchestrator executes saga requests, stores and interprets the states of each task, and handles failure recovery with compensating transactions.

image-20240827-192837.png
Diagram from Microsoft Documentation

Saga In Practice

Example of orchestration with Azure Function Apps and 2 different triggers : Call API from Web App (client application) or a change in a blob Storage.

image-20240828-152029.png

Example of Triggering with Azure Blob Storage

public static class BlobTriggerStart { [FunctionName("BlobTriggerStart")] public static async Task HttpBlobStart( [BlobTrigger("photoscontainer/{name}", Connection = "StorageConnectionString")] CloudBlockBlob myCloudBlob, string name, ILogger log, [DurableClient] IDurableOrchestrationClient starter) { try { log.LogInformation($"Started orchestration trigged by BLOB trigger. A blob item with name = '{name}'"); log.LogInformation($"BLOB Name {myCloudBlob.Name}"); // Function input comes from the request content. if (myCloudBlob != null) { var newUploadedBlobItem = new CloudBlobItem { Name = myCloudBlob.Name, BlobUrl = myCloudBlob.Uri.AbsoluteUri.ToString(), Metadata = (Dictionary<string, string>)myCloudBlob.Metadata, FileType = myCloudBlob.BlobType.ToString(), Size = myCloudBlob.Name.Length.ToString(), ETag = myCloudBlob.Properties.ETag.ToString() }; var instanceId = await starter.StartNewAsync("AzureStorageOrchestrator", newUploadedBlobItem); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); } else { log.LogError($"The blob was trigged but myCloudBlob was empty"); } } catch (Exception ex) { //Errorhandling log.LogError("Something went wrong. Error : " + ex.InnerException); throw; } } }