Documentation · Technical Documentation

Cron Events And Scheduler

This document describes the WP-Cron events that the ADP Car Market Hub plugin schedules, the custom intervals it registers and the locking strategy that prevents overlapping runs.

When to use this document

Read this document if you need to:

  • Configure automatic imports in the Automation admin tab.
  • Set up an external system cron to trigger imports reliably.
  • Diagnose missed, overlapping or stuck import runs.
  • Plan production deployments where WP-Cron is disabled in favour of a real cron job.

For per-listing import logic, see Import Engine. For image queue mechanics, see Image Importer And Queue.

Overview

The plugin uses two complementary trigger paths:

  • WP-Cron — WordPress's built-in pseudo-cron, fired by frontend page views. The plugin registers cron hooks and custom intervals here.
  • REST cron endpointGET /wp-json/as24ci/v1/cron-import, authenticated by a token, lets a real system cron trigger imports even on low-traffic sites.

Both paths call the same shared runner, AS24CI\Scheduler::run_import(), which acquires a transient lock, delegates to the importer, optionally performs full-sync deletes, schedules the image-queue worker and records the run.

Cron hooks registered by the plugin

AS24CI\Scheduler is constructed during AS24CI\Plugin bootstrap and registers the following WordPress hooks:

HookBound toPurpose
cron_schedulesScheduler::add_cron_intervals()Register the as24ci_every_6_hours and as24ci_custom intervals.
as24ci_scheduled_importScheduler::run_scheduled_import()Main import event. Calls run_import('wp-cron').
as24ci_image_queue_processScheduler::run_image_queue()Image-queue worker (see Image Importer And Queue).
Ai_Assistant::AI_QUEUE_HOOKAi_Assistant::process_ai_queue (static)Background AI generation worker. Bound unconditionally so the callback always exists; scheduling is gated by AI options.

In addition, the plugin's main file (adp-car-market-hub.php) registers a separate cron_schedules filter that adds the as24ci_every_5_minutes interval (300 seconds) unconditionally — useful for the AI queue and any custom integrations that need a sub-hourly cadence.

Other plugin subsystems schedule their own events independently:

  • as24ci_daily_cleanup — daily analytics retention cleanup, owned by AS24CI\Analytics. Scheduled on activation and removed on deactivation.
  • Pricing engine cron — daily, owned by AS24CI\Pricing_Engine (hook tag as24ci_pricing_analysis_cron). Scheduled when the feature is enabled and cleared on deactivation.
  • Data Quality Scanner — runs on its own schedule (hook tag as24ci_automated_taxonomy_scan, owned by AS24CI\Data_Quality_Scanner) controlled by as24ci_dq_scan_frequency and as24ci_dq_scan_time.
  • AI queue worker — hook tag as24ci_process_ai_queue, owned by AS24CI\Ai_Assistant. Bound unconditionally; scheduled on the as24ci_every_5_minutes interval only when the AI Assistant is enabled, and cleared on deactivation.
  • License re-validation — hook tag as24ci_license_refresh, owned by AS24CI\License_Manager. Scheduled daily on activation (and via an admin_init self-heal) and cleared on deactivation. This is the authoritative daily refresh of the ADP Car Market Hub license / feature-rights state.

Custom intervals

Interval keyLengthRegistered in
as24ci_every_5_minutes300 sadp-car-market-hub.php (always available).
as24ci_every_6_hours6 hScheduler::add_cron_intervals().
as24ci_customN minutesScheduler::add_cron_intervals(). N comes from as24ci_cron_custom_minutes and is clamped to a minimum of 15.

Standard WordPress intervals (hourly, twicedaily, daily) are also accepted by the import schedule.

Scheduling the main import event

The main import event is as24ci_scheduled_import, scheduled by AS24CI\Scheduler::reschedule(). The method:

  1. Calls unschedule() to clear all existing occurrences of the hook.
  2. Returns immediately if as24ci_auto_import_enabled is 0 (no event is scheduled).
  3. Reads as24ci_cron_schedule (default hourly). Allowed values: hourly, as24ci_every_6_hours, twicedaily, daily, as24ci_custom.
  4. Computes the next-run timestamp via calculate_next_run(): - For daily and twicedaily, uses the configured as24ci_cron_start_time (HH:MM, 24 h) interpreted in the WordPress site timezone. If today's start time has already passed, the next run is pushed to tomorrow. - For all other schedules, the next run is now.
  5. Calls wp_schedule_event( $next_run, $schedule, 'as24ci_scheduled_import' ).

The scheduler never schedules more than one occurrence of the hook; unschedule() clears every pending entry before reschedule() adds a new one.

The shared runner

AS24CI\Scheduler::run_import( string $source = 'manual' ) is the single entry point used by:

  • The as24ci_scheduled_import cron hook (source: wp-cron).
  • The REST cron endpoint (source: rest).
  • The admin "Run now" button (source: manual).

The runner:

  1. Acquires the transient lock as24ci_cron_import_running (Scheduler::LOCK_TRANSIENT) with a TTL of Scheduler::LOCK_TTL. A stale lock (older than the TTL) is automatically cleared.
  2. For cron and REST sources, raises the PHP execution time limit to 300 seconds via set_time_limit(300).
  3. Reads as24ci_cron_max_vehicles (default 50) and as24ci_cron_image_queue (default 1).
  4. Calls Importer::set_cron_image_queue( true|false ) accordingly.
  5. Iterates over the configured seller IDs and calls Importer::import_all_for_seller() for each, accumulating counts and the full listing_ids set seen this run. Stops as soon as the vehicle limit is reached.
  6. If full-sync is enabled (as24ci_full_sync = 1) and the run was not aborted by the limit, calls Importer::full_sync_after_import( $remote_ids ). The full-sync call is a no-op if the remote ID set is empty (safety guard).
  7. Schedules the image-queue worker (maybe_schedule_image_queue_worker).
  8. Releases the lock and resets cron-image-queue mode in the finally block so manual subsequent calls are unaffected.
  9. Records as24ci_last_run_time and as24ci_last_run_status (counts array { inserted, updated, skipped, errors, sync_deleted, sync_kept, api_active }).
  10. Invalidates the dashboard "API status" transient (Admin_Page::TRANSIENT_API_STATUS) so dashboard cards refresh on the next page load.

The runner returns array{ success: bool, message: string, counts: array<string,int> }.

REST cron endpoint

AS24CI\Cron_Endpoint::register_routes() registers a single GET route:

  • GET /wp-json/as24ci/v1/cron-import

Authentication is performed inside the handler. The token is read from as24ci_cron_token. The endpoint:

  • Refuses requests when no token is configured (403, with a message asking the administrator to generate one in the admin UI).
  • Accepts the token via either the Authorization: Bearer <token> header (preferred — keeps the token out of server access logs) or the ?token=<token> query parameter.
  • Calls Scheduler::run_import( 'rest' ) on success.

The plugin also tracks "external cron is alive" pings independently of the REST endpoint:

  • Visiting any URL with ?as24ci_cron=1 updates as24ci_last_external_cron_run to the current Unix timestamp via Cron_Endpoint::record_external_ping() (hooked on init).

Locks and concurrency

The plugin uses two short-lived transients to prevent overlapping work:

TransientOwnerTTLPurpose
as24ci_cron_import_runningSchedulerScheduler::LOCK_TTL (~40 min)Single-run lock for run_import().
as24ci_image_queue_runningSchedulerScheduler::IMAGE_QUEUE_LOCK_TTLSingle-run lock for the queue worker.

A stale lock (older than the TTL) is automatically cleared by the next runner, so a process that died mid-run does not block future imports indefinitely. Manual recovery (deleting the transient via WP-CLI or a database tool) is only needed if you want to force a new run earlier than the TTL allows.

Configuration reference

OptionEffectDefault
as24ci_auto_import_enabledMaster toggle for the cron-driven import.0
as24ci_cron_scheduleOne of hourly, as24ci_every_6_hours, twicedaily, daily, as24ci_custom.hourly
as24ci_cron_start_timeHH:MM (24 h, site timezone). Used by daily / twicedaily.06:00
as24ci_cron_custom_minutesMinutes for the as24ci_custom schedule. Clamped to >= 15.30
as24ci_cron_modewp-cron or server-cron. Drives admin UI hints.wp-cron
as24ci_cron_tokenToken for the REST cron endpoint.(none)
as24ci_cron_max_vehiclesVehicle cap per cron / REST run. 0 = unlimited.50
as24ci_cron_image_queueUse the image queue during cron / REST runs.1
as24ci_full_syncDelete local vehicles missing from the API after each import.0
as24ci_last_run_timeUnix timestamp of the last completed run.0
as24ci_last_run_statusCounts array from the last run.(empty)
as24ci_last_external_cron_runUnix timestamp of the last ?as24ci_cron=1 ping.0

