کرون وردپرس، همان لایه‌ای است که وقتی از کار می‌افتد، کسی متوجه نمی‌شود تا وقتی که یک بکاپ هفتگی از دست رفته یا خبرنامه‌ای که باید برای هزار مشتری ارسال شود، هرگز نرسیده باشد. من در پروژه‌های واقعی چند بار با این سناریو روبرو شده‌ام: صاحب سایت شکایت دارد که «بکاپ خودکار من دو ماه است اجرا نشده» و در بررسی مشخص می‌شود سایت ترافیک کمی داشته و WP-Cron هیچ‌وقت بیدار نشده. این داستان، هم برای تازه‌کارها رخ می‌دهد و هم برای توسعه‌دهندگانی که سال‌ها با وردپرس کار کرده‌اند ولی هرگز به مکانیزم درونی WP-Cron دقت نکرده‌اند.

اگر تازه با مفاهیم پایه آشنا می‌شوید، پیش از ادامه افزونه وردپرس چیست و چگونه افزونه مناسب انتخاب کنیم را بخوانید. این نوشته، لایه زیرساختی همان بحث است: مکانیزمی که وردپرس برای زمان‌بندی کارها استفاده می‌کند و در مدیریت داده‌های موقت در وردپرس به‌عنوان یکی از چهار دسته داده موقت معرفی شد. در رفع مشکلات کرون در وردپرس هم مسیر عیب‌یابی را جداگانه باز کرده‌ام؛ این مقاله، نگاه عمیق به طراحی و انتخاب الگو است.

کرون وردپرس دقیقاً چیست؟

WP-Cron یک مکانیزم زمان‌بندی رویدادها است که در هسته وردپرس تعبیه شده تا وظایف دوره‌ای سایت — مثل بررسی به‌روزرسانی‌های هسته، اجرای بکاپ‌های خودکار، پاک‌سازی ترنزینت‌های منقضی و ارسال ایمیل‌های خبرنامه — در بازه‌های مشخص اجرا شوند. نکته کلیدی که این مکانیزم را از نمونه‌های مشابه در سایر سیستم‌ها متمایز می‌کند، نحوه بیدار شدنش است: WP-Cron برای اجرای وظایف، به ترافیک سایت وابسته است. یعنی هر بار کاربری وارد سایت می‌شود، وردپرس چک می‌کند که آیا وظیفه‌ای برای اجرا رسیده است یا نه.

این طراحی که در ابتدا برای کاهش پیچیدگی راه‌اندازی وردپرس انتخاب شد، در عمل هم مزیت دارد هم دام. مزیتش این است که شما به‌عنوان کاربر وردپرس، لازم نیست با cron سرور یا پنل هاست درگیر شوید. دامش این است که سایت‌های کم‌ترافیک، عملاً هیچ‌وقت وظایفشان را اجرا نمی‌کنند. تجربه میدانی من این است که این دام، منبع بی‌سروصدای بسیاری از باگ‌های «چرا این کار انجام نشد» در پروژه‌های واقعی است. اگر با معماری وردپرس آشنا نیستید، وردپرس چیست و چگونه شروع به کار با آن کنیم دید کلی خوبی می‌دهد.

WP-Cron نامش cron است ولی ذاتاً cron نیست؛ یک سیستم زمان‌بندی رویداد است که به ترافیک کاربران وابسته است.

مکانیزم WP-Cron: چرا واقعاً cron نیست؟

برای درک عمیق WP-Cron، باید مکانیزم بیدار شدنش را دقیق بدانید. Cron سیستمی (مثل cron در لینوکس) یک فرایند دائمی است که همیشه در پس‌زمینه اجرا می‌شود و دقیقاً در زمان مشخص، کار مورد نظر را انجام می‌دهد. WP-Cron چنین چیزی نیست. WP-Cron یک آرایه از رویدادهای زمان‌بندی‌شده است که در جدول wp_options با کلید cron ذخیره می‌شود و به‌طور مداوم بررسی می‌کند که آیا رویدادی به زمان اجرایش رسیده یا نه.

زنجیره بیدار شدن WP-Cron

هر بار که یک درخواست به سایت وردپرس می‌رسد — چه بازدید کاربر عادی، چه درخواست یک ربات، چه حتی فراخوانی یک فایل PHP — وردپرس در پایان پردازش درخواست، تابع spawn_cron() را فراخوانی می‌کند. این تابع یک درخواست HTTP غیرهمگام (Asynchronous) به فایل wp-cron.php می‌فرستد که در ریشه نصب وردپرس قرار دارد. آن فایل، جداگانه اجرا می‌شود و رویدادهای رسیده را یکی‌یکی اجرا می‌کند. این طراحی، از کند شدن درخواست اصلی کاربر جلوگیری می‌کند، ولی نقطه ضعف بزرگی هم دارد: اگر ترافیک سایت پایین باشد، تعداد درخواست‌ها به حدی نیست که رویدادها به‌موقع اجرا شوند.

