Summary
There is no minimum iteration cost or rate limit on df.loop(). An unconditional loop with no meaningful work calls continue_as_new as fast as duroxide will allow, consuming CPU on the background worker and generating unbounded history entries.
Severity
Low — requires a specific (unrealistic) usage pattern; cannot happen accidentally in well-formed workflows. However, there is no safeguard if a user accidentally constructs such a loop.
Repro
-- DO NOT run without being ready to cancel immediately
SELECT df.start(df.loop(df.sleep(0)), 'bug13-busy-loop');
SELECT df.cancel('<instance_id>', 'stop busy loop');
Notes
- The pattern
df.loop(df.sleep(0)) calls continue_as_new on every iteration with no pause.
- Any loop that lacks a break condition and uses a zero or very small sleep has the same problem.
- The issue is not specific to
df.sleep(0); any body that completes instantaneously has the same effect.
Recommendation
- Documentation: clearly document that all loop bodies should either call
df.break() or contain a df.sleep() with a reasonable delay.
- Optional code fix: enforce a minimum per-
continue_as_new delay in the loop handler, or emit a warning when sleep_seconds == 0 inside a loop.
Summary
There is no minimum iteration cost or rate limit on
df.loop(). An unconditional loop with no meaningful work callscontinue_as_newas fast as duroxide will allow, consuming CPU on the background worker and generating unbounded history entries.Severity
Low — requires a specific (unrealistic) usage pattern; cannot happen accidentally in well-formed workflows. However, there is no safeguard if a user accidentally constructs such a loop.
Repro
Notes
df.loop(df.sleep(0))callscontinue_as_newon every iteration with no pause.df.sleep(0); any body that completes instantaneously has the same effect.Recommendation
df.break()or contain adf.sleep()with a reasonable delay.continue_as_newdelay in the loop handler, or emit a warning whensleep_seconds == 0inside a loop.