Skip to content

Commit 6dbaec1

Browse files
Nicholas Greenfieldgreenie-msft
authored andcommitted
Address PR feedback and change all examples to use DTS
1 parent 800725f commit 6dbaec1

File tree

17 files changed

+377
-514
lines changed

17 files changed

+377
-514
lines changed
File renamed without changes.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ This repo contains a Python SDK for use with the [Azure Durable Task Scheduler](
88

99
⚠️ This SDK is currently under active development and is evolving rapidly. While it's not yet ready for production use, we are excited about its potential and look forward to your feedback as we continue to improve it. ⚠️
1010

11-
> Note that this SDK is **not** currently compatible with [Azure Durable Functions](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-overview). If you are looking for a Python SDK for Azure Durable Functions, please see [this repo](https://github.com/Azure/azure-functions-durable-python).
11+
> Note that this SDK is **not** currently compatible with [Azure Durable Functions](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview). If you are looking for a Python SDK for Azure Durable Functions, please see [this repo](https://github.com/Azure/azure-functions-durable-python).
1212
1313
# References
1414
- [Supported Patterns](./docs/supported-patterns.md)
1515
- [Available Features](./docs/features.md)
1616
- [Getting Started](./docs/getting-started.md)
1717
- [Development Guide](./docs/development.md)
18-
- [Contributing Guide](./docs/development.md)
18+
- [Contributing Guide](./CONTRIBUTING.md)
1919

2020
## Trademarks
2121
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft

docs/features.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ Orchestrations can start child orchestrations using the `call_sub_orchestrator`
2222

2323
Orchestrations can wait for external events using the `wait_for_external_event` API. External events are useful for implementing human interaction patterns, such as waiting for a user to approve an order before continuing.
2424

25-
### Continue-as-new (TODO)
25+
### Continue-as-new
2626

2727
Orchestrations can be continued as new using the `continue_as_new` API. This API allows an orchestration to restart itself from scratch, optionally with a new input.
2828

2929
### Suspend, resume, and terminate
3030

3131
Orchestrations can be suspended using the `suspend_orchestration` client API and will remain suspended until resumed using the `resume_orchestration` client API. A suspended orchestration will stop processing new events, but will continue to buffer any that happen to arrive until resumed, ensuring that no data is lost. An orchestration can also be terminated using the `terminate_orchestration` client API. Terminated orchestrations will stop processing new events and will discard any buffered events.
3232

33-
### Retry policies (TODO)
33+
### Retry policies
3434

3535
Orchestrations can specify retry policies for activities and sub-orchestrations. These policies control how many times and how frequently an activity or sub-orchestration will be retried in the event of a transient error.

docs/supported-patterns.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def sequence(ctx: task.OrchestrationContext, _):
2020
return [result1, result2, result3]
2121
```
2222

23-
You can find the full sample [here](./examples/activity_sequence.py).
23+
You can find the full sample [here](../examples/activity_sequence.py).
2424

2525
### Fan-out/fan-in
2626

@@ -48,7 +48,7 @@ def orchestrator(ctx: task.OrchestrationContext, _):
4848
return {'work_items': work_items, 'results': results, 'total': sum(results)}
4949
```
5050

51-
You can find the full sample [here](./examples/fanout_fanin.py).
51+
You can find the full sample [here](../examples/fanout_fanin.py).
5252

5353
### Human interaction and durable timers
5454

@@ -79,4 +79,4 @@ def purchase_order_workflow(ctx: task.OrchestrationContext, order: Order):
7979

8080
As an aside, you'll also notice that the example orchestration above works with custom business objects. Support for custom business objects includes support for custom classes, custom data classes, and named tuples. Serialization and deserialization of these objects is handled automatically by the SDK.
8181

82-
You can find the full sample [here](./examples/human_interaction.py).
82+
You can find the full sample [here](../examples/human_interaction.py).

examples/README.md

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,86 @@
1-
# Examples
2-
3-
This directory contains examples of how to author durable orchestrations using the Durable Task Python SDK.
4-
5-
## Prerequisites
6-
7-
All the examples assume that you have a Durable Task-compatible sidecar running locally. There are two options for this:
8-
9-
1. Install the latest version of the [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/), which contains and exposes an embedded version of the Durable Task engine. The setup process (which requires Docker) will configure the workflow engine to store state in a local Redis container.
10-
11-
2. Clone and run the [Durable Task Sidecar](https://github.com/microsoft/durabletask-go) project locally (requires Go 1.18 or higher). Orchestration state will be stored in a local sqlite database.
12-
13-
## Running the examples
14-
15-
With one of the sidecars running, you can simply execute any of the examples in this directory using `python3`:
16-
17-
```sh
18-
python3 ./activity_sequence.py
19-
```
20-
21-
In some cases, the sample may require command-line parameters or user inputs. In these cases, the sample will print out instructions on how to proceed.
22-
23-
## List of examples
24-
25-
- [Activity sequence](./activity_sequence.py): Orchestration that schedules three activity calls in a sequence.
26-
- [Fan-out/fan-in](./fanout_fanin.py): Orchestration that schedules a dynamic number of activity calls in parallel, waits for all of them to complete, and then performs an aggregation on the results.
27-
- [Human interaction](./human_interaction.py): Orchestration that waits for a human to approve an order before continuing.
1+
# Examples
2+
3+
This directory contains examples of how to author durable orchestrations using the Durable Task Python SDK in conjunction with the Durable Task Scheduler (DTS).
4+
5+
## Prerequisites
6+
If using a deployed Durable Task Scheduler:
7+
- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli)
8+
- [`az durabletask` CLI extension](https://learn.microsoft.com/en-us/cli/azure/durabletask?view=azure-cli-latest)
9+
10+
## Running the Examples
11+
There are two separate ways to run an example:
12+
13+
- Using the Emulator (recommended for learning and development)
14+
- Using a deployed Scheduler and Taskhub in Azure
15+
16+
### Running with the Emulator
17+
We recommend using the emulator for learning and development as it's faster to set up and doesn't require any Azure resources. The emulator simulates a scheduler and taskhub, packaged into an easy-to-use Docker container.
18+
19+
1. Install Docker: If it is not already installed.
20+
21+
2. Pull the Docker Image for the Emulator:
22+
```bash
23+
docker pull mcr.microsoft.com/dts/dts-emulator:v0.0.6
24+
```
25+
26+
3. Run the Emulator: Wait a few seconds for the container to be ready.
27+
```bash
28+
docker run --name dtsemulator -d -p 8080:8080 mcr.microsoft.com/dts/dts-emulator:v0.0.6
29+
```
30+
31+
4. Install the Required Packages:
32+
```bash
33+
pip install -r requirements.txt
34+
```
35+
36+
Note: The example code has been updated to use the default emulator settings automatically (endpoint: http://localhost:8080, taskhub: default). You don't need to set any environment variables.
37+
38+
### Running with a Deployed Scheduler and Taskhub Resource in Azure
39+
For production scenarios or when you're ready to deploy to Azure, you can create a taskhub using the Azure CLI:
40+
41+
1. Create a Scheduler:
42+
```bash
43+
az durabletask scheduler create --resource-group <testrg> --name <testscheduler> --location <eastus> --ip-allowlist "[0.0.0.0/0]" --sku-capacity 1 --sku-name "Dedicated" --tags "{'myattribute':'myvalue'}"
44+
```
45+
46+
2. Create Your Taskhub:
47+
```bash
48+
az durabletask taskhub create --resource-group <testrg> --scheduler-name <testscheduler> --name <testtaskhub>
49+
```
50+
51+
3. Retrieve the Endpoint for the Scheduler: Locate the taskhub in the Azure portal to find the endpoint.
52+
53+
4. Set the Environment Variables:
54+
Bash:
55+
```bash
56+
export TASKHUB=<taskhubname>
57+
export ENDPOINT=<taskhubEndpoint>
58+
```
59+
Powershell:
60+
```powershell
61+
$env:TASKHUB = "<taskhubname>"
62+
$env:ENDPOINT = "<taskhubEndpoint>"
63+
```
64+
65+
5. Install the Required Packages:
66+
```bash
67+
pip install -r requirements.txt
68+
```
69+
70+
### Running the Examples
71+
You can now execute any of the examples in this directory using Python:
72+
73+
```bash
74+
python3 example_file.py
75+
```
76+
77+
### Review Orchestration History and Status in the Durable Task Scheduler Dashboard
78+
To access the Durable Task Scheduler Dashboard, follow these steps:
79+
80+
- **Using the Emulator**: By default, the dashboard runs on portal 8082. Navigate to http://localhost:8082 and click on the default task hub.
81+
82+
- **Using a Deployed Scheduler**: Navigate to the Scheduler resource. Then, go to the Task Hub subresource that you are using and click on the dashboard URL in the top right corner.
83+
84+
```sh
85+
python3 activity_sequence.py
86+
```

examples/activity_sequence.py

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,54 @@
1-
"""End-to-end sample that demonstrates how to configure an orchestrator
2-
that calls an activity function in a sequence and prints the outputs."""
3-
from durabletask import client, task, worker
4-
5-
6-
def hello(ctx: task.ActivityContext, name: str) -> str:
7-
"""Activity function that returns a greeting"""
8-
return f'Hello {name}!'
9-
10-
11-
def sequence(ctx: task.OrchestrationContext, _):
12-
"""Orchestrator function that calls the 'hello' activity function in a sequence"""
13-
# call "hello" activity function in a sequence
14-
result1 = yield ctx.call_activity(hello, input='Tokyo')
15-
result2 = yield ctx.call_activity(hello, input='Seattle')
16-
result3 = yield ctx.call_activity(hello, input='London')
17-
18-
# return an array of results
19-
return [result1, result2, result3]
20-
21-
22-
# configure and start the worker
23-
with worker.TaskHubGrpcWorker() as w:
24-
w.add_orchestrator(sequence)
25-
w.add_activity(hello)
26-
w.start()
27-
28-
# create a client, start an orchestration, and wait for it to finish
29-
c = client.TaskHubGrpcClient()
30-
instance_id = c.schedule_new_orchestration(sequence)
31-
state = c.wait_for_orchestration_completion(instance_id, timeout=10)
32-
if state and state.runtime_status == client.OrchestrationStatus.COMPLETED:
33-
print(f'Orchestration completed! Result: {state.serialized_output}')
34-
elif state:
35-
print(f'Orchestration failed: {state.failure_details}')
1+
"""End-to-end sample that demonstrates how to configure an orchestrator
2+
that calls an activity function in a sequence and prints the outputs."""
3+
import os
4+
5+
from azure.identity import DefaultAzureCredential
6+
7+
from durabletask import client, task
8+
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
9+
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
10+
11+
12+
def hello(ctx: task.ActivityContext, name: str) -> str:
13+
"""Activity function that returns a greeting"""
14+
return f'Hello {name}!'
15+
16+
17+
def sequence(ctx: task.OrchestrationContext, _):
18+
"""Orchestrator function that calls the 'hello' activity function in a sequence"""
19+
# call "hello" activity function in a sequence
20+
result1 = yield ctx.call_activity(hello, input='Tokyo')
21+
result2 = yield ctx.call_activity(hello, input='Seattle')
22+
result3 = yield ctx.call_activity(hello, input='London')
23+
24+
# return an array of results
25+
return [result1, result2, result3]
26+
27+
28+
# Use environment variables if provided, otherwise use default emulator values
29+
taskhub_name = os.getenv("TASKHUB", "default")
30+
endpoint = os.getenv("ENDPOINT", "http://localhost:8080")
31+
32+
print(f"Using taskhub: {taskhub_name}")
33+
print(f"Using endpoint: {endpoint}")
34+
35+
# Set credential to None for emulator, or DefaultAzureCredential for Azure
36+
credential = None if endpoint == "http://localhost:8080" else DefaultAzureCredential()
37+
38+
# configure and start the worker - use secure_channel=False for emulator
39+
secure_channel = endpoint != "http://localhost:8080"
40+
with DurableTaskSchedulerWorker(host_address=endpoint, secure_channel=secure_channel,
41+
taskhub=taskhub_name, token_credential=credential) as w:
42+
w.add_orchestrator(sequence)
43+
w.add_activity(hello)
44+
w.start()
45+
46+
# Construct the client and run the orchestrations
47+
c = DurableTaskSchedulerClient(host_address=endpoint, secure_channel=secure_channel,
48+
taskhub=taskhub_name, token_credential=credential)
49+
instance_id = c.schedule_new_orchestration(sequence)
50+
state = c.wait_for_orchestration_completion(instance_id, timeout=60)
51+
if state and state.runtime_status == client.OrchestrationStatus.COMPLETED:
52+
print(f'Orchestration completed! Result: {state.serialized_output}')
53+
elif state:
54+
print(f'Orchestration failed: {state.failure_details}')

examples/dts/README.md

Lines changed: 0 additions & 82 deletions
This file was deleted.

0 commit comments

Comments
 (0)