Skip to content

Preserve a paused deployment schedule on redeploy#22396

Open
apoorva-01 wants to merge 2 commits into
PrefectHQ:mainfrom
apoorva-01:fix/19302-preserve-disabled-schedule-on-redeploy
Open

Preserve a paused deployment schedule on redeploy#22396
apoorva-01 wants to merge 2 commits into
PrefectHQ:mainfrom
apoorva-01:fix/19302-preserve-disabled-schedule-on-redeploy

Conversation

@apoorva-01

@apoorva-01 apoorva-01 commented Jun 29, 2026

Copy link
Copy Markdown

closes #19302

Pause a deployment schedule, re-.deploy() without passing active, and it silently turns back on (only for slug-less schedules).

Two things combine, so the fix is on both sides. The client always sent active=True when the caller didn't set it, so the exclude_unset=True payload still carried it. And on update the server rebuilds slug-less schedules from scratch and defaulted active to True. (OSS resets too; the slug workaround in the issue looks Cloud-specific.)

What changed

active is left unset when the caller doesn't pass it (the deployment-schedule builders and the Schedule/Cron/Interval/RRule factories), so an update no longer forces True. On update the server keeps the existing schedule's active when none is given, matched by slug or, for slug-less ones, by serialized definition. New/changed schedules still default to active; an explicit active= still wins. Added tests for the paused-on-redeploy case (slug + slug-less, client + server); existing suites pass.

Design note — your call

This flips the default of Schedule.active (and the factory active params) from True to None. Create behavior is unchanged (unset still resolves to True on create); None just means "leave active alone on update", but it's a public default change. The bigger alternative is fully idempotent reconciliation, larger and overlapping with the slug-rename work in #19298.

@github-actions github-actions Bot added the bug Something isn't working label Jun 29, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ef8a3d586

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"""
if slug:
return f"slug:{slug}"
definition = schedule.model_dump_json() if schedule is not None else "null"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid matching on generated interval anchors

Matching slug-less schedules on the full schedule.model_dump_json() makes preservation depend on fields that Prefect regenerates during deployment construction. For a common redeploy like flow.deploy(..., interval=3600) or Interval(3600) without an explicit anchor_date, the client creates a fresh anchor timestamp each time, so the serialized definition no longer matches the stored schedule and _resolve_schedule_active falls back to True. In that case a schedule paused in the UI is still silently reactivated on redeploy even though active was omitted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apoorva-01 this review comment will need to be addressed before we can merge these changes

@apoorva-01 apoorva-01 Jul 11, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anchor_date (and the injected rrule DTSTART) get regenerated every build, so the full-dump key never matched on redeploy and the paused schedule flipped back on.

Fixed in 75dbbb2, matching on the recurrence definition without those fields, plus an interval regression test.

@codspeed-hq

codspeed-hq Bot commented Jun 29, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 8 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing apoorva-01:fix/19302-preserve-disabled-schedule-on-redeploy (75dbbb2) with main (c7f899b)

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

Re-deploying without an explicit active silently re-activated a paused
slug-less schedule: the client always sent active=True and the server
defaulted it to True on update. Leave active unset and inherit it.
@apoorva-01 apoorva-01 force-pushed the fix/19302-preserve-disabled-schedule-on-redeploy branch from 8ef8a3d to 18b4d90 Compare July 2, 2026 02:53
The previous match keyed on the full serialized schedule, which includes
fields Prefect regenerates on each deploy: an interval schedule's
anchor_date and the DTSTART injected into rrule strings. A redeploy of an
unchanged `Interval(3600)` therefore failed to match and reactivated a
paused schedule. Key on the recurrence definition with those regenerated
fields excluded instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deployment schedule does not maintain state without schedule slug

2 participants