The Scheduler::LOCK_TTL and image-queue worker constants are defined in code; verify the current values in the source if you need exact numbers.

Step by step instructions

Enabling automated imports through WP-Cron

  1. Open the Automation admin tab.
  2. Set Cron mode to wp-cron.
  3. Set Auto import to enabled (as24ci_auto_import_enabled = 1).
  4. Choose a schedule. For daily / twicedaily, set the start time. For as24ci_custom, set the interval (minimum 15 minutes).
  5. Save. The Import & Limits tab calls Scheduler::reschedule() to register the next run.
  6. (Optional) Lower Vehicles per cron run for very large catalogues so individual runs stay within the host's PHP time limit.

Triggering imports through a system cron

  1. Switch Cron mode to server-cron so the admin UI shows server-cron hints.
  2. (Recommended) Disable WP-Cron in wp-config.php:
  • Set define( 'DISABLE_WP_CRON', true );. WordPress will then only run scheduled events when something explicitly triggers them.
  1. Generate a token in the Import & Limits screen. The plugin stores it in as24ci_cron_token.
  2. Configure your system cron to call the REST endpoint, for example every hour:
   0 * * * * curl -fsS -H "Authorization: Bearer YOUR_TOKEN" \
     "https://example.com/wp-json/as24ci/v1/cron-import?as24ci_cron=1"

The optional ?as24ci_cron=1 query parameter also updates the "external cron is alive" timestamp shown in the admin status.

  1. Verify the response. A successful run returns a JSON payload with counts; a 403 indicates a missing or wrong token.

Operational notes

  • WP-Cron is fired by site traffic. On low-traffic sites scheduled events may run late or not at all. Use a real system cron in production.
  • The set_time_limit(300) call may be ignored by hosts that enforce PHP time limits. The plugin logs a warning in that case.
  • The as24ci_full_sync option is opt-in for safety. With it enabled, vehicles whose listings disappear from the API are permanently deleted (their imported attachments included). Make sure the configured seller IDs cover the entire catalogue you intend to keep online.
  • Deactivating the plugin clears all of its scheduled events (as24ci_scheduled_import, the analytics cleanup, the AI queue schedule, the pricing-engine schedule and the as24ci_license_refresh event — plus the legacy as24ci_competitor_watcher_cron hook) and removes the as24ci_cron_import_running transient.
  • The plugin never hard-codes external cron URLs or tokens. The example above uses a placeholder; substitute your own host and a generated token before deploying.

Troubleshooting

  • Imports do not run automatically. Verify as24ci_auto_import_enabled = 1, that a schedule is selected and that wp_next_scheduled( 'as24ci_scheduled_import' ) returns a future timestamp. If the value is false, save the Automation tab again to call reschedule().
  • Import already in progress is logged. The single-run lock is held by another runner. Wait until the lock TTL expires (the next runner will auto-clear a stale lock) or delete the as24ci_cron_import_running transient via WP-CLI for an immediate retry.
  • No seller IDs configured is returned. Set as24ci_seller_ids in the API settings.
  • Daily schedules fire at the wrong time. The plugin uses the WordPress site timezone (wp_timezone()). Verify the timezone in Settings → General, not the server's local time.
  • REST endpoint returns 403. Either the token is missing (as24ci_cron_token empty — the response message confirms this) or the token in the request does not match. Generate a fresh token in the admin UI.
  • External cron looks dead in the admin status. Check that the URL you call includes ?as24ci_cron=1 (or that the REST cron endpoint succeeds), and that the request reaches the WordPress site (no firewall, no auth wall). The plugin updates as24ci_last_external_cron_run on every successful ping.
  • Image queue does not drain after a cron import. This is a separate worker. See the troubleshooting section in Image Importer And Queue.