Skip to content

Fence Backfiller tasks by generation - #11311

Merged
yiminc merged 7 commits into
temporalio:mainfrom
chaptersix:sch-074
Aug 7, 2026
Merged

Fence Backfiller tasks by generation#11311
yiminc merged 7 commits into
temporalio:mainfrom
chaptersix:sch-074

Conversation

@chaptersix

@chaptersix chaptersix commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What changed?

  • Fence Backfiller tasks with a persisted task sequence value instead of comparing task execution time with the backfill HWM.
  • Accept unnumbered tasks created by an older binary and restore task numbering when a new binary executes one.
  • Add lifecycle coverage and CHASM test support for firing due persisted pure tasks.

Why?

LastProcessedTime tracks progress through the requested schedule range, while a task's ScheduledTime controls when that task runs. Comparing the two can either keep an already-processed historical task valid or reject a forward-dated task before it runs.

How did you test it?

  • covered by existing tests
  • added new unit test(s)

Potential risks

The immediate task N created with a Backfiller is scheduled and executed in one transaction. Mixed-version risk begins with delayed task N+1.

If N+1 is handled by a 160 binary:

  • For a forward-dated backfill, the old HWM validator can remove N+1 before execution, leaving the Backfiller alive with no task to make further progress.
  • For a historical backfill, the old validator can accept N+1 again after it has already advanced the HWM, so duplicate execution remains possible during rollout or rollback.

@chaptersix

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 7e4c3c06d2

ℹ️ 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".

@chaptersix chaptersix changed the title fix(scheduler): fence Backfiller tasks by generation, not range cursor Fence Backfiller tasks by generation Jul 28, 2026
Comment thread chasm/lib/scheduler/backfiller_lifecycle_test.go
@chaptersix
chaptersix marked this pull request as ready for review July 28, 2026 20:31
@chaptersix
chaptersix requested review from a team as code owners July 28, 2026 20:31
@chaptersix
chaptersix requested a review from awln-temporal July 28, 2026 20:31

@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: 18eea9ba96

ℹ️ 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".

Comment thread chasm/lib/scheduler/backfiller_tasks.go Outdated
Comment on lines +65 to +67
if taskGeneration == 0 {
// An old binary schedules generation-zero tasks and advances only Attempt.
valid = currentGeneration == 0 || attempt >= currentGeneration

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 Keep forward backfills alive across old-binary handoff

During a rolling upgrade, a forward-dated backfill can still stall before this compatibility branch is reached: if an old binary receives the generation-bearing continuation, its old Validate ignores the generation and calls validateTaskHighWaterMark; because LastProcessedTime is in the future while the delayed task uses wall-clock time, it rejects and removes the continuation without scheduling the generation-zero successor assumed here. The mixed-version test only increments Attempt and therefore skips this real old-validator path; preserve a continuation across that handoff or explicitly gate mixed-version processing.

Useful? React with 👍 / 👎.

Comment thread chasm/lib/scheduler/backfiller_lifecycle_test.go Outdated
@chaptersix
chaptersix marked this pull request as draft July 30, 2026 21:24
LastProcessedTime is a schedule-range cursor and cannot identify stale task deliveries. Stamp Backfiller tasks with a generation so superseded and redelivered tasks are rejected, while preserving generation-zero continuations during rolling upgrades.
@chaptersix
chaptersix marked this pull request as ready for review August 4, 2026 19:13
@chaptersix
chaptersix requested a review from a team August 4, 2026 19:13
@chaptersix

Copy link
Copy Markdown
Contributor Author

@codex review

@chaptersix
chaptersix marked this pull request as draft August 4, 2026 19:18
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 0c1391a904

ℹ️ 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".

@chaptersix
chaptersix marked this pull request as ready for review August 4, 2026 21:03
int64 attempt = 8;

// Fence shared with the currently scheduled BackfillerTask.
int64 task_generation = 9;

@davidporter-id-au davidporter-id-au Aug 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you mind adding a bit more context as to the purpose of this field:

  • When it gets incremented, what should increment it, what incrementing indicates
  • The intent of the field - what it's present to guard against
  • How is this different from attempt count?

Re naming: just a consistency check, do we use the term 'generation' to refer to a monotonic counter for this purpose? I don't have strong feelings, but I've heard version used like this, it's not clear to me if this is any clearer.

I guess version avoids the dual meanings that generation implies (as a kind of age-related cohort, versus the act of creating a new thing; to generate)? I don't feel very strongly about the naming thing though.

message BackfillerTask {}
message BackfillerTask {
// Generation used to fence superseded tasks.
int64 generation = 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same as above

Comment thread chasm/lib/scheduler/backfiller_tasks.go
taskStamp := task.GetStamp()
currentStamp := backfiller.GetTaskStamp()
attempt := backfiller.GetAttempt()
valid := taskStamp == currentStamp && currentStamp > attempt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry it took me so long to get my head around this. Nonblocking, we can fix / land this later if it's useful.

A simplification (not from me) that might be easier:

// A stamped task is outstanding while TaskStamp is ahead of Attempt. Attempt
// catches up to TaskStamp when a task executes without scheduling a successor
// (the Backfiller completed), and when an older binary executes a stamped task
// without advancing TaskStamp.
outstanding := currentStamp > attempt

var valid bool
if taskStamp == 0 {
    // An old binary schedules zero-stamp tasks and advances only Attempt.
    // Honour one only while no stamped task is outstanding to supersede it.
    valid = !outstanding
} else {
    valid = outstanding && taskStamp == currentStamp
}

@davidporter-id-au davidporter-id-au added the reliability-2026 Reliability related changes label Aug 7, 2026
@davidporter-id-au

Copy link
Copy Markdown
Contributor

I tested the case with multiple backfillers also, seems good, so I'm not overly worried about that. We can follow up with some additional testing later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reliability-2026 Reliability related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants