Gil
04/20/2026, 3:45 PMMarvin
04/20/2026, 3:45 PMMarvin
04/20/2026, 3:48 PMcache_key=null)
- This matches what you’re seeing.
References:
- Transactions
- Caching
- States (RolledBack)
2) Is using transaction(commit_mode=EAGER) at the call site idiomatic?
- Yes. Wrapping the critical call(s) in an EAGER transaction is the intended way to force early persistence of task results and cache records, independent of the parent’s eventual success/failure.
Example:
from prefect.transactions import transaction, CommitMode
with transaction(commit_mode=CommitMode.EAGER):
batch_id = submit_batch_task(...)
- You can scope this narrowly around just the side-effectful or expensive step(s) whose cache/result you need to survive a crash.
There are no per-task or flow-level decorator options to set commit mode. Commit mode is controlled via explicit transaction contexts.
3) Any sharp edges with an EAGER child inside a LAZY parent?
- Expected/normal behavior:
- The EAGER child commits immediately on exiting its context; those results and caches persist even if the parent later rolls back or crashes.
- Caveats to be aware of:
- Partial commit: if the parent later fails, you can end up with some steps persisted (the EAGER ones) and others not. Use EAGER only where you want that durability.
- Process-kill window: if the pod is killed before the EAGER block exits, the commit still won’t occur. Keeping the EAGER block small reduces this window.
- Rollback hooks on the parent will not undo already-committed EAGER results or external side effects.
- Make sure the EAGER’d tasks are idempotent or harmless to re-use via cache on retry.
4) For long-running flows needing crash-recovery caching — should I rely on external idempotency instead?
- Prefer a combination:
- Use provider-level idempotency for external side effects (e.g., OpenAI idempotency keys) so duplicates are naturally rejected.
- Use Prefect task caching with persist_result=True plus a stable cache_policy, and wrap critical steps in EAGER transactions so the cache persists mid-run.
- Optionally restructure long workflows into smaller, independently-executing flow runs (e.g., subflows as separate deployments) to narrow transactional blast radius and commit durable boundaries more frequently.
Practical patterns:
- Wrap only the submission step in EAGER:
with transaction(commit_mode=CommitMode.EAGER):
batch_id = submit_batch_task(...)
- Persist the submission receipt (e.g., batch_id) to a durable store (GCS/DB) that you can re-read on retry to avoid re-submission.
- Use OpenAI (or other API) idempotency tokens keyed on your inputs so a double-submit is a no-op server-side.
If you want, I can help sketch a minimal example showing:
- First attempt submits once under EAGER (persisted)
- Parent crashes
- Second attempt reads the cache or external receipt and skips resubmission
Docs for further reading:
- Transactions
- Caching
- Cache workflow steps
- States (including RolledBack)