Skip to content

Add specific time scheduling to Preload#1026

Draft
kraftbj wants to merge 9 commits into
trunkfrom
add/wpsc-preload-schedule
Draft

Add specific time scheduling to Preload#1026
kraftbj wants to merge 9 commits into
trunkfrom
add/wpsc-preload-schedule

Conversation

@kraftbj
Copy link
Copy Markdown
Contributor

@kraftbj kraftbj commented Apr 7, 2026

Summary

  • Add a "Scheduler" option to the Preload settings with "Timer" (interval-based) and "Clock" (specific time) modes
  • Port the "Clock" UI selector from Garbage Collection settings to the Preload tab for consistency
  • Update scheduling logic in wp_cron_preload_cache() and wpsc_preload_settings() to handle both interval and time-based scheduling

This allows server administrators to schedule resource-intensive preloading during off-peak hours (e.g., 03:00 AM) to minimize impact on site performance.

Originally proposed by @zeus2611 in Automattic/jetpack#47018.
Fixes #1017

Test plan

  • Navigate to Settings > WP Super Cache > Preload tab
  • Verify a "Scheduler" section with "Timer" and "Clock" radio buttons is present
  • Test Clock mode: select Clock, enter a time, save, and verify wp_cache_preload_hook is scheduled at the correct time (e.g., via WP Crontrol)
  • Test Timer mode: select Timer, enter an interval, save, and confirm the hook is scheduled based on the interval
  • Verify switching between modes reschedules correctly

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a scheduler to WP Super Cache’s Preload feature so admins can run preloading either on a repeating timer or at a specific clock time (similar to the Garbage Collection scheduler).

Changes:

  • Introduces new Preload scheduling settings (interval vs time, scheduled time, cron interval).
  • Updates wpsc_preload_settings() and wp_cron_preload_cache() to schedule either single events (timer) or recurring cron events (clock).
  • Adds Preload UI controls for selecting Timer/Clock mode and configuring time + interval.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
wp-cache.php Implements new schedule settings persistence and cron scheduling behavior for interval/time modes.
wp-cache-config-sample.php Adds sample defaults for the new preload scheduling variables.
partials/preload.php Adds the Scheduler UI (Timer/Clock) to the Preload settings tab.
Comments suppressed due to low confidence (1)

wp-cache.php:3720

  • In time-based scheduling mode, $cache_max_time is still derived from $wp_cache_preload_interval (minutes). If the user switches to Clock mode but leaves the timer value at a small/default value, the cleanup step may delete cached files much sooner than the next scheduled preload run. Consider deriving $cache_max_time from the Clock interval (e.g., the selected cron schedule duration) when $preload_schedule_type === 'time'.
		global $file_prefix, $cache_max_time;
		if ( $wp_cache_preload_interval > 0 ) {
			$cache_max_time = (int)$wp_cache_preload_interval * 60; // fool the GC into expiring really old files
		} else {
			$cache_max_time = 86400; // fool the GC into expiring really old files
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread partials/preload.php Outdated
Comment thread partials/preload.php Outdated
Comment thread wp-cache.php Outdated
Comment thread wp-cache.php
Comment on lines +4181 to +4188
if ( $preload_schedule_type === 'interval' ) {
// Interval-based: schedule single event X minutes from now
wp_schedule_single_event( time() + ( $wp_cache_preload_interval * 60 ), 'wp_cache_full_preload_hook' );
} else {
// Time-based: schedule recurring event at specific time
if ( ! isset( $preload_scheduled_time ) ) {
$preload_scheduled_time = '00:00';
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

Time-based scheduling uses wp_schedule_event( strtotime( $preload_scheduled_time ), ...), but strtotime('HH:MM') is interpreted in the server/PHP timezone and can resolve to a timestamp in the past (causing the event to run immediately rather than at the next intended time). Since the UI says “UTC”, it would be safer to compute the next future UTC timestamp explicitly (e.g., based on current_time()/wp_timezone() and advancing to tomorrow when needed).

Suggested change
if ( $preload_schedule_type === 'interval' ) {
// Interval-based: schedule single event X minutes from now
wp_schedule_single_event( time() + ( $wp_cache_preload_interval * 60 ), 'wp_cache_full_preload_hook' );
} else {
// Time-based: schedule recurring event at specific time
if ( ! isset( $preload_scheduled_time ) ) {
$preload_scheduled_time = '00:00';
}
// Time-based: schedule recurring event at a specific UTC time.
if ( ! isset( $preload_scheduled_time ) ) {
$preload_scheduled_time = '00:00';
}
if ( ! isset( $preload_schedule_interval ) ) {
$preload_schedule_interval = 'daily';
}
$preload_time_parts = explode( ':', $preload_scheduled_time, 2 );
$preload_hour = isset( $preload_time_parts[0] ) ? (int) $preload_time_parts[0] : 0;
$preload_minute = isset( $preload_time_parts[1] ) ? (int) $preload_time_parts[1] : 0;
if ( $preload_hour < 0 || $preload_hour > 23 ) {
$preload_hour = 0;
}
if ( $preload_minute < 0 || $preload_minute > 59 ) {
$preload_minute = 0;
}
$preload_utc_now = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
$preload_utc_next = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
$preload_utc_next->setTime( $preload_hour, $preload_minute, 0 );
if ( $preload_utc_next->getTimestamp() <= $preload_utc_now->getTimestamp() ) {
$preload_utc_next->modify( '+1 day' );
}
wp_schedule_event( $preload_utc_next->getTimestamp(), $preload_schedule_interval, 'wp_cache_full_preload_hook' );

Copilot uses AI. Check for mistakes.
Comment thread wp-cache.php Outdated
Comment on lines +3703 to +3712
if ( ! isset( $preload_schedule_interval ) ) {
$preload_schedule_interval = 'daily';
}
$schedules = wp_get_schedules();
$interval_display = isset( $schedules[ $preload_schedule_interval ]['display'] ) ? $schedules[ $preload_schedule_interval ]['display'] : $preload_schedule_interval;
if ( $wp_cache_preload_email_me )
/* translators: 1: scheduled time, 2: schedule interval display name */
$msg = sprintf( __( 'Scheduling next preload at %1$s (%2$s).', 'wp-super-cache' ), $preload_scheduled_time, $interval_display );
wp_cache_debug( "wp_cron_preload_cache: no more posts. scheduling next preload at $preload_scheduled_time ($preload_schedule_interval).", 5 );
wp_schedule_event( strtotime( $preload_scheduled_time ), $preload_schedule_interval, 'wp_cache_full_preload_hook' );
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

When rescheduling in time mode, $preload_schedule_interval is used directly in wp_schedule_event(...) without verifying it still exists in wp_get_schedules(). If a custom schedule was removed after the setting was saved, scheduling will fail and preloading may stop. Consider falling back to a known interval (e.g., daily) when the configured interval is missing.

Copilot uses AI. Check for mistakes.
kraftbj and others added 7 commits April 7, 2026 17:27
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Display input time in the site's configured timezone instead of UTC.
- Convert the entered HH:MM to the next UTC timestamp via a shared helper,
  advancing to the next day when the chosen time has already passed today.
- Fix a stray/malformed preg_match line left over from the previous commit
  and tighten HH:MM validation to reject out-of-range values.
@donnchawp
Copy link
Copy Markdown
Contributor

This review was generated by AI.

Reviewed the scheduling state machine, since converting wp_cache_full_preload_hook from a self-rescheduling single event to a recurring event in "time" mode is the risky part. Findings below; I verified the load-bearing ones against the code.

Context

Two cron hooks both fire wp_cron_preload_cache (wp-cache.php:3748-3749): wp_cache_preload_hook (per-batch loop) and wp_cache_full_preload_hook (full-preload kickoff). The PR makes the latter recurring in "time" mode but only updates some of the paths that manage it.

Blocker

wpsc_cancel_preload() cannot cancel a recurring time-mode preloadwp-cache.php:3990

wp_unschedule_event( $next_full_preload, 'wp_cache_full_preload_hook' );

wp_unschedule_event() removes only the next occurrence. For the recurring event that "time" mode now creates, all future occurrences survive — so clicking "Cancel Cache Preload" leaves preloading to resume at the next wall-clock time. wpsc_cancel_preload() isn't touched by this PR, even though wp_clear_scheduled_hook() was added to the reschedule path in wpsc_preload_settings(). Same fix needed here:

wp_clear_scheduled_hook( 'wp_cache_full_preload_hook' ); // handles single + recurring

The preload_off flow routes through wpsc_cancel_preload(), so fixing this one function covers both entry points.

Cleanup (not a blocker)

End-of-run reschedule block in "time" mode is dead code — in wp_cron_preload_cache()'s else-branch, the guard ! wp_next_scheduled( 'wp_cache_full_preload_hook' ) is always false while the recurring event is alive, so the wp_schedule_event() there never runs. No double-scheduling occurs (the guard holds), but the block is misleading. In time mode the recurring event self-fires — do nothing at end-of-run and drop that wp_schedule_event() call.

Warnings

  • .attr('checked', true).prop('checked', true) in the new inline script (partials/preload.php). In jQuery 1.6+, .attr doesn't reliably toggle the radio's checked property, so clicking the time field or interval dropdown can silently fail to select its radio.
  • Missing phpcs:ignore WordPress.Security.NonceVerification.Missing on the preload_scheduled_time $_POST block, while its two siblings have it — this will fail the changed-lines PHPCS lint in CI.
  • Mode switch mid-preload (interval → time) leaves orphaned wp_cache_preload_hook loop events; the reschedule path only clears wp_cache_full_preload_hook.
  • wpsc_enable_preload() ("Preload Now") is not mode-aware — it always fires a single event. It currently survives via the wp_next_scheduled guard, but it's fragile alongside the recurring registration.

Not issues

Timezone handling in wpsc_next_scheduled_preload_timestamp() is sound — createFromFormat('H:i') inheriting today's date is intended, and modify('+1 day') is DST-safe. The regex-then-substr fallback is unreachable but harmless.

Suggestion

The root cause is that "convert to recurring" wasn't applied uniformly across the schedule / cancel / enable paths. Consider centralizing into a single wpsc_schedule_preload() / wpsc_unschedule_preload() pair that both modes route through, so these paths can't diverge again.

@donnchawp donnchawp added the needs-triage Maintainer needs to evaluate this issue label May 27, 2026
@donnchawp donnchawp self-assigned this May 27, 2026
@donnchawp donnchawp added ready-for-human Requires human implementation and removed needs-triage Maintainer needs to evaluate this issue labels May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Requires human implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhancement:Time specific preload in wp-super cache

4 participants