Skip to content

Registry needs a non-persisting adopt/declare path for consumers with many persisted routines #548

Description

@chubes4

Problem

WP_Agent_Routine_Registry is in-memory per request, so consumers with persisted routine sets must call Registry::register() on every boot. Each call fires wp_agent_routine_registered, and the default bridge-sync listener forwards it to WP_Agent_Routine_Action_Scheduler_Bridge::register(), which unconditionally:

  1. mints and persists a fresh generation option,
  2. as_unschedule_all_actions() for the routine,
  3. as_schedule_recurring_action() / as_schedule_cron_action().

For a consumer with a large persisted fleet (Data Machine converges ~700 scheduled flows onto routines in Extra-Chill/data-machine#3475), naive per-request re-declaration rewrites every AS chain on every request — thousands of pointless writes per request, plus generation churn that defeats the purpose of fencing (each re-register invalidates all in-flight actions).

There is currently no supported way to say "adopt this routine into the registry for this request without (re)scheduling it if the backend already owns an equivalent schedule".

What consumers are forced to do today

Install a backend decorator via the wp_agent_routine_backend filter that implements WP_Agent_Routine_Backend by delegation and no-ops register() when a consumer-persisted schedule fingerprint (trigger + target + input hash) matches what it last scheduled. This works (that is exactly what the DM PR above ships as an interim consumer-side cache), but it:

  • duplicates scheduling-state bookkeeping in every heavyweight consumer,
  • requires a verification-mode escape hatch so Registry::reconcile() repairs are not swallowed by the hash gate (a no-op register() inside reconcile would leave genuinely missing schedules unrepaired),
  • risks divergence if the bridge's registration semantics change.

Proposal

Some combination of:

  1. Coverage-aware registration — a register() mode (or separate adopt()/declare() method) that adds the routine to the in-memory registry and asks the backend to schedule it only when no pending action exists for the routine id (the bridge already has exact-match coverage queries; this is one indexed lookup, not the bulk pending_by_routine() scan).
  2. A documented reconciliation-only register contract — guarantee that reconcile()'s internal backend->register() calls are distinguishable from boot-time re-declaration, so consumer-side gating cannot accidentally block reconcile repairs.

Either would let consumers drop the decorator entirely: boot becomes Registry::declare( $id, $args ) per routine with backend work happening only for genuinely new/changed schedules, and reconcile() remains the sole drift-repair path.

Context

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions