Skip to content

Commit e6fb37e

Browse files
committed
Initial commit for the Saga Distributed Transactions Pattern on Azure
0 parents  commit e6fb37e

26 files changed

+1051
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Igor Iric
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Saga Distributed Transactions Pattern on Azure
2+
3+
This repository provides an implementation of the **Saga Distributed Transactions Pattern** using Azure services, including Azure Functions, Azure Service Bus, and Azure Cosmos DB. The Saga Pattern is used to manage distributed transactions across multiple microservices, ensuring data consistency and reliability even when some services fail.
4+
5+
## 🏗️ Architectural Overview
6+
7+
The **Saga Pattern** breaks a distributed transaction into a series of smaller transactions that are executed in a sequence. If any transaction fails, the Saga Pattern triggers compensating transactions to undo the changes made by previous steps, ensuring **eventual consistency** across the entire system.
8+
9+
### Architecture Diagram
10+
11+
```mermaid
12+
graph TD
13+
Client["Client"] -->|Initiates Transaction| OrchestratorFunction["Orchestrator Function (Azure Function)"]
14+
OrchestratorFunction -->|Manages Steps| ActivityFunction["Activity Function (Azure Function)"]
15+
OrchestratorFunction -->|Compensation Triggered| CompensatorFunction["Compensator Function (Azure Function)"]
16+
ActivityFunction -->|Calls| ServiceBus["Azure Service Bus"]
17+
ServiceBus -->|Triggers| CompensatorFunction
18+
ActivityFunction -->|Manipulates| CosmosDB["Azure Cosmos DB"]
19+
CompensatorFunction -->|Ensures| CosmosDB
20+
OrchestratorFunction -->|Monitors| AppInsights["Application Insights"]
21+
ActivityFunction -->|Monitors| AppInsights
22+
CompensatorFunction -->|Monitors| AppInsights
23+
```
24+
25+
### Components of the Architecture
26+
27+
1. **Client**: Initiates a transaction request to the **Orchestrator Function**.
28+
2. **Orchestrator Function**: Manages the overall workflow of the Saga, coordinating the execution of the **Activity Function** and triggering the **Compensator Function** in case of a failure.
29+
3. **Activity Function**: Performs individual steps of the transaction, such as processing business logic and writing to **Azure Cosmos DB**.
30+
4. **Compensator Function**: Reverts or compensates for changes if a failure occurs in any step of the Saga.
31+
5. **Azure Service Bus**: Acts as a messaging backbone for communication between functions, triggering compensating transactions when necessary.
32+
6. **Azure Cosmos DB**: Serves as the data store where the transaction data is stored.
33+
7. **Application Insights**: Collects logs, metrics, and telemetry data for monitoring and troubleshooting purposes.
34+
35+
## 📂 Repository Structure
36+
37+
```
38+
/saga-distributed-transactions
39+
40+
├── README.md # Root README with architecture overview and getting started
41+
├── LICENSE # MIT License
42+
43+
├── infrastructure
44+
│ ├── README.md # README for Infrastructure deployment
45+
│ ├── azure-resources.bicep # Bicep template for all Azure resources
46+
│ └── .github/workflows/deploy-bicep.yml # GitHub Action to deploy Azure resources
47+
48+
├── orchestrator-function
49+
│ ├── README.md # README for Orchestrator Function
50+
│ ├── orchestrator_function.py # Python code for Orchestrator Function
51+
│ ├── requirements.txt # Dependencies for Orchestrator Function
52+
│ └── .github/workflows/deploy-orchestrator.yml # GitHub Action to deploy the Orchestrator Function
53+
54+
├── activity-function
55+
│ ├── README.md # README for Activity Function
56+
│ ├── activity_function.py # Python code for Activity Function
57+
│ ├── requirements.txt # Dependencies for Activity Function
58+
│ └── .github/workflows/deploy-activity.yml # GitHub Action to deploy the Activity Function
59+
60+
├── compensator-function
61+
│ ├── README.md # README for Compensator Function
62+
│ ├── compensator_function.py # Python code for Compensator Function
63+
│ ├── requirements.txt # Dependencies for Compensator Function
64+
│ └── .github/workflows/deploy-compensator.yml # GitHub Action to deploy the Compensator Function
65+
```
66+
67+
## 🚀 Getting Started
68+
69+
### Step 1: Deploy the Infrastructure
70+
71+
1. Navigate to the **`infrastructure`** folder.
72+
2. Follow the instructions in the [Infrastructure README](infrastructure/README.md) to deploy the required Azure resources using the Bicep template and GitHub Actions.
73+
74+
### Step 2: Deploy the Azure Functions
75+
76+
1. Deploy the **Orchestrator Function**:
77+
- Navigate to the **`orchestrator-function`** folder.
78+
- Follow the instructions in the [Orchestrator Function README](orchestrator-function/README.md) to deploy the function using GitHub Actions.
79+
80+
2. Deploy the **Activity Function**:
81+
- Navigate to the **`activity-function`** folder.
82+
- Follow the instructions in the [Activity Function README](activity-function/README.md) to deploy the function using GitHub Actions.
83+
84+
3. Deploy the **Compensator Function**:
85+
- Navigate to the **`compensator-function`** folder.
86+
- Follow the instructions in the [Compensator Function README](compensator-function/README.md) to deploy the function using GitHub Actions.
87+
88+
### Step 3: Running the Solution
89+
90+
1. **Trigger the Orchestrator Function**:
91+
- Use an HTTP client (like Postman or `curl`) to send a POST request to the `/startSaga` endpoint of the Orchestrator Function.
92+
93+
2. **Monitor the Workflow**:
94+
- Check **Application Insights** in the Azure portal for logs and telemetry data to monitor the execution flow and identify any issues.
95+
96+
## 💡 How It Works
97+
98+
1. **Initiate a Transaction**:
99+
- The **Client** initiates a transaction request to the **Orchestrator Function** by sending an HTTP request with order details.
100+
101+
2. **Orchestrator Function Manages Workflow**:
102+
- The **Orchestrator Function** processes the request, calls the **Activity Function** to handle the business logic, and waits for a success or failure response.
103+
104+
3. **Activity Function Processes Data**:
105+
- The **Activity Function** receives the order data and writes it to **Azure Cosmos DB**.
106+
- If the transaction is successful, it completes the process; if there is an error, the **Compensator Function** is triggered.
107+
108+
4. **Compensator Function Handles Failures**:
109+
- The **Compensator Function** is triggered if a failure occurs during the transaction.
110+
- It compensates for the failure by removing or rolling back any changes made to **Azure Cosmos DB**.
111+
112+
5. **Application Insights Monitors the Flow**:
113+
- All functions log their activities to **Application Insights** for monitoring and troubleshooting purposes.
114+
115+
## 📝 About the Saga Pattern
116+
117+
The **Saga Pattern** is a design pattern for managing distributed transactions in microservices architecture. It breaks down a large transaction into smaller sub-transactions that are executed in a sequence, ensuring eventual consistency across the system.
118+
119+
- **Resilience**: By breaking down transactions into smaller steps and defining compensating actions, the Saga Pattern helps systems recover from failures gracefully.
120+
- **Scalability**: Decoupling transactions across services improves scalability, allowing independent scaling of microservices.
121+
- **Flexibility**: The Saga Pattern supports various consistency models, including eventual consistency, which is crucial for distributed systems.
122+
123+
## 📄 License
124+
125+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
126+
127+
## 🙌 Contributing
128+
129+
Contributions are welcome! Please open an issue or submit a pull request for any improvements or suggestions.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Deploy Activity Function
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
env:
10+
AZURE_FUNCTIONAPP_NAME: 'activityFunctionApp'
11+
AZURE_FUNCTIONAPP_PACKAGE_PATH: './activity-function'
12+
AZURE_RESOURCE_GROUP: '<your-resource-group>'
13+
FUNCTIONAPP_RUNTIME: 'python'
14+
PYTHON_VERSION: '3.8'
15+
16+
jobs:
17+
build-and-deploy:
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v2
23+
24+
- name: Set up Python environment
25+
uses: actions/setup-python@v2
26+
with:
27+
python-version: ${{ env.PYTHON_VERSION }}
28+
29+
- name: Login to Azure
30+
uses: azure/login@v1
31+
with:
32+
creds: ${{ secrets.AZURE_CREDENTIALS }}
33+
34+
- name: Deploy Azure Function
35+
uses: Azure/functions-action@v1
36+
with:
37+
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
38+
package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
39+
publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}
40+
41+
- name: Set Azure Function App Environment Variables
42+
run: |
43+
az functionapp config appsettings set --resource-group ${{ env.AZURE_RESOURCE_GROUP }} --name ${{ env.AZURE_FUNCTIONAPP_NAME }} --settings "ServiceBusConnection=${{ secrets.ServiceBusConnection }}" "CosmosDBConnectionString=${{ secrets.CosmosDBConnectionString }}" "FUNCTIONS_WORKER_RUNTIME=${{ env.FUNCTIONAPP_RUNTIME }}"

