Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0589a99
Clean up references/core/ai-integration.md
donald-pinckney Feb 13, 2026
22d90c5
Clean up references/core/common-gotchas.md
donald-pinckney Feb 13, 2026
a32152b
Clean up references/core/common-gotchas.md
donald-pinckney Feb 13, 2026
9ce8441
Clean up references/core/determinism.md
donald-pinckney Feb 13, 2026
519b2ea
Clean up references/core/determinism.md
donald-pinckney Feb 13, 2026
49c449a
Update error-reference.md
donald-pinckney Feb 17, 2026
d2a4af3
Update interactive-workflows.md
donald-pinckney Feb 17, 2026
e62865a
Clean up patterns.md
donald-pinckney Feb 17, 2026
324b532
Cut shell scripts
donald-pinckney Feb 17, 2026
5d3f79f
Edit troubleshooting.md
donald-pinckney Feb 17, 2026
19a9589
remove interceptors for now
donald-pinckney Feb 18, 2026
761b220
remove dynamic workflows
donald-pinckney Feb 18, 2026
4d032f4
clarify on heartbeating of async activity completions, and prompt it …
donald-pinckney Feb 18, 2026
8d77164
Improve references/python/advanced-features.md
donald-pinckney Feb 18, 2026
ceb40a0
Use explicit namespace in connect
donald-pinckney Feb 18, 2026
c61bc64
remove duplicated content from determinism.md, clean up
donald-pinckney Feb 18, 2026
236eab2
Improve references/python/data-handling.md
donald-pinckney Feb 18, 2026
43b1ed7
Prefer start_to_close_timeout
donald-pinckney Feb 18, 2026
cc593e3
don't explicitely provide defaults for retry policies
donald-pinckney Feb 18, 2026
24f0e3e
error-handling.md cleanup
donald-pinckney Feb 18, 2026
18cb1e8
move idempotency patterns to patterns.md
donald-pinckney Feb 18, 2026
03e8706
remove multi-param activities
donald-pinckney Feb 18, 2026
3f7dc9d
small edits
donald-pinckney Feb 18, 2026
9163fa2
Unify sandbox stuff into one file
donald-pinckney Feb 18, 2026
4c288cf
local activities aren't experimental
donald-pinckney Feb 18, 2026
b8b47ca
Clean up references/python/sync-vs-async.md
donald-pinckney Feb 18, 2026
9bc285f
Cleanup observability.md, remove duplicated search attributes
donald-pinckney Feb 19, 2026
f455d39
Cut otel for now
donald-pinckney Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move idempotency patterns to patterns.md
  • Loading branch information
donald-pinckney committed Feb 18, 2026
commit 18cb1e867673a209b0fd91c4bb503d9c375ae424
70 changes: 70 additions & 0 deletions references/core/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,75 @@ This will enable the Activity to be retried exactly on the set interval.

**Advantage:** Individual Activity retries are not recorded in Workflow History, so this approach can poll for a very long time without affecting the history size.

## Idempotency Patterns

**Purpose**: Ensure activities can be safely retried and replayed without causing duplicate side effects.

**Why It Matters**: Temporal may re-execute activities during retries (on failure) or replay (on worker restart). Without idempotency, this can cause duplicate charges, duplicate emails, duplicate database entries, etc.

### Using Idempotency Keys

Pass a unique identifier to external services so they can detect and deduplicate repeated requests:

```
Activity: charge_payment(order_id, amount)
└── Call payment API with:
amount: $100
idempotency_key: "order-{order_id}"
└── Payment provider deduplicates based on key
(second call with same key returns original result)
```

**Good idempotency key sources**:
- Workflow ID (unique per workflow execution)
- Business identifier (order ID, transaction ID)
- Workflow ID + activity name + attempt number

### Check-Before-Act Pattern

Query the external system's state before making changes:

```
Activity: send_welcome_email(user_id)
├── Check: Has welcome email been sent for user_id?
│ │
│ ├── YES: Return early (already done)
│ │
│ └── NO: Send email, mark as sent
```

### Designing Idempotent Activities

1. **Use unique identifiers** as idempotency keys with external APIs
2. **Check before acting**: Query current state before making changes
3. **Make operations repeatable**: Ensure calling twice produces the same result
4. **Record outcomes**: Store transaction IDs or results for verification
5. **Leverage external system features**: Many APIs (Stripe, AWS, etc.) have built-in idempotency key support

### Tracking State in Workflows

For complex multi-step operations, track completion status in workflow state:

```
Workflow State:
payment_completed: false
shipment_created: false

Run:
if not payment_completed:
charge_payment(...)
payment_completed = true

if not shipment_created:
create_shipment(...)
shipment_created = true
```

This ensures that on replay, already-completed steps are skipped.

## Choosing Between Patterns

| Need | Pattern |
Expand All @@ -271,3 +340,4 @@ This will enable the Activity to be retried exactly on the set interval.
| Rollback on failure | Saga |
| Process items concurrently | Parallel Execution |
| Long-lived stateful entity | Entity Workflow |
| Safe retries/replays | Idempotency |
51 changes: 0 additions & 51 deletions references/python/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,57 +119,6 @@ class MyWorkflow:

**Note:** Do not use `non_retryable=` with `ApplicationError` inside a worklow (as opposed to an activity).

## Idempotency Patterns

When Activities interact with external systems, making them idempotent ensures correctness during retries and replay.

### Using Workflow IDs as Idempotency Keys

```python
from temporalio import activity

@activity.defn
async def charge_payment(order_id: str, amount: float) -> str:
# Use order_id as idempotency key with payment provider
result = await payment_api.charge(
amount=amount,
idempotency_key=f"order-{order_id}", # Prevents duplicate charges
)
return result.transaction_id
```

### Tracking Operation Status in Workflow State

```python
from datetime import timedelta
from temporalio import workflow

@workflow.defn
class OrderWorkflow:
def __init__(self):
self._payment_completed = False
self._transaction_id: str | None = None

@workflow.run
async def run(self, order: Order) -> str:
if not self._payment_completed:
self._transaction_id = await workflow.execute_activity(
charge_payment, order.id, order.total,
start_to_close_timeout=timedelta(minutes=5),
)
self._payment_completed = True

# Continue with order processing...
return self._transaction_id
```

### Designing Idempotent Activities

1. **Use unique identifiers** as idempotency keys (workflow ID, activity ID, or business ID)
2. **Check before acting**: Query external system state before making changes
3. **Make operations repeatable**: Ensure calling twice produces the same result
4. **Record outcomes**: Store transaction IDs or results for verification

## Best Practices

1. Use specific error types for different failure modes
Expand Down