Python | Durable Task SDK
This sample demonstrates large payload externalization with the Azure Durable Task Scheduler using the Python SDK. When orchestration inputs, activity outputs, or event data exceed a configurable size threshold, the SDK automatically offloads them to Azure Blob Storage and replaces them with compact reference tokens in gRPC messages.
In this sample:
- A
generate_dataactivity produces payloads of configurable size - A
process_dataactivity receives and summarizes the data - The orchestrator runs twice — once with a small payload (stays inline) and once with a large payload (externalized to blob storage)
- Externalization is completely transparent to the orchestration logic
This pattern is useful for:
- Workflows that process large documents, images, or datasets
- Avoiding gRPC message size limits when passing data between activities
- Keeping orchestration history compact while allowing large intermediate results
- Python 3.10+
- Docker (for running the emulator and Azurite)
- Azure CLI (if using a deployed Durable Task Scheduler)
There are two ways to run this sample locally:
This sample provides a docker-compose.yml that starts both the DTS emulator and Azurite (Azure Storage emulator) together:
docker compose up -dThis starts:
- DTS emulator on ports 8080 (gRPC) and 8082 (dashboard)
- Azurite on ports 10000 (blob), 10001 (queue), and 10002 (table)
Wait a few seconds for both containers to be ready. To stop the services later:
docker compose downNote: The example code automatically uses the default emulator settings (endpoint: http://localhost:8080, taskhub: default) and the Azurite connection string (UseDevelopmentStorage=true). You don't need to set any environment variables.
Local development with a deployed scheduler:
-
Install the durable task scheduler CLI extension:
az upgrade az extension add --name durabletask --allow-preview true -
Create a resource group in a region where the Durable Task Scheduler is available:
az provider show --namespace Microsoft.DurableTask --query "resourceTypes[?resourceType=='schedulers'].locations | [0]" --out tableaz group create --name my-resource-group --location <location>
-
Create a durable task scheduler resource:
az durabletask scheduler create \ --resource-group my-resource-group \ --name my-scheduler \ --ip-allowlist '["0.0.0.0/0"]' \ --sku-name "Dedicated" \ --sku-capacity 1 \ --tags "{'myattribute':'myvalue'}" -
Create a task hub within the scheduler resource:
az durabletask taskhub create \ --resource-group my-resource-group \ --scheduler-name my-scheduler \ --name "my-taskhub" -
Grant the current user permission to connect to the
my-taskhubtask hub:subscriptionId=$(az account show --query "id" -o tsv) loggedInUser=$(az account show --query "user.name" -o tsv) az role assignment create \ --assignee $loggedInUser \ --role "Durable Task Data Contributor" \ --scope "/subscriptions/$subscriptionId/resourceGroups/my-resource-group/providers/Microsoft.DurableTask/schedulers/my-scheduler/taskHubs/my-taskhub"
Once you have set up either the emulator or deployed scheduler, follow these steps to run the sample:
-
First, activate your Python virtual environment (if you're using one):
python -m venv venv source venv/bin/activate # On Windows, use: venv\Scripts\activate
-
If you're using a deployed scheduler, set environment variables:
export ENDPOINT=$(az durabletask scheduler show \ --resource-group my-resource-group \ --name my-scheduler \ --query "properties.endpoint" \ --output tsv) export TASKHUB="my-taskhub" export STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=https;AccountName=..."
-
Install the required packages:
pip install -r requirements.txt
-
Start the worker in a terminal:
python worker.py
You should see output indicating the worker has started and registered the orchestration and activities.
-
In a new terminal (with the virtual environment activated if applicable), run the client:
Note: Remember to set the environment variables again if you're using a deployed scheduler.
python client.py
Using taskhub: default
Using endpoint: http://localhost:8080
INFO:__main__:Starting Large Payload pattern worker...
INFO:__main__:Generating data with 10 records
INFO:__main__:Processed 10 records (70 bytes)
INFO:__main__:Generating data with 10000 records
INFO:__main__:Processed 10000 records (70000 bytes)
Using taskhub: default
Using endpoint: http://localhost:8080
--- Small payload (stays inline) ---
Result: "Processed 10 records (70 bytes)"
--- Large payload (externalized to blob storage) ---
Result: "Processed 10000 records (70000 bytes)"
Done!
Both orchestrations produce the same type of result. The difference is invisible to the application code — the SDK transparently externalizes the ~342 KB payload to blob storage and retrieves it when needed.
The BlobPayloadStore is configured with a BlobPayloadStoreOptions object:
store = BlobPayloadStore(BlobPayloadStoreOptions(
connection_string=storage_conn_str,
threshold_bytes=262_144, # Externalize payloads larger than 256 KiB
))Key options:
connection_string: Azure Storage connection string (orUseDevelopmentStorage=truefor Azurite)threshold_bytes: Payloads larger than this are externalized (default: 256 KiB)max_stored_payload_bytes: Maximum payload size that can be stored (default: 10 MB)enable_compression: Whether to compress payloads with GZip before storing (default:True)container_name: Blob container name (default:durabletask-payloads)
Both the worker and client must be configured with the same payload store:
# Worker
with DurableTaskSchedulerWorker(..., payload_store=store) as worker:
...
# Client
client = DurableTaskSchedulerClient(..., payload_store=store)- Emulator: Navigate to http://localhost:8082 → select the "default" task hub
- Azure: Navigate to your Scheduler resource in the Azure Portal → Task Hub → Dashboard URL
- Function Chaining - Basic sequential workflow pattern
- Fan-Out/Fan-In - Parallel processing pattern
- Large Payload (.NET) - Same pattern in .NET
- Large Payload (Go) - Payload externalization with the Go SDK