activity-function/.gitignore

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
bin
2+
obj
3+
csx
4+
.vs
5+
edge
6+
Publish
7+
8+
*.user
9+
*.suo
10+
*.cscfg
11+
*.Cache
12+
project.lock.json
13+
14+
/packages
15+
/TestResults
16+
17+
/tools/NuGet.exe
18+
/App_Data
19+
/secrets
20+
/data
21+
.secrets
22+
appsettings.json
23+
local.settings.json
24+
25+
node_modules
26+
dist
27+
28+
# Local python packages
29+
.python_packages/
30+
31+
# Python Environments
32+
.env
33+
.venv
34+
env/
35+
venv/
36+
ENV/
37+
env.bak/
38+
venv.bak/
39+
40+
# Byte-compiled / optimized / DLL files
41+
__pycache__/
42+
*.py[cod]
43+
*$py.class
44+
45+
# Azurite artifacts
46+
__blobstorage__
47+
__queuestorage__
48+
__azurite_db*__.json

activity-function/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Activity Function for Saga Distributed Transactions Pattern
2+
3+
This folder contains the Python code for the **Activity Function**. The Activity Function performs individual steps of the transaction, such as processing a part of the business logic and writing to Cosmos DB.
4+
5+
## 📑 Files
6+
7+
- **`activity_function.py`**: Python code for the Activity Function.
8+
- **`requirements.txt`**: Lists the dependencies required by the Activity Function.
9+
- **`deploy-activity.yml`**: GitHub Action workflow to automate the deployment of the Activity Function.
10+
11+
## 🚀 How to Deploy the Activity Function
12+
13+
### Prerequisites
14+
15+
1. **Azure Subscription**: You need an active Azure account.
16+
2. **Azure Function App**: Ensure the Azure Function App is created (using the Bicep template in the `/infrastructure` folder).
17+
3. **GitHub Secrets Configuration**:
18+
- **`AZURE_CREDENTIALS`**: Azure service principal credentials in JSON format.
19+
- **`AZURE_FUNCTIONAPP_PUBLISH_PROFILE`**: Publish profile for the Azure Function App.
20+
- **`ServiceBusConnection`**: Connection string for the Azure Service Bus namespace.
21+
- **`CosmosDBConnectionString`**: Connection string for the Azure Cosmos DB account.
22+
23+
### Steps to Deploy
24+
25+
1. **Add Required Secrets to GitHub**:
26+
- Go to your repository's **Settings > Secrets and variables > Actions > New repository secret**.
27+
- Add the following secrets:
28+
- **`AZURE_CREDENTIALS`**: Your Azure service principal credentials.
29+
- **`AZURE_FUNCTIONAPP_PUBLISH_PROFILE`**: The publish profile for your Azure Function App.
30+
- **`ServiceBusConnection`**: The connection string for your Azure Service Bus namespace.
31+
- **`CosmosDBConnectionString`**: The connection string for your Azure Cosmos DB account.
32+
33+
2. **Run the GitHub Action**:
34+
- Push your changes to the `main` branch or manually trigger the **Deploy Activity Function** workflow from the **Actions** tab.
35+
36+
3. **Monitor the Deployment**:
37+
- Go to the **Actions** tab in your GitHub repository.
38+
- Select the **Deploy Activity Function** workflow to monitor the deployment progress.
39+
40+
### 📝 How the Activity Function Works
41+
42+
1. **Listen for Messages**:
43+
- The Activity Function is triggered when it receives a message from the **Service Bus Topic** (`sagaServiceBusTopic`).
44+
- This message contains order details, such as `orderId`, `customerName`, and `items`.
45+
46+
2. **Process the Transaction**:
47+
- The function writes the order details to **Azure Cosmos DB**.
48+
- If successful, the data is stored in the Cosmos DB container; otherwise, it logs an error.
49+
50+
## 📄 License
51+
52+
This project is licensed under the MIT License - see the [LICENSE](../LICENSE) file for details.

activity-function/__init__.py

Whitespace-only changes.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import azure.functions as func
2+
import logging
3+
import os
4+
import json
5+
from azure.cosmos import CosmosClient, exceptions
6+
7+
# Get environment variables
8+
cosmos_db_conn_str = os.getenv('CosmosDBConnectionString')
9+
cosmos_db_database_name = os.getenv('cosmosDbDatabaseName', 'myDatabase')
10+
cosmos_db_container_name = os.getenv('cosmosDbContainerName', 'myContainer')
11+
12+
app = func.FunctionApp()
13+
14+
@app.function_name(name="ActivityFunction")
15+
@app.service_bus_topic_trigger(
16+
connection="ServiceBusConnection",
17+
topic_name="sagaServiceBusTopic",
18+
subscription_name="activitySubscription",
19+
)
20+
def run_activity(message: func.ServiceBusMessage):
21+
logging.info('Activity Function: Processing message...')
22+
23+
try:
24+
order_data = json.loads(message.get_body().decode('utf-8'))
25+
logging.info(f"Processing order data: {order_data}")
26+
27+
cosmos_client = CosmosClient.from_connection_string(cosmos_db_conn_str)
28+
database = cosmos_client.get_database_client(cosmos_db_database_name)
29+
container = database.get_container_client(cosmos_db_container_name)
30+
31+
# Upsert order data to Cosmos DB
32+
container.upsert_item(order_data)
33+
logging.info(f"Order data stored in Cosmos DB: {order_data['orderId']}")
34+
35+
except exceptions.CosmosHttpResponseError as e:
36+
logging.error(f"Cosmos DB error: {e}")
37+
except Exception as e:
38+
logging.error(f"Unexpected error in Activity Function: {e}")

activity-function/host.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"version": "2.0",
3+
"logging": {
4+
"applicationInsights": {
5+
"samplingSettings": {
6+
"isEnabled": true,
7+
"excludedTypes": "Request"
8+
}
9+
}
10+
},
11+
"extensionBundle": {
12+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
13+
"version": "[4.*, 5.0.0)"
14+
}
15+
}

activity-function/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
azure-functions
2+
azure-cosmos

0 commit comments

Comments
 (0)