Skip to content

feat: index every driver's events in the cron option, migrate between drivers - #2

Merged
ogorzalka merged 5 commits into
mainfrom
feat/cron-index
Oct 13, 2026
Merged

ogorzalka merged 5 commits into
mainfrom
feat/cron-index

Conversation

@ogorzalka

@ogorzalka ogorzalka commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Step 3 of the pollora/schedule delivery plan (package side). Stacked on #1: the base is feat/action-scheduler-driver; retarget to main before deleting that branch.

What it does

  • The cron option indexes every event with the driver that keeps it (driver key; no key = WP-Cron's own). wp_next_scheduled(), Site Health, WP Crontrol and Query Monitor read it with no query. pre_get_scheduled_event is no longer filtered.
  • WP-Cron never runs a marked entry (pre_get_ready_cron_jobs); a direct write to the option that removes or moves one reaches its driver (pre_update_option_cron).
  • Action Scheduler's reports (stored, completed, failed, cancelled; own group only) keep the index current: Contracts\ReportsChanges, optional, so ScheduleDriver is unchanged and the package stays in 0.1.x.
  • Migrator moves pending events between drivers in both directions, target first then source, interval from the entry, --dry-run, safe to rerun. ScheduleEngine::reindex() rebuilds the index from a driver.
  • WP-CLI: wp cron event list extended (type, driver, filters), wp pollora schedule migrate.
  • Schedule::boot(): the framework starts before WordPress, so wiring waits for add_filter().

Found while testing live

wp cron event run reschedules then unschedules the old time. On a marked recurring event that cancelled the Action Scheduler action. The interceptor now remembers the pair and treats the second step as "stays". Without this, the index would have exposed Action Scheduler events to a command that destroys them.

Tests

88 Pest tests, PHPStan level 5 clean. Also run on pollora-test with WooCommerce's Action Scheduler: events scheduled through wp_schedule_event() are indexed and listed with their driver; wp cron event run keeps a recurring event and consumes a single one; a due Action Scheduler event is not in wp_get_ready_cron_jobs(); a direct write removing an entry cancels the action; a 23-event round trip WP-Cron → Action Scheduler → WP-Cron leaves the cron option byte-identical and a second run moves nothing.

Not covered live: Site Health's late-event check (it reads the same array, so it depends on the index being current).

Added after a review of the first push

  • A stale entry no longer stops rescheduling. The index mirrors a driver; an action deleted from Tools › Scheduled Actions left an entry that wp_next_scheduled() believed, so a recurring #[Schedule] was never created again. ScheduleEngine::next() now asks the driver that keeps a marked event and drops the entry when it has none (a driver that is not ready is trusted). A deleted action also reconciles the index once, when the request ends. WordPress's own wp_next_scheduled() still reads the entry until the engine or a deletion hook has run: that is the price of reading the option with no query.
  • Index writes read from the database first (wp_cache_delete('alloptions'|'cron', 'options')), so a long request (Action Scheduler's queue runner) does not write back its start-of-request copy over what others scheduled meanwhile. Run live with two processes (one sleeping 12 s over a stale copy): both entries survive. Core's own writes keep their own race.
  • A site that never left WP-Cron hooks into nothing. The guard and the Action Scheduler watchers are installed at init, only when the index holds an entry another driver keeps. This departs from nothing in the study: with WP-Cron and no leftover, no filter is posted.
  • Schedule::knows().

Tests: 94 Pest, PHPStan clean; Tests and Coding Standards re-run on the branch by workflow_dispatch (the workflows only trigger on PRs to main): green.

The 'cron' option holds each event with the driver that keeps it, so
wp_next_scheduled(), Site Health, WP Crontrol and Query Monitor see events
Action Scheduler runs. WP-Cron no longer runs what another driver keeps, a
direct write to the option reaches the driver, and Action Scheduler's own
reports keep the index current. A migrator moves events between drivers,
in both directions, with a dry run.
…Press loads

wp cron event list gains type and driver and their filters, and running an
event another driver keeps leaves its schedule alone. wp pollora schedule
migrate moves events between drivers, with --dry-run. An application that
starts before WordPress chooses the driver at once and the engine hooks into
WordPress as soon as it can.
…dexed event

wp cron event run reschedules the event, then unschedules its old time. For an
event another driver keeps, that pair now leaves it scheduled. Documents the
index and the migration.
…resh read

The index mirrors a driver's events, so an action deleted from Tools › Scheduled
Actions left an entry that wp_next_scheduled() believed and that kept a recurring
event from ever being scheduled again: the engine now asks the driver that keeps
an event, and drops the entry when the driver has none. A deleted action also
reconciles the index when the request ends. An index write reads the option from
the database first, so a long request does not undo what others scheduled since.
With WP-Cron as the driver, the guard and the watchers are installed at init, and
only when another driver still keeps events.
@ogorzalka
ogorzalka changed the base branch from feat/action-scheduler-driver to main October 13, 2026 12:44
@ogorzalka
ogorzalka merged commit 8dc1430 into main Oct 13, 2026
6 checks passed
@ogorzalka
ogorzalka deleted the feat/cron-index branch October 13, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant