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 endpoint —
GET /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:
| Hook | Bound to | Purpose |
|---|---|---|
cron_schedules | Scheduler::add_cron_intervals() | Register the as24ci_every_6_hours and as24ci_custom intervals. |
as24ci_scheduled_import | Scheduler::run_scheduled_import() | Main import event. Calls run_import('wp-cron'). |
as24ci_image_queue_process | Scheduler::run_image_queue() | Image-queue worker (see Image Importer And Queue). |
Ai_Assistant::AI_QUEUE_HOOK | Ai_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 byAS24CI\Analytics. Scheduled on activation and removed on deactivation.- Pricing engine cron — daily, owned by
AS24CI\Pricing_Engine(hook tagas24ci_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 byAS24CI\Data_Quality_Scanner) controlled byas24ci_dq_scan_frequencyandas24ci_dq_scan_time. - AI queue worker — hook tag
as24ci_process_ai_queue, owned byAS24CI\Ai_Assistant. Bound unconditionally; scheduled on theas24ci_every_5_minutesinterval only when the AI Assistant is enabled, and cleared on deactivation. - License re-validation — hook tag
as24ci_license_refresh, owned byAS24CI\License_Manager. Scheduleddailyon activation (and via anadmin_initself-heal) and cleared on deactivation. This is the authoritative daily refresh of the ADP Car Market Hub license / feature-rights state.
Custom intervals
| Interval key | Length | Registered in |
|---|---|---|
as24ci_every_5_minutes | 300 s | adp-car-market-hub.php (always available). |
as24ci_every_6_hours | 6 h | Scheduler::add_cron_intervals(). |
as24ci_custom | N minutes | Scheduler::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:
- Calls
unschedule()to clear all existing occurrences of the hook. - Returns immediately if
as24ci_auto_import_enabledis0(no event is scheduled). - Reads
as24ci_cron_schedule(defaulthourly). Allowed values:hourly,as24ci_every_6_hours,twicedaily,daily,as24ci_custom. - Computes the next-run timestamp via
calculate_next_run(): - Fordailyandtwicedaily, uses the configuredas24ci_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. - 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_importcron hook (source:wp-cron). - The REST cron endpoint (source:
rest). - The admin "Run now" button (source:
manual).
The runner:
- Acquires the transient lock
as24ci_cron_import_running(Scheduler::LOCK_TRANSIENT) with a TTL ofScheduler::LOCK_TTL. A stale lock (older than the TTL) is automatically cleared. - For cron and REST sources, raises the PHP execution time limit
to 300 seconds via
set_time_limit(300). - Reads
as24ci_cron_max_vehicles(default50) andas24ci_cron_image_queue(default1). - Calls
Importer::set_cron_image_queue( true|false )accordingly. - Iterates over the configured seller IDs and calls
Importer::import_all_for_seller()for each, accumulating counts and the fulllisting_idsset seen this run. Stops as soon as the vehicle limit is reached. - If full-sync is enabled (
as24ci_full_sync = 1) and the run was not aborted by the limit, callsImporter::full_sync_after_import( $remote_ids ). The full-sync call is a no-op if the remote ID set is empty (safety guard). - Schedules the image-queue worker
(
maybe_schedule_image_queue_worker). - Releases the lock and resets cron-image-queue mode in the
finallyblock so manual subsequent calls are unaffected. - Records
as24ci_last_run_timeandas24ci_last_run_status(counts array{ inserted, updated, skipped, errors, sync_deleted, sync_kept, api_active }). - 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=1updatesas24ci_last_external_cron_runto the current Unix timestamp viaCron_Endpoint::record_external_ping()(hooked oninit).
Locks and concurrency
The plugin uses two short-lived transients to prevent overlapping work:
| Transient | Owner | TTL | Purpose |
|---|---|---|---|
as24ci_cron_import_running | Scheduler | Scheduler::LOCK_TTL (~40 min) | Single-run lock for run_import(). |
as24ci_image_queue_running | Scheduler | Scheduler::IMAGE_QUEUE_LOCK_TTL | Single-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
| Option | Effect | Default |
|---|---|---|
as24ci_auto_import_enabled | Master toggle for the cron-driven import. | 0 |
as24ci_cron_schedule | One of hourly, as24ci_every_6_hours, twicedaily, daily, as24ci_custom. | hourly |
as24ci_cron_start_time | HH:MM (24 h, site timezone). Used by daily / twicedaily. | 06:00 |
as24ci_cron_custom_minutes | Minutes for the as24ci_custom schedule. Clamped to >= 15. | 30 |
as24ci_cron_mode | wp-cron or server-cron. Drives admin UI hints. | wp-cron |
as24ci_cron_token | Token for the REST cron endpoint. | (none) |
as24ci_cron_max_vehicles | Vehicle cap per cron / REST run. 0 = unlimited. | 50 |
as24ci_cron_image_queue | Use the image queue during cron / REST runs. | 1 |
as24ci_full_sync | Delete local vehicles missing from the API after each import. | 0 |
as24ci_last_run_time | Unix timestamp of the last completed run. | 0 |
as24ci_last_run_status | Counts array from the last run. | (empty) |
as24ci_last_external_cron_run | Unix 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
- Open the Automation admin tab.
- Set Cron mode to
wp-cron. - Set Auto import to enabled
(
as24ci_auto_import_enabled = 1). - Choose a schedule. For
daily/twicedaily, set the start time. Foras24ci_custom, set the interval (minimum 15 minutes). - Save. The Import & Limits tab calls
Scheduler::reschedule()to register the next run. - (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
- Switch Cron mode to
server-cronso the admin UI shows server-cron hints. - (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.
- Generate a token in the Import & Limits screen. The plugin
stores it in
as24ci_cron_token. - 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.
- Verify the response. A successful run returns a JSON payload with
counts; a
403indicates 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_syncoption 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 theas24ci_license_refreshevent — plus the legacyas24ci_competitor_watcher_cronhook) and removes theas24ci_cron_import_runningtransient. - 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 thatwp_next_scheduled( 'as24ci_scheduled_import' )returns a future timestamp. If the value isfalse, save the Automation tab again to callreschedule(). Import already in progressis 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 theas24ci_cron_import_runningtransient via WP-CLI for an immediate retry.No seller IDs configuredis returned. Setas24ci_seller_idsin 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_tokenempty — 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 updatesas24ci_last_external_cron_runon 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.