تابع wp_doing_cron و محدودیت‌هایش

از وردپرس ۵.۱ به بعد، تابع wp_doing_cron() اضافه شده که به شما می‌گوید آیا درخواست فعلی در حال اجرای یک رویداد cron است یا نه. این تابع در افزونه‌های حرفه‌ای بسیار مفید است چون به شما اجازه می‌دهد رفتار افزونه را در حین اجرای cron تغییر دهید:

if ( wp_doing_cron() ) {
    // رفتار ویژه در زمان اجرای cron
    return;
}

یک کاربرد کلاسیک این تابع، جلوگیری از اجرای کوئری سنگین یا ارسال ایمیل در زمان بازدید کاربر است. اگر افزونه شما ایمیل تأیید سفارش می‌فرستد و می‌خواهید فقط در cron ارسال شود، این تابع نقطه تمایز است. اگر به هوک‌های وردپرس آشنایی کمتری دارید، نحوه استفاده صحیح از هوک‌های وردپرس پیش‌نیاز خوبی است.

ثابت DISABLE_WP_CRON

اگر می‌خواهید WP-Cron را غیرفعال کنید — مثلاً چون به cron سیستمی مهاجرت کرده‌اید — این ثابت در فایل wp-config.php کار را انجام می‌دهد:

define( 'DISABLE_WP_CRON', true );

هشدار مهم: این ثابت را نباید بدون جایگزین فعال کنید. اگر آن را true کنید و cron سیستمی هم تنظیم نکنید، هیچ‌کدام از وظایف دوره‌ای سایت اجرا نمی‌شوند و بعد از چند هفته به مشکلات انبار شدن داده موقت برمی‌خورید. باید قبل از فعال‌سازی این ثابت، cron سیستمی را راه‌اندازی کرده باشید. الگوی درست این مهاجرت را در بخش جایگزینی با cron سیستمی همین مقاله توضیح می‌دهم.

تفاوت WP-Cron با cron سیستمی

یکی از سؤالات پرتکرار در پروژه‌ها این است که «تفاوت WP-Cron با cron سیستمی چیست و کدام را استفاده کنم؟» این سؤال پاسخ ساده‌ای ندارد چون هر کدام برای سناریوی متفاوتی ساخته شده‌اند. جدول زیر تفاوت‌های عملی را خلاصه می‌کند:

ویژگیWP-CronCron سیستمی
زمان‌بندی دقیقوابسته به ترافیکدقیق و ثابت
نیاز به تنظیمات سرورندارددارد
مناسب برای سایت‌های کم‌ترافیکخیربله
مناسب برای هاست اشتراکیبلهمحدود
مصرف منابع در ترافیک بالابالامستقل از ترافیک
قابل مشاهده در پنل هاستخیربله

قاعده تجربی من: سایت‌های شخصی و وبلاگ‌های کم‌ترافیک، با WP-Cron راحت‌ترند چون مدیریتشان ساده‌تر است. سایت‌های فروشگاهی، خبری و هر سایتی که ترافیک قابل توجه یا وظایف زمان‌بندی حساس دارد، باید به cron سیستمی مهاجرت کنند. انتخاب هاست در این تصمیم اثر مستقیم دارد چون بعضی پلن‌ها اجازه اجرای cron سیستمی نمی‌دهند. برای درک این محدودیت، هاست چیست و چگونه انتخاب درستی داشته باشیم مفصل بحث کرده است.

انتخاب بین WP-Cron و cron سیستمی، یک تصمیم معماری است نه سلیقه؛ اگر سایت شما منبع درآمد دارد، WP-Cron تنها گذاشته‌اش نکنید.

زمان‌بندی کارها: توابع اصلی و الگوها

هسته وردپرس برای زمان‌بندی کارها چهار تابع اصلی در اختیار شما می‌گذارد که هر کدام نقش مشخصی دارند. درک دقیق این چهار تابع، پیش‌نیاز هر پیاده‌سازی حرفه‌ای است.

wp_schedule_event و زمان‌بندی تکرارشونده

این تابع برای زمان‌بندی رویدادهایی که باید در بازه‌های مشخص تکرار شوند استفاده می‌شود:

if ( ! wp_next_scheduled( 'myplugin_daily_cleanup' ) ) {
    wp_schedule_event(
        strtotime( 'tomorrow 03:00' ),
        'daily',
        'myplugin_daily_cleanup'
    );
}

نکته مهم در این الگو، شرط wp_next_scheduled است. بدون این شرط، هر بار که فایل functions.php یا افزونه لود شود، یک رویداد تکراری ثبت می‌شود و در جدول wp_options ردیف‌های تکراری انبار می‌شود. این خطا یکی از رایج‌ترین دام‌هاست که در رفع مشکلات کرون در وردپرس مفصل به آن پرداخته‌ام.

wp_schedule_single_event و زمان‌بندی یک‌باره

برای رویدادهایی که فقط یک بار در تاریخ مشخص باید اجرا شوند:

wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'myplugin_send_report' );

این تابع در سناریوهای متعددی کاربرد دارد: ارسال یادآوری به کاربر پس از ثبت‌نام، حذف داده موقت پس از چند ساعت، اجرای یک‌بار پاک‌سازی. یک نکته ظریف که در پروژه‌های واقعی به آن برخورده‌ام: اگر می‌خواهید یک رویداد یک‌باره را با آرگومان‌های متفاوت زمان‌بندی کنید، وردپرس به‌طور پیش‌فرض رویدادهای با نام یکسان را تکرار نمی‌کند. برای تفکیک، باید نام رویداد را با آرگومان یا شناسه یکتا ترکیب کنید.

wp_clear_scheduled_hook و پاک‌سازی

هنگام غیرفعال‌سازی یا حذف افزونه، تمام رویدادهای cron اختصاصی آن باید پاک شوند:

function myplugin_deactivate() {
    $timestamp = wp_next_scheduled( 'myplugin_daily_cleanup' );
    if ( $timestamp ) {
        wp_unschedule_event( $timestamp, 'myplugin_daily_cleanup' );
    }

    wp_clear_scheduled_hook( 'myplugin_daily_cleanup' );
}
register_deactivation_hook( __FILE__, 'myplugin_deactivate' );

این الگو، نقطه تفاوت بین افزونه حرفه‌ای و آماتور است. افزونه‌ای که هنگام غیرفعال شدن، cron خودش را پاک نکند، در سایت مشتری رویدادهای یتیم جا می‌گذارد که برای همیشه باقی می‌مانند. این دقیقاً همان الگویی است که در ساختار استاندارد یک افزونه حرفه‌ای وردپرس به‌عنوان یک ستون معماری معرفی کرده‌ام.

بازه‌های زمانی سفارشی

وردپرس به‌طور پیش‌فرض سه بازه زمانی hourly، twicedaily و daily را ارائه می‌دهد. اگر بازه دیگری می‌خواهید — مثل هر پنج دقیقه یا هر هفته — باید آن را با فیلتر cron_schedules اضافه کنید:

add_filter( 'cron_schedules', function ( $schedules ) {
    $schedules['five_minutes'] = [
        'interval' => 5 * MINUTE_IN_SECONDS,
        'display'  => __( 'Every 5 Minutes', 'myplugin' ),
    ];
    return $schedules;
} );

توجه: بازه‌های کوتاه‌تر از پنج دقیقه در WP-Cron معنا ندارد چون عملاً هر بازدید کاربر می‌تواند باعث اجرای چندباره رویداد شود. اگر نیاز به زمان‌بندی دقیق‌تر دارید، cron سیستمی تنها گزینه درست است. برای درک اثر این تصمیم روی سرعت سایت، تاثیر هاست بر سرعت سایت چقدر است تحلیل دقیقی دارد.

ماندگاری رویدادها در دیتابیس

رویدادهای WP-Cron در جدول wp_options با کلید cron ذخیره می‌شوند. ساختار داده‌ای این ردیف، یک آرایه سریالایز شده است که هر رویداد در آن با timestamp به‌عنوان کلید و نام هوک به‌عنوان مقدار ظاهر می‌شود. درک این ساختار در زمان دیباگ بسیار مفید است چون می‌توانید مستقیماً از دیتابیس، وضعیت رویدادها را ببینید:

SELECT option_value
FROM wp_options
WHERE option_name = 'cron';

خروجی این کوئری، یک رشته طولانی سریالایز است که در نگاه اول غیرقابل خواندن به نظر می‌رسد. برای دیباگ راحت‌تر، از WP-CLI استفاده می‌کنم:

wp cron event list
wp cron event run myplugin_daily_cleanup
wp cron event schedule myplugin_daily_cleanup now daily

این دستورها در پروژه‌های واقعی، ابزار روزانه من هستند. بدون WP-CLI، دیباگ WP-Cron در سایت‌های پیچیده تقریباً غیرممکن می‌شود. اگر با مدیریت سرور و خط فرمان آشنا نیستید، VPS چیست و چه تفاوتی با هاست اشتراکی دارد تفاوت دسترسی‌ها را روشن می‌کند.

چرا کوتاه بودن آرگومان‌ها اهمیت دارد

آرایه cron با هر انتشار، با هر ثبت رویداد جدید، دوباره سریالایز می‌شود و در دیتابیس نوشته می‌شود. اگر آرگومان‌های رویداد بزرگ باشند — مثلاً آرایه‌ای با هزار عضو — این سریالایز و دیسریالایز می‌تواند به‌طور محسوسی روی عملکرد سایت اثر بگذارد. قاعده‌ای که در همه پروژه‌ها رعایت می‌کنم: آرگومان‌های cron را تا حد امکان کوچک نگه دارید. اگر نیاز به انتقال داده بزرگ دارید، آن را در یک Transient یا option جدا ذخیره کنید و به‌عنوان شناسه کوچک، فقط یک کلید به cron منتقل کنید.

معمای سایت‌های کم‌ترافیک

یکی از پرتکرارترین سؤالات در پشتیبانی وردپرس این است که «چرا بکاپ خودکار من در ساعات مشخص اجرا نمی‌شود؟» پاسخ ریشه در طراحی WP-Cron دارد. اگر سایت شما بازدید کمی دارد — مثلاً کمتر از صد بازدید در روز — به‌احتمال زیاد WP-Cron فقط در ساعاتی از روز اجرا می‌شود که ترافیک وجود دارد. اگر رویداد شما در ساعت ۳ بامداد تنظیم شده و سایت شما در آن ساعت هیچ بازدیدی ندارد، رویداد به اولین بازدید بعدی موکول می‌شود.

سه نشانه‌ای که سایت شما از این معما رنج می‌برد

نشانه اول: بکاپ‌های خودکار با تاریخ‌های نامنظم ساخته می‌شوند — یک روز صبح، روز بعد شب. نشانه دوم: ایمیل‌های خبرنامه‌ای که باید در ساعت مشخص ارسال شوند، با تأخیر چند ساعته می‌رسند. نشانه سوم: در گزارش‌های امنیتی، اسکن‌های خودکار گاهی چندین روز عقب می‌افتند. اگر هر کدام از این سه را دیدید، سایت شما کاندید جدی مهاجرت به cron سیستمی است.

راه‌حل سریع برای سایت‌های کم‌ترافیک

دو راه‌حل برای این مشکل وجود دارد. راه‌حل موقت، استفاده از سرویس‌های خارجی است که در بازه‌های منظم به سایت شما درخواست می‌فرستند. سرویس‌های مثل UptimeRobot یا Pingdom در بازه‌های پنج‌دقیقه‌ای به سایت شما سر می‌زنند و همین بازدید مصنوعی، باعث بیدار شدن WP-Cron می‌شود. راه‌حل دائمی، مهاجرت به cron سیستمی است که در بخش بعدی توضیح می‌دهم.

وقتی سایت شما بسیار سریع رشد می‌کند

مشکل معکوس هم وجود دارد. سایت‌های پرترافیک با WP-Cron، در هر بازدید کاربر چندین بار تابع spawn_cron() را اجرا می‌کنند و همین باعث می‌شود که روی هاست‌های اشتراکی با محدودیت Entry Processes، سایت به‌طور مرتب به دیوار محدودیت بخورد. تجربه‌ام این است که سایت‌های با بیش از یک میلیون بازدید ماهانه، بهترین عملکرد را با cron سیستمی و غیرفعال کردن WP-Cron دارند. برای درک محدودیت‌های هاست اشتراکی در این سناریو، چرا SSD برای هاست وردپرس ضروری است توضیح می‌دهد که چرا منابع سرور در ترافیک بالا به گلوگاه تبدیل می‌شوند.

سایت کم‌ترافیک با WP-Cron، رویدادهایش را هرگز به‌موقع اجرا نمی‌کند؛ سایت پرترافیک با WP-Cron، منابع هاستش را هدر می‌دهد. در هیچ‌کدام از این دو حالت، WP-Cron انتخاب بهینه نیست.

جایگزینی با cron سیستمی

مهاجرت به cron سیستمی، تغییر بزرگی در نحوه زمان‌بندی کارها است ولی مراحلش ساده است. مهم این است که به ترتیب درست انجام شود تا در میانه مهاجرت، هیچ وظیفه‌ای از دست نرود.

گام اول: تنظیم cron سیستمی

در پنل cPanel، از بخش Cron Jobs یک cron جدید بسازید که در بازه‌های پنج‌دقیقه‌ای، فایل wp-cron.php را اجرا کند:

*/5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

یا با curl که در بعضی محیط‌ها پایدارتر است:

*/5 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1

نکته ظریفی که در پروژه‌های واقعی به آن برخورده‌ام: پارامتر doing_wp_cron در انتهای URL ضروری است چون وردپرس از این پارامتر می‌فهمد که درخواست، یک درخواست cron است نه بازدید کاربر. بدون این پارامتر، رفتار WP-Cron به‌درستی فعال نمی‌شود. برای نحوه راه‌اندازی cron در هاست‌ها، آموزش نصب وردپرس روی هاست برای مبتدیان محیط را معرفی کرده و ابزارهای مدیریتی را توضیح داده است.

گام دوم: غیرفعال کردن WP-Cron داخلی

حالا که cron سیستمی تنظیم شده، WP-Cron داخلی باید غیرفعال شود تا رویدادها دوبار اجرا نشوند:

define( 'DISABLE_WP_CRON', true );

ترتیب این دو گام حیاتی است: اگر اول WP-Cron را غیرفعال کنید و بعد cron سیستمی را تنظیم کنید، در فاصله بین این دو، رویدادها اجرا نمی‌شوند. اگر برعکس عمل کنید، برای مدت کوتاهی رویدادها دوبار اجرا می‌شوند که ممکن است بی‌ضرر باشد ولی برای بعضی رویدادها مثل ارسال ایمیل، پیامد ناخوشایند دارد.

گام سوم: تأیید عملکرد

پس از هر دو گام، با ابزار WP-CLI یا با بازدید مستقیم از لاگ cron در پنل هاست، تأیید کنید که رویدادها در بازه‌های درست اجرا می‌شوند. یک روش عملی که استفاده می‌کنم، اضافه کردن یک ردیف لاگ موقت در یکی از رویدادهای کرون است:

add_action( 'myplugin_daily_cleanup', function () {
    error_log( 'Cron executed at: ' . current_time( 'mysql' ) );
} );

پس از چند روز، اگر فایل error_log را نگاه کنید و رکوردها با فاصله زمانی منظم ظاهر شده باشند، یعنی cron سیستمی به‌درستی کار می‌کند. فراموش نکنید این کد دیباگ را بعد از تأیید پاک کنید.

محدودیت‌های cron سیستمی در هاست اشتراکی

بعضی هاست‌های اشتراکی، تعداد cronهای سیستمی را محدود می‌کنند یا اجازه اجرای آن‌ها با فاصله کمتر از پانزده دقیقه را نمی‌دهند. پیش از مهاجرت، این محدودیت را از پشتیبانی هاست بپرسید. اگر محدودیت وجود دارد، گزینه بعدی استفاده از سرویس‌های خارجی (Scheduled Tasks) است که در بسیاری از پروژه‌ها جایگزین قابل‌قبولی هستند. برای درک تفاوت پلن‌های هاست و امکانات‌شان، بهترین هاست برای وردپرس مقایسه دقیقی دارد.

دام‌های واقعی در زمان‌بندی

در بازبینی افزونه‌ها و پروژه‌های مشتری، چند دام تکرارشونده در حوزه WP-Cron دیده‌ام که هر کدامشان به شکل‌های متفاوتی در سایت ظاهر می‌شوند. این فهرست، چک‌لیست پیش از انتشار افزونه‌های من است.

دام اول: رویدادهای تکراری به‌خاطر نداشتن شرط wp_next_scheduled

این دام، رایج‌ترین دام در افزونه‌های تازه‌کار است. اگر بدون شرط wp_next_scheduled رویداد ثبت کنید، هر بار که وردپرس لود شود، یک ردیف تکراری در آرایه cron اضافه می‌شود. در سایتی با ترافیک بالا، بعد از یک ماه، می‌توانید هزاران ردیف تکراری در دیتابیس داشته باشید که هر کدام باعث کندی لود سایت می‌شوند.

دام دوم: استفاده از زمان ثابت به‌جای timestamp نسبی

هنگام ثبت رویداد، باید timestamp دقیق بدهید. اگر به‌جای time() + HOUR_IN_SECONDS از یک عدد ثابت مثل 1700000000 استفاده کنید، آن رویداد در گذشته است و بلافاصله اجرا می‌شود. این خطا در پروژه‌های کپی-پیست زیاد دیده می‌شود:

// اشتباه: زمان ثابت
wp_schedule_event( 1700000000, 'daily', 'myplugin_task' );

// درست: زمان نسبی از حال
wp_schedule_event( time() + DAY_IN_SECONDS, 'daily', 'myplugin_task' );

دام سوم: پاک‌نکردن رویدادها هنگام غیرفعال‌سازی

افزونه‌ای که هنگام غیرفعال‌سازی، رویدادهای cron خودش را پاک نکند، در سایت مشتری رویدادهای یتیم جا می‌گذارد. این رویدادها بعد از حذف افزونه، به‌طور ناموفق اجرا می‌شوند چون تابع مربوطه وجود ندارد. در بازبینی‌های واقعی، سایت‌هایی دیده‌ام که بعد از حذف دو افزونه، نزدیک بیست رویداد cron یتیم داشته‌اند که در هر بازدید چک می‌شده‌اند بدون اینکه هیچ کاری انجام دهند.

دام چهارم: استفاده از cron برای کارهای طولانی

WP-Cron به‌طور پیش‌فرض، رویدادها را با محدودیت زمانی اجرا می‌کند. اگر رویداد شما طولانی باشد — مثلاً پاک‌سازی دیتابیس بزرگ — وردپرس ممکن است آن را در میانه کار قطع کند. الگوی درست برای کارهای طولانی، تقسیم به تکه‌های کوچک و زمان‌بندی هر تکه به‌صورت جداگانه است. جزئیات الگوی تقسیم کار در مدیریت داده‌های موقت در وردپرس تحت عنوان «پاک‌سازی مرحله‌ای» توضیح داده شده است.

دام پنجم: عدم توجه به timezone سرور

توابع WP-Cron بر پایه timestamp یونیکس کار می‌کنند که منطقه زمانی ندارد. ولی نمایش آن به کاربر ممکن است با منطقه زمانی سایت متفاوت باشد. اگر رویدادی را برای ساعت ۳ بامداد به وقت تهران تنظیم می‌کنید، باید محاسبه را بر اساس UTC انجام دهید و بعد به منطقه زمانی سایت تبدیل کنید. یک الگوی امن که در پروژه‌ها استفاده می‌کنم:

$timezone = wp_timezone();
$datetime = new DateTime( 'tomorrow 03:00', $timezone );
$timestamp = $datetime->getTimestamp();

wp_schedule_event( $timestamp, 'daily', 'myplugin_daily_cleanup' );

این الگو از wp_timezone() که از وردپرس ۵.۳ به بعد در دسترس است استفاده می‌کند و منطقه زمانی سایت را دقیق اعمال می‌کند. نبود این دقت، منبع مشکلاتی است که در ظاهر تصادفی به نظر می‌رسند ولی در واقع از عدم تطابق timezone می‌آیند.

پایش و اندازه‌گیری سلامت کرون

پایش دوره‌ای WP-Cron، مثل پایش سایر اجزای سایت، بخشی از نگهداری حرفه‌ای است. چهار شاخص کلیدی که در پروژه‌ها زیر نظر می‌گیرم:

شاخص اول: تعداد کل رویدادها

تعداد رویدادهای فعال در سایت باید با تعداد افزونه‌های فعال هم‌خوانی داشته باشد. اگر تعداد رویدادها بیشتر از یک مضرب معقول از تعداد افزونه‌ها است، یعنی رویدادهای تکراری یا یتیم انبار شده‌اند:

wp cron event list --fields=hook,next_run_relative --format=table

خروجی این دستور، فهرست همه رویدادها با زمان اجرای بعدی‌شان را نشان می‌دهد. اگر رویدادی با نام غیرقابل شناسایی یا زمان اجرای گذشته در فهرست دیدید، کاندید بررسی است.

شاخص دوم: تأخیر اجرا

فاصله بین زمان مورد انتظار اجرای رویداد و زمان واقعی اجرا، شاخص مهمی از سلامت است. اگر تأخیر به‌طور مداوم از یک ساعت بیشتر است، یعنی سایت شما به WP-Cron وابسته است ولی ترافیکش برای اجرای به‌موقع کافی نیست. این وضعیت، نشانه صریح برای مهاجرت به cron سیستمی است.

شاخص سوم: خطاها در حین اجرا

هر خطای PHP در حین اجرای رویداد cron باید لاگ شود. اگر با WP_DEBUG_LOG فعال کار می‌کنید، این خطاها در فایل wp-content/debug.log ظاهر می‌شوند. بررسی دوره‌ای این فایل، پیش از آنکه خطاها به مشکلات کاربری تبدیل شوند، فرصت اصلاح می‌دهد. اگر با حالت دیباگ وردپرس آشنا نیستید، تست و دیباگ پروژه‌های توسعه وردپرس راهنمای دقیقی است.

شاخص چهارم: مصرف منابع در زمان اجرای cron

رویدادهایی که کوئری سنگین دیتابیس یا فراخوانی API بیرونی دارند، در زمان اجرا مصرف CPU و حافظه سرور را بالا می‌برند. اگر هم‌زمان با اجرای رویدادهای cron، سایر کاربران سایت کندی حس می‌کنند، یعنی رویداد شما به بهینه‌سازی نیاز دارد. یکی از الگوهای استاندارد، اجرای رویدادهای سنگین در ساعات کم‌ترافیک است. تحلیل دقیق این تعادل بین مصرف منابع و زمان‌بندی در چگونه مصرف منابع هاست را کاهش دهیم آمده است.

پرسش‌های پرتکرار درباره کرون وردپرس

آیا می‌توانم WP-Cron را به‌طور کامل غیرفعال کنم؟ بله، با ثابت DISABLE_WP_CRON. ولی این کار را فقط وقتی انجام دهید که cron سیستمی را به‌عنوان جایگزین تنظیم کرده باشید. بدون جایگزین، هیچ‌کدام از رویدادهای دوره‌ای سایت اجرا نمی‌شوند.

چرا بکاپ خودکار من در ساعت مشخص اجرا نمی‌شود؟ به احتمال زیاد سایت شما ترافیک کمی در آن ساعت دارد و WP-Cron به بازدید کاربران وابسته است. راه‌حل، مهاجرت به cron سیستمی یا استفاده از سرویس‌های خارجی پینگ است. الگوی دقیق مهاجرت در همین مقاله توضیح داده شده است.

آیا با cron سیستمی، سرعت سایت کاهش می‌یابد؟ نه، برعکس. با WP-Cron، هر بازدید کاربر شامل بررسی رویدادهای در انتظار است. با cron سیستمی، این بررسی از مسیر بازدید کاربر خارج می‌شود و درخواست‌های کاربر سبک‌تر می‌شوند. این تفاوت در سایت‌های پرترافیک کاملاً محسوس است.

چه بازه زمانی برای cron سیستمی مناسب است؟ بازه پنج دقیقه‌ای برای اکثر سایت‌ها نقطه تعادل خوبی است. بازه‌های کوتاه‌تر از پنج دقیقه، در اکثر هاست‌های اشتراکی مجاز نیستند و بازه‌های طولانی‌تر از پانزده دقیقه، دقت زمان‌بندی را کاهش می‌دهند.

چگونه رویدادهای یتیم cron را پیدا کنم؟ با WP-CLI و دستور wp cron event list می‌توانید فهرست همه رویدادها را ببینید. رویدادهایی که نام‌شان به افزونه‌ای اشاره می‌کند که دیگر نصب نیست، کاندید حذف هستند. برای تطبیق دقیق با فهرست افزونه‌های فعال، از فایل functions.php یا اسکریپت سفارشی استفاده کنید. جزئیات روش پاک‌سازی داده یتیم در مدیریت داده‌های موقت در وردپرس آمده است.

آیا رویدادهای cron در محیط Object Cache رفتار متفاوتی دارند؟ نه، رفتار WP-Cron از Object Cache مستقل است. رویدادها در هر حالت در جدول wp_options ذخیره می‌شوند. ولی اگر Object Cache فعال باشد، سرعت اجرای رویدادها کمی بهتر می‌شود چون خواندن و نوشتن آرایه cron از حافظه انجام می‌شود.

آیا می‌توانم رویدادی را با فاصله کوتاه‌تر از پنج دقیقه زمان‌بندی کنم؟ بله، با تعریف بازه سفارشی در فیلتر cron_schedules. ولی در عمل، WP-Cron برای بازه‌های کوتاه مناسب نیست چون هر بازدید کاربر می‌تواند رویداد را چندبار اجرا کند. برای زمان‌بندی دقیق زیر پنج دقیقه، cron سیستمی تنها گزینه درست است.

چگونه مطمئن شوم cron سیستمی به‌درستی کار می‌کند؟ با اضافه کردن یک کد دیباگ موقت که در هر اجرا یک خط لاگ می‌نویسد. بعد از چند روز، تاریخ‌های لاگ را بررسی کنید. اگر فاصله‌های زمانی منظم بودند، cron به‌درستی کار می‌کند. پس از تأیید، کد دیباگ را پاک کنید.

آیا استفاده از سرویس‌های خارجی برای بیدار کردن WP-Cron بی‌خطر است؟ برای سایت‌های کوچک، بی‌خطر و مقرون‌به‌صرفه است. ولی برای سایت‌های بزرگ که ترافیک قابل توجهی دارند، این رویکرد مسکّن است نه درمان. cron سیستمی، راه‌حل اصولی است که برای مقیاس‌های بزرگ طراحی شده.

آیا با چند سایت وردپرسی روی یک هاست، cron سیستمی هر سایت را جدا تنظیم کنم؟ بله. هر سایت باید cron سیستمی اختصاصی خودش را داشته باشد. اگر بخواهید با یک cron، همه سایت‌ها را پوشش دهید، در زمان اجرای همزمان همه سایت‌ها، منابع سرور تحت فشار قرار می‌گیرد. توصیه من، پخش کردن زمان‌بندی cronها در بازه‌های مختلف است.

آیا باید هنگام مهاجرت به cron سیستمی، رویدادهای موجود را پاک کنم؟ نه، این رویدادها باید حفظ شوند چون داده‌ای مستقل از مکانیزم اجرا هستند. فقط باید ثابت DISABLE_WP_CRON را فعال کنید تا از آن به بعد، رویدادها توسط cron سیستمی اجرا شوند نه توسط بازدید کاربران.

چرا بعضی رویدادها در فهرست WP-CLI با زمان اجرای گذشته نشان داده می‌شوند؟ این اتفاق وقتی رخ می‌دهد که رویداد در زمان مقرر اجرا نشده باشد ولی هنوز در صف مانده است. WP-Cron در اولین فرصت بعدی، این رویداد را اجرا می‌کند. اگر تعداد این رویدادهای عقب‌مانده زیاد باشد، یعنی سایت شما به WP-Cron وابسته است ولی ترافیکش برای اجرای به‌موقع کافی نیست.

زمان‌بندی درست، بیمه‌نامه پشت‌صحنه سایت

در پایان این مسیر، یک حقیقت را باید بپذیریم: WP-Cron یکی از نقاط ضعف ساختاری وردپرس است که از روز اول در هسته بوده و تغییرش در آینده هم بعید است. این ضعف، در سایت‌های کوچک عملاً بی‌اثر است ولی در سایت‌هایی که وظایف زمان‌بندی‌شده حساس دارند — بکاپ، فروشگاه، خبرنامه، اسکن امنیتی — به‌طور جدی خودش را نشان می‌دهد. تجربه من این است که هر سایتی که از یک سطح ترافیک خاص فراتر می‌رود، باید به cron سیستمی مهاجرت کند، حتی اگر این مهاجرت به‌نظر پیچیده بیاید.

یکی از عادات حرفه‌ای که در همه پروژه‌های خودم رعایت می‌کنم، این است که در جلسه راه‌اندازی سایت، مکانیزم cron را یکی از چهار موضوع فنی روز اول تعیین می‌کنم. سه موضوع دیگر عبارتند از: انتخاب سیستم کش، استراتژی بکاپ و تنظیمات امنیتی پایه. تجربه‌ام این است که اگر در روز اول این چهار موضوع به‌درستی تعیین نشوند، اصلاحشان در میانه کار چند برابر هزینه دارد.

اگر در ابتدای مسیر یادگیری هستید، سه تمرین را پیشنهاد می‌کنم. اول، روی یک سایت تستی، چند رویداد cron با بازه‌های مختلف زمان‌بندی کنید و با WP-CLI وضعیتشان را پایش کنید. دوم، WP-Cron داخلی را غیرفعال کنید و با cron سیستمی جایگزین کنید و تفاوت رفتار رویدادها را در طول یک هفته ببینید. سوم، یک شبیه‌سازی از دام «رویداد تکراری» بسازید — بدون شرط wp_next_scheduled — و ببینید بعد از چند روز، چند ردیف تکراری در دیتابیس جمع می‌شود. این سه تمرین، درک عمیقی از WP-Cron به شما می‌دهد که هیچ مقاله‌ای جایگزینش نمی‌شود.

اگر تجربه‌ای از پروژه‌ای دارید که مشکل زمان‌بندی در آن به شکل غیرمنتظره‌ای ظاهر شده — یا برعکس، پروژه‌ای که با مهاجرت به cron سیستمی به‌طور محسوس پایدارتر شده — در دیدگاه بنویسید. آن تجربه برای کسی که همین امروز تصمیم می‌گیرد آیا سایتش به مهاجرت cron نیاز دارد یا نه، ارزشمندتر از هر مستند رسمی است. ⏱️