Skip to content

Commit 158157b

Browse files
authored
Eager workflow start sample (#256)
* Eager workflow start sample * Formatting * Update README
1 parent 7b437d5 commit 158157b

File tree

9 files changed

+111
-0
lines changed

9 files changed

+111
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Some examples require extra dependencies. See each sample's directory for specif
6565
* [custom_decorator](custom_decorator) - Custom decorator to auto-heartbeat a long-running activity.
6666
* [custom_metric](custom_metric) - Custom metric to record the workflow type in the activity schedule to start latency.
6767
* [dsl](dsl) - DSL workflow that executes steps defined in a YAML file.
68+
* [eager_wf_start](eager_wf_start) - Run a workflow using Eager Workflow Start
6869
* [encryption](encryption) - Apply end-to-end encryption for all input/output.
6970
* [env_config](env_config) - Load client configuration from TOML files with programmatic overrides.
7071
* [gevent_async](gevent_async) - Combine gevent and Temporal.

eager_wf_start/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Eager Workflow Start
2+
3+
This sample shows how to create a workflow that uses Eager Workflow Start.
4+
5+
The target use case is workflows whose first task needs to execute quickly (ex: payment verification in an online checkout workflow). That work typically can't be done directly in the workflow (ex: using web APIs, databases, etc.), and also needs to avoid the overhead of dispatching another task. Using a Local Activity suffices both needs, which this sample demonstrates.
6+
7+
You can read more about Eager Workflow Start in our:
8+
9+
- [Eager Workflow Start blog](https://temporal.io/blog/improving-latency-with-eager-workflow-start)
10+
- [Worker Performance Docs](https://docs.temporal.io/develop/worker-performance#eager-workflow-start)
11+
12+
To run, first see the main [README.md](../README.md) for prerequisites.
13+
14+
Then run the sample via:
15+
16+
uv run eager_wf_start/run.py

eager_wf_start/__init__.py

Whitespace-only changes.

eager_wf_start/activities.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from temporalio import activity
2+
3+
4+
@activity.defn()
5+
async def greeting(name: str) -> str:
6+
return f"Hello {name}!"

eager_wf_start/run.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import asyncio
2+
import uuid
3+
4+
from temporalio.client import Client
5+
from temporalio.worker import Worker
6+
7+
from eager_wf_start.activities import greeting
8+
from eager_wf_start.workflows import EagerWorkflow
9+
10+
TASK_QUEUE = "eager-wf-start-task-queue"
11+
12+
13+
async def main():
14+
15+
# Note that the worker and client run in the same process and share the same client connection.
16+
client = await Client.connect("localhost:7233")
17+
worker = Worker(
18+
client,
19+
task_queue=TASK_QUEUE,
20+
workflows=[EagerWorkflow],
21+
activities=[greeting],
22+
)
23+
24+
# Run worker in the background
25+
async with worker:
26+
# Start workflow(s) while worker is running
27+
wf_handle = await client.start_workflow(
28+
EagerWorkflow.run,
29+
"Temporal",
30+
id=f"eager-workflow-id-{uuid.uuid4()}",
31+
task_queue=TASK_QUEUE,
32+
request_eager_start=True,
33+
)
34+
35+
# This is an internal flag not intended to be used publicly.
36+
# It is used here purely to display that the workflow was eagerly started.
37+
print(f"Workflow eagerly started: {wf_handle.__temporal_eagerly_started}")
38+
print(await wf_handle.result())
39+
40+
41+
if __name__ == "__main__":
42+
asyncio.run(main())

eager_wf_start/workflows.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from datetime import timedelta
2+
3+
from temporalio import workflow
4+
5+
with workflow.unsafe.imports_passed_through():
6+
from eager_wf_start.activities import greeting
7+
8+
9+
@workflow.defn
10+
class EagerWorkflow:
11+
@workflow.run
12+
async def run(self, name: str) -> str:
13+
return await workflow.execute_local_activity(
14+
greeting, name, schedule_to_close_timeout=timedelta(seconds=5)
15+
)

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ async def env(request) -> AsyncGenerator[WorkflowEnvironment, None]:
4545
dev_server_extra_args=[
4646
"--dynamic-config-value",
4747
"frontend.enableExecuteMultiOperation=true",
48+
"--dynamic-config-value",
49+
"system.enableEagerWorkflowStart=true",
4850
]
4951
)
5052
elif env_type == "time-skipping":

tests/eager_wf_start/__init__.py

Whitespace-only changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import uuid
2+
3+
from temporalio.client import Client
4+
from temporalio.worker import Worker
5+
6+
from eager_wf_start.activities import greeting
7+
from eager_wf_start.workflows import EagerWorkflow
8+
9+
10+
async def test_eager_wf_start(client: Client):
11+
task_queue_name = str(uuid.uuid4())
12+
13+
async with Worker(
14+
client,
15+
task_queue=task_queue_name,
16+
workflows=[EagerWorkflow],
17+
activities=[greeting],
18+
):
19+
handle = await client.start_workflow(
20+
EagerWorkflow.run,
21+
"Temporal",
22+
id=f"workflow-{uuid.uuid4()}",
23+
task_queue=task_queue_name,
24+
request_eager_start=True,
25+
)
26+
print("HANDLE", handle.__temporal_eagerly_started)
27+
assert handle.__temporal_eagerly_started
28+
result = await handle.result()
29+
assert result == "Hello Temporal!"

0 commit comments

Comments
 (0)