Do not arm a scheduled step of a module held as found on an adopted node (hq ADR 0103)

This commit is contained in:
2026-09-22 19:48:05 +02:00
parent da008460ac
commit dc861fb4a8
3 changed files with 68 additions and 7 deletions
+13 -1
View File
@@ -86,7 +86,11 @@ func NewScheduler(clock Clock, run Runner, log func(string)) *Scheduler {
// A job whose declaration is unchanged keeps its place in the cadence — its next due time and
// whether a run is in flight — so an ordinary reconcile every few minutes does not keep resetting
// the clock out from under a schedule and prevent it ever firing.
func (s *Scheduler) Sync(d *declaration.Declaration) {
// held is the ids this node holds as found — what an adopted node keeps until its module is taken
// (novox/hq ADR 0100). A step of a module not yet taken is not armed: run on its cadence it would
// work on the predecessor's data, under the predecessor's service, which is the one thing an
// adopted node must not do. Nil on a converged node, where nothing is held.
func (s *Scheduler) Sync(d *declaration.Declaration, held map[string]bool) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -96,6 +100,14 @@ func (s *Scheduler) Sync(d *declaration.Declaration) {
if !ok || c.Schedule == "" {
continue
}
if module, untaken := d.Adoption.UntakenModuleOf(c.Identity()); untaken || held[c.Identity()] {
if module == "" {
module = "its module"
}
s.log(fmt.Sprintf("scheduled step %s: not armed while %s is held as found on this node",
c.Identity(), module))
continue
}
cron, err := declaration.ParseCron(c.Schedule)
if err != nil {
// The declaration parser already refused a malformed cron before this runs, so a