A small Temporal demo that spawns a large number of workflows and then cancels them in bulk. It includes:
- Work service (Python) that creates a high volume of child/grandchild workflows and tags them with a search attribute.
- Canceler service (Python) that orchestrates cancellation and confirmation.
- Batch worker (Java) that calls the Temporal service batch operation API (activity on a separate task queue).
Use this project to experiment with high‑volume workflow creation and mass cancellation strategies on Temporal Cloud.
-
Work service (Python):
- Task queue
work-task-queue. - Workflow
CancelableWorkflowspawns manyChildWorkflowinstances. - All workflows upsert search attribute
WorkloadId = "1"by default.
- Task queue
-
Canceler service (Python):
- Task queue
canceler-task-queue. - Workflow
BulkCancelWorkflowcoordinates cancellation:- Triggers a Java activity
batch_cancel_workflowson task queuebatch-queue. - Loops to find new workflows started after the batch request time and terminates them in parallel.
- Polls until no matching workflows remain running.
- Triggers a Java activity
- Uses an oversize payload codec that writes large payloads under
/tmp/payloads.
- Task queue
-
Batch worker (Java):
- Task queue
batch-queue. - Activity
batch_cancel_workflowsissues a service batch termination with a visibility query.
- Task queue
Namespace topology:
- "Main" namespace hosts the high‑volume workflows (work service).
- "Canceler" namespace hosts the canceler workflow and the Java activity worker that polls
batch-queue. - The Java activity implementation connects to the main namespace when issuing StartBatchOperation so it can target the spawned workflows. Ensure both namespaces are correctly set in
.envand in the Java worker if you change defaults.
src/
work/
activity.py # generate_uuid activity
workflow.py # CancelableWorkflow (parent) + ChildWorkflow
worker.py # Work service worker (task queue: work-task-queue)
run.py # Starts a CancelableWorkflow instance
cancel.py # Sends cancel signal to the parent workflow (optional)
config.py # Work service config (reads from .env)
canceler/
activity.py # query, bulk terminate, confirm
workflow.py # BulkCancelWorkflow orchestration
worker.py # Canceler worker (task queue: canceler-task-queue)
run.py # Starts BulkCancelWorkflow
payload_manager.py # Simple oversize payload codec (/tmp/payloads)
config.py # Canceler service config (reads from .env)
main Java sources (batch worker)
src/main/java/com/testcanceler/
worker/WorkerStarter.java # Starts worker on task queue "batch-queue"
activity/WorkflowBatchActivities.java # Activity interface
activity/WorkflowBatchActivitiesImpl.java# Activity implementation (batch termination)
gradle/ build files
build.gradle, settings.gradle, gradlew*
app/ # Unrelated sample Gradle app
pyproject.toml # Python deps
.env # Environment variables (fill with your own values)
Key Python:
src/work/workflow.py– Spawns load and addsWorkloadIdsearch attribute.src/canceler/workflow.py– Batch trigger → new‑workflows cleanup → confirm loop.src/canceler/activity.py– Query, parallel terminate, and confirm helpers.
Key Java:
src/main/java/com/testcanceler/worker/WorkerStarter.java– Starts the batch activity worker.src/main/java/com/testcanceler/activity/WorkflowBatchActivitiesImpl.java– Calls StartBatchOperation with a visibility query.
- Python 3.13 (see
.python-version). - Java 21 and Gradle (wrapper included).
- Temporal Cloud namespace and API keys.
Install Python dependencies with uv or pip:
# Using uv
uv venv
source .venv/bin/activate
uv pip install -e .
# Or using pip
python -m venv .venv
source .venv/bin/activate
pip install -e .
This repo centralizes most settings in Python via src/work/config.py and src/canceler/config.py, which read from .env.
Set these variables in .env at the repo root:
-
Work service (Python):
TEMPORAL_MAIN_ADDRESS– e.g.,us-east-1.aws.api.temporal.io:7233TEMPORAL_MAIN_NAMESPACE– your namespace for spawning workflowsTEMPORAL_MAIN_TASK_QUEUE– e.g.,work-task-queueTEMPORAL_API_KEY_MAIN– API key with access to the above namespace
-
Canceler service (Python):
TEMPORAL_CANCELER_ADDRESS– usually same as main addressTEMPORAL_CANCELER_NAMESPACE– namespace where canceler workflow runs (often same as main)TEMPORAL_CANCELER_TASK_QUEUE– e.g.,canceler-task-queueTEMPORAL_API_KEY_CANCELER– API key for the canceler
-
Optional tuning:
CANCEL_CONCURRENCY– max parallel terminates (default 750)CONFIRM_TIMEOUT_SECONDS– confirm loop timeout (default 240)CONFIRM_POLL_SECONDS– confirm loop poll interval (default 5) These are set in the config files located inside the work and canceler packages.
Notes:
- The Java activity and Python canceler must target the same Temporal namespace as the spawned workloads (typically the "main" namespace) for visibility queries and terminations to succeed.
- Search attribute used is a keyword
WorkloadIdwith default value "1"; adjust insrc/work/config.py(WORKLOAD_ID_VALUE) and ensure Java/Python queries match. - The
.envcommitted here contains example keys; replace with your own and avoid committing secrets.
Payload codec:
src/canceler/payload_manager.pystores large payloads under/tmp/payloads. Create the directory if needed:mkdir -p /tmp/payloads.
- Prepare environment
cp .env .env.local # optional copy; edit values
source .venv/bin/activate
- Start the Python work worker (terminal A)
python src/work/worker.py
- Start the workflow snowball. This can grow as large as 2M+ workflows. (terminal B)
python src/work/run.py
# Starts parent CancelableWorkflow with many children/grandchildren on work-task-queue
- Start the Java batch worker (terminal C)
./gradlew build
./gradlew run
After this step, wait for the workflow execution snowball to grow as large as you'd like.
- Start the Python canceler worker (terminal D)
python src/canceler/worker.py
- Start the cancellation orchestration (terminal E)
python src/canceler/run.py
# Triggers Java batch cancel, cleans up new workflows, confirms all canceled
-
Work service
CancelableWorkflowspawns manyChildWorkflowinstances with concurrency control andParentClosePolicy.TERMINATE.- Each child upserts
WorkloadIdto enable query‑based targeting.
-
Canceler service
- Kicks off
batch_cancel_workflows(Java activity onbatch-queue) which issues a StartBatchOperation with a visibility query (by defaultWorkloadId = "1" AND ExecutionStatus = "Running"). - Tracks the batch start time and repeatedly executes
query_new_wf_executionsto find workflows withStartTime >= batchStartTimeand the sameWorkloadId, then callsbulk_cancel_workflowsto terminate them in parallel. - Calls
confirm_all_canceledto poll until no running workflows remain for theWorkloadId.
- Kicks off
- No workflows found: ensure all workers/clients share the same namespace and
WorkloadIdvalue. - 429/rate limiting: lower
CANCEL_CONCURRENCY. - Confirmation never finishes: increase
CONFIRM_TIMEOUT_SECONDSand verify your visibility query matches status + attribute value. - Payload codec errors: ensure
/tmp/payloadsexists and is writable.
- Namespaces are hard‑coded in several files; unify them or switch to reading from
.envfor all clients/workers. - Java
WorkerStartercurrently hard‑codes the canceler namespace; consider reading it from.envlike the activity implementation does for the main namespace. - Some concurrency settings in Python workers are set very high for laptop demos; tune down for real environments.
track_batchinsrc/canceler/activity.pyis a WIP and not wired.- Consider adding
.envto.gitignoreand rotating any committed secrets.
No license specified.