آموزش وردپرس

WP-Cron و زمان‌بندی واقعی در وردپرس: چرا کارها سر وقت اجرا نمی‌شوند

وحید بهنام20 دقیقه مطالعه2 بازدید0 دیدگاه
wp-cron-hero

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

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

WP-Cron چیست و چه تفاوتی با کران واقعی سرور دارد

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

مقایسهٔ WP-Cron و کران واقعی سرور
یکی با بازدید کار می‌کند، دیگری با ساعت

کران واقعی یک سرویس سیستمی است: سرویسی به نام crond که همیشه روشن است، مستقل از سایت و مستقل از بازدیدکننده. شما به آن می‌گویید «دقیقهٔ ۱۵، ساعت ۳، هر روز» و او همان لحظه اجرا می‌کند، حتی اگر در آن ساعت هیچ‌کس به سایت شما نگاه نکرده باشد. WP-Cron چنین سرویسی ندارد. هیچ فرایندی در پس‌زمینهٔ وردپرس بیدار نمی‌ماند که ساعت را نگاه کند.

WP-Cron و کران واقعی، شش تفاوت عملی
معیارWP-Cronکران واقعی سرور
محرک اجرابارگذاری صفحه در سایتسرویس زمان‌بند سیستم
وابستگی به ترافیککامل؛ بدون بازدید، اجرا نیستهیچ
کار ازدست‌رفتهدر صف می‌ماند و در فرصت بعدی اجرا می‌شوداز دست می‌رود و دوباره تلاش نمی‌شود
قالب زمانtimestamp شروع + فاصله بر حسب ثانیهپنج ستون: دقیقه، ساعت، روز ماه، ماه، روز هفته
سقف زمان اجراهمان سقف درخواست وب (اغلب ۳۰ ثانیه)معمولاً بدون سقف زمانی وب
مناسب برایکارهای غیرحیاتی و غیرحساس به دقیقهپشتیبان‌گیری، ارسال زمان‌دار، هر کار حساس به وقت

دو مدل زمان‌بندی هم با هم فرق دارند و این تفاوت بیشتر از آنچه به‌نظر می‌رسد مهم است. کران سیستمی می‌گوید «ساعت ۳:۱۵ هر شب». WP-Cron می‌گوید «از این لحظه، هر ۸۶۴۰۰ ثانیه یک‌بار». وردپرس فقط دو چیز را نگه می‌دارد: زمان اولین اجرا و فاصله. پس WP-Cron ساعت ثابت ندارد؛ اگر یک اجرا دیر شود، همهٔ اجراهای بعدی به همان اندازه جابه‌جا می‌شوند. به همین دلیل هم هست که ساعت اجرای پشتیبان‌گیری شما هر شب کمی تغییر می‌کند و در نهایت یک شب به ساعت پیک سایت می‌رسد.

بازه‌های آمادهٔ وردپرس چهار عدد است: hourly، twicedaily، daily و weekly (که از وردپرس ۵.۴ اضافه شد). اگر بازهٔ دیگری می‌خواهید، با فیلتر cron_schedules اضافه‌اش می‌کنید؛ مقدار interval همیشه بر حسب ثانیه است:

PHP
add_filter( 'cron_schedules', function ( $schedules ) {
	$schedules['every_ten_minutes'] = array(
		'interval' => 600,          // ۱۰ دقیقه، بر حسب ثانیه
		'display'  => 'هر ۱۰ دقیقه',
	);
	return $schedules;
} );

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

چرا کارها فقط با بازدید کاربر اجرا می‌شوند

هر بار که صفحه‌ای از سایت باز می‌شود، وردپرس بوت می‌شود و در جریان بوت یک تابع به نام wp_cron() صدا زده می‌شود. این تابع فهرست کارهای زمان‌بندی‌شده را می‌خواند؛ اگر کاری سررسیده باشد، با یک درخواست HTTP به فایل wp-cron.php می‌گوید برو اجرا کن. نکتهٔ مهم این است که این درخواست مسدودکننده نیست: وردپرس آن را با تایم‌اوت ۰٫۰۱ ثانیه می‌فرستد و بی‌درنگ رهایش می‌کند. یعنی صفحهٔ کاربر منتظر تمام شدن پشتیبان‌گیری نمی‌ماند — و یعنی هیچ‌کس هم نمی‌فهمد آن درخواست شکست خورده یا نه.

سمت دیگر ماجرا، فایل wp-cron.php است. این فایل ثابت DOING_CRON را تعریف می‌کند، سقف حافظه را برای کران بالا می‌برد، قفل اجرا را می‌گیرد، و بعد رویدادهای سررسیده را به ترتیب اجرا می‌کند. سه رفتارش را باید بدانید:

  • قفل اجرا. یک ترنزینت به نام doing_cron نگه می‌دارد تا دو اجرا همزمان شروع نشوند. عمر این قفل با ثابت WP_CRON_LOCK_TIMEOUT تعیین می‌شود که مقدار پیش‌فرضش ۶۰ ثانیه است. اگر قفل تازه باشد، درخواست تازه اجرا نمی‌شود و بی‌صدا برمی‌گردد.
  • زمان‌بندی پیش از اجرا. هر رویداد را اول دوباره زمان‌بندی می‌کند و بعد اجرا. اگر اجرای این نوبت خطای مرگ‌آور بدهد، رویداد برای دورهٔ بعد سر جایش است — ولی رویدادهای بعدی صف در همان درخواست اجرا نمی‌شوند.
  • خروجی خالی. اگر هیچ کار سررسیده‌ای نباشد، فایل همان‌جا تمام می‌شود. پس دیدن پاسخ خالی از wp-cron.php نشانهٔ خرابی نیست؛ نشانهٔ صف خالی است.

در گزارش دسترسی سرور، این رفت‌وآمد را می‌شود دید. خط زیر یک درخواست واقعی کران است: متد POST، پارامتر doing_wp_cron، پاسخ 200 با بدنهٔ صفر بایت، و عامل کاربر که خود وردپرس می‌فرستد:

BASH
grep "wp-cron.php" /home/USER/logs/access.log | tail -3

203.0.113.10 - - [01/Nov/2026:02:14:07 +0330] "POST /wp-cron.php?doing_wp_cron=1761956647.3185 HTTP/1.1" 200 0 "-" "WordPress/6.8; https://example.com"

از همین مکانیک چهار نتیجهٔ عملی می‌گیریم:

  • سایت کم‌بازدید دیر اجرا می‌کند. سایتی که شبانه سه بازدید دارد، ممکن است پشتیبان‌گیری ساعت ۳ را ساعت ۱۱ صبح اجرا کند. این خرابی نیست، طراحی است.
  • کش کامل صفحه کران را گرسنه می‌گذارد. اگر افزونهٔ کش، صفحه را بدون اجرای PHP تحویل بدهد، دیگر جایی برای صدا زدن wp_cron() نمانده است. هرچه سایت شما سریع‌تر و کش‌شده‌تر باشد، این اثر جدی‌تر می‌شود.
  • بسته بودن درخواست داخلی، کران را می‌خواباند. اگر افزونهٔ امنیتی یا فایروال میزبان، درخواست سایت به خودش (loopback) را ببندد، همان درخواستی که وردپرس به wp-cron.php می‌فرستد بی‌پاسخ می‌ماند؛ نتیجه، صف پر و اجرای صفر است.
  • ترافیک ربات هم کران را بیدار می‌کند. هر خزنده‌ای که صفحه‌ای را بار کند، یک فرصت اجرا می‌سازد. سایت پرترافیک گاهی به‌طور تصادفی زمان‌بندی درست دارد و همین باعث می‌شود مشکل دیرتر دیده شود.

چطور بفهمیم WP-Cron در سایت ما گیر کرده

پیش از هر تغییری، پنج بررسی بزنید. هر کدام یک لایهٔ متفاوت را می‌سنجد و با هم مشخص می‌کنند مشکل «اجرا نمی‌شود» است یا «اجرا می‌شود و شکست می‌خورد». اگر SSH دارید، دو بررسی اول با WP-CLI چند ثانیه است؛ اگر ندارید، از بخش بعدی و لاگ سرور استفاده کنید.

BASH
wp cron event list --fields=hook,next_run_gmt,next_run_relative,recurrence

+---------------------------+---------------------+---------------------+------------+
| hook                      | next_run_gmt        | next_run_relative   | recurrence |
+---------------------------+---------------------+---------------------+------------+
| wp_version_check          | 2026-11-01 10:15:13 | 3 hours 12 minutes  | 12 hours   |
| wp_update_plugins         | 2026-11-01 10:15:13 | 3 hours 12 minutes  | 12 hours   |
| backup_daily_run          | 2026-10-31 23:30:00 | -7 hours 40 minutes | 24 hours   |
+---------------------------+---------------------+---------------------+------------+

ستون next_run_relative قاضی است. عدد منفی یعنی زمان اجرا گذشته و رویداد هنوز در صف است؛ همان چیزی که در سطر سوم می‌بینید. اگر همهٔ سطرها مثبت باشند و فقط کار شما دیر شده، مشکل ساختار کران نیست، خود آن کار است.

BASH
# ۱) قفل اجرا؛ عدد قدیمی یا آیندهٔ دور، نشانهٔ اجرای نیمه‌کاره است
wp transient get doing_cron

# ۲) آیا فایل کران از بیرون پاسخ می‌دهد؟ ۲۰۰ خوب است، ۴۰۳ یعنی بسته شده
curl -s -o /dev/null -w "http=%{http_code} time=%{time_total}sn" "https://example.com/wp-cron.php"

# ۳) آخرین اجراها در لاگ دسترسی
grep -c "wp-cron.php" /home/USER/logs/access.log
نشانه، معنی و کار بعدی
نشانهمعنیکار بعدی
next_run_relative منفی روی بیشتر رویدادهاکران اجرا نمی‌شود یا اجرایش شکست می‌خوردبررسی ۲ و ۳، بعد لاگ خطا
پاسخ ۴۰۳ یا ۴۰۱ برای wp-cron.phpافزونهٔ امنیتی یا فایروال میزبان جلوی دسترسی را گرفتهاستثنا کردن مسیر، یا رفتن به روش کران سیستمی
پاسخ ۵۰۰ یا تایم‌اوتخطای مرگ‌آور PHP در یکی از رویدادهاخواندن debug.log و اجرای تک‌تک رویدادها
قفل doing_cron با مقدار آیندهٔ دورقفل خراب یا ساعت سرور ناهماهنگپاک کردن ترنزینت و بررسی ساعت سرور
همه‌چیز سالم، ولی کار سنگین نیمه‌کاره می‌ماندسقف زمان یا حافظهٔ درخواست وبانتقال کار به خط فرمان یا کران سیستمی

یک نکتهٔ ظریف: curl مستقیم به wp-cron.php فقط می‌گوید فایل از بیرون در دسترس است. اگر سایت شما جلوی درخواست داخلی را بگیرد ولی درخواست بیرونی را رد نکند، این آزمون سبز می‌شود و مشکل باقی می‌ماند. برای همین لاگ دسترسی — نه پاسخ curl — سند نهایی است: در لاگ باید ببینید که درخواست‌های wp-cron.php در ساعات شبانه هم وجود دارند.

خطای Missed schedule و پست زمان‌داری که منتشر نمی‌شود

برچسب Missed schedule در فهرست پست‌ها یک شرط ساده دارد: وضعیت پست future باشد و زمان انتشار مقررش گذشته باشد. همین. وردپرس پست را برای آینده ذخیره می‌کند، یک رویداد به نام publish_future_post با شناسهٔ آن پست در صف می‌گذارد، و منتظر می‌ماند کران آن را اجرا کند. تا وقتی این رویداد اجرا نشود، پست در وضعیت زمان‌بندی‌شده می‌ماند و از دید خواننده وجود ندارد.

چرخهٔ خطای Missed schedule
پنج گام تا پستی که منتشر نمی‌شود

پس «Missed schedule» تقریباً همیشه یعنی یکی از این چهار چیز:

  • کران در آن بازه هیچ اجرایی نداشته (ترافیک صفر یا کران خاموش)؛
  • کران اجرا شده ولی رویدادهای پیش از آن در صف خطای مرگ‌آور داده‌اند و نوبت به این یکی نرسیده؛
  • افزونه‌ای رویداد را پاک کرده یا با آرگومان‌های نادرست ثبت کرده؛ یادتان باشد wp_next_scheduled() فقط وقتی رویداد را پیدا می‌کند که آرگومان‌هایش دقیقاً با آرگومان‌های زمان ثبت یکی باشد؛
  • منطقهٔ زمانی سایت با ساعتی که در ذهن دارید یکی نیست؛ زمان رویدادها در دیتابیس به UTC ذخیره می‌شود و اگر تنظیم ساعت سایت اشتباه باشد، پست ساعت دیگری منتشر می‌شود.

درمان، اول اجرای کارهای سررسیده و بعد تصمیم دربارهٔ پست است. پست را دستی منتشر نکنید مگر اینکه واقعاً بخواهید زمان انتشارش عوض شود:

BASH
# رویدادهای سررسیده را همین حالا اجرا کن (قفل doing_cron رعایت می‌شود)
wp cron event run --due-now

# فهرست پست‌هایی که زمانشان گذشته و هنوز future هستند
wp post list --post_status=future --fields=ID,post_title,post_date --format=table

# اگر می‌خواهید همان پست با زمان فعلی منتشر شود (زمان انتشار را عوض می‌کند)
wp post update 1234 --post_status=publish

بعد از --due-now معمولاً پست خودش منتشر می‌شود، چون وردپرس هنگام اجرای publish_future_post وضعیت را از future به publish تغییر می‌دهد. اگر منتشر نشد، مشکل در خود رویداد است نه در کران؛ با wp cron event list --fields=hook,args ببینید رویداد مربوط به آن شناسه وجود دارد یا نه.

خاموش کردن WP-Cron و جایگزینی با کران واقعی

خاموش کردن WP-Cron با یک خط در wp-config.php انجام می‌شود. این خط باید پیش از فراخوانی wp-settings.php باشد؛ معمولاً کنار بقیهٔ define()ها، بالای فایل:

PHP
/* زمان‌بند داخلی وردپرس خاموش؛ اجرا با کران سرور */
define( 'DISABLE_WP_CRON', true );

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

یک حالت میانی هم وجود دارد که برای هاست‌هایی است که درخواست داخلی را می‌بندند. ثابت ALTERNATE_WP_CRON باعث می‌شود وردپرس به‌جای درخواست HTTP جداگانه، همان درخواست صفحه را با یک ریدایرکت کوتاه به wp-cron.php بفرستد و کران را در همان اتصال اجرا کند:

PHP
/* فقط اگر درخواست داخلی سایت به خودش بسته است */
define( 'ALTERNATE_WP_CRON', true );

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

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

در سی‌پنل مسیر Cron Jobs است، معمولاً در دستهٔ Advanced. دو راه برای صدا زدن کران دارید: اجرای مستقیم فایل با باینری PHP، یا درخواست HTTP با curl یا wget. راه اول مطمئن‌تر است چون فایروال و افزونهٔ امنیتی نمی‌توانند جلویش را بگیرند؛ راه دوم ساده‌تر است و به مسیر باینری PHP نیاز ندارد.

مسیر اجرای کران واقعی روی هاست اشتراکی
از wp-config تا لاگ اجرا
BASH
# راه اول: اجرای مستقیم با باینری PHP (مسیر واقعی را از میزبان بپرسید)
*/5 * * * * /usr/local/bin/php /home/USER/public_html/wp-cron.php >/dev/null 2>&1

# راه دوم: درخواست HTTP بدون ذخیرهٔ خروجی
*/5 * * * * curl -s "https://example.com/wp-cron.php" >/dev/null 2>&1

# نمونهٔ رسمی وردپرس با wget
*/15 * * * * wget --delete-after http://example.com/wp-cron.php

پنج نکته که بیشتر خرابی‌ها از همین‌ها می‌آید:

  1. مسیر دستور، نه اسم دستور. در محیط کران سی‌پنل، متغیر PATH همان چیزی نیست که در ترمینال می‌بینید. php ممکن است پیدا نشود یا نسخهٔ قدیمی باشد. مسیر کامل باینری را بنویسید.
  2. تفاوت نسخهٔ PHP خط فرمان با وب. روی هاست‌های MultiPHP، سایت شما ممکن است با PHP ۸.۲ اجرا شود ولی php خط فرمان ۷.۴ باشد. آن‌وقت کران اجرا می‌شود و با خطای ناسازگاری می‌میرد. مسیر نسخهٔ درست را انتخاب کنید و با wp --info یا php -v تأیید بگیرید.
  3. فاصلهٔ اجرا را از میزبان بپرسید. بعضی میزبان‌ها فاصلهٔ کمتر از ۵ یا ۱۵ دقیقه را روی پلن اشتراکی قبول نمی‌کنند یا بی‌صدا نادیده می‌گیرند. عدد پیشنهادی: هر ۵ تا ۱۵ دقیقه.
  4. کار را قفل‌دار بنویسید. اگر اجرای قبلی هنوز تمام نشده، اجرای تازه نباید شروع شود. برای اسکریپت‌های پیچیده از flock استفاده کنید؛ برای wp-cron.php خود وردپرس با ترنزینت doing_cron این کار را می‌کند.
  5. خروجی را به جایی بفرستید که بخوانید.>/dev/null 2>&1 خطاها را هم دفن می‌کند. تا زمانی که مطمئن نشده‌اید همه‌چیز درست کار می‌کند، خروجی را به یک فایل لاگ بفرستید.

در پایان همین بخش، ترتیب درست کار را یک‌بار جمع کنیم: کران سرور را بسازید، خروجی را به فایل بفرستید، یک‌بار دستی اجرا کنید و کد وضعیت را ببینید، در لاگ دسترسی تأیید بگیرید، و تنها بعد از آن DISABLE_WP_CRON را اضافه کنید. اگر برعکس عمل کنید، فاصلهٔ بین دو کار، فاصلهٔ بی‌کرانی سایت است.

اجرای دستی کارها با WP-CLI

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

BASH
# چه چیزی در صف است
wp cron event list

# همهٔ کارهای سررسیده را همین حالا اجرا کن
wp cron event run --due-now
Executed the cron event 'backup_daily_run' in 12.41s.
Success: Executed a total of 1 cron event.

# فقط یک قلاب مشخص را اجرا کن
wp cron event run backup_daily_run

# رویدادهای جاماندهٔ یک افزونهٔ حذف‌شده را پاک کن
wp cron event delete old_plugin_sync
Success: Deleted a total of 1 cron event.

دستور wp cron event run --all هم وجود دارد و همهٔ قلاب‌ها را اجرا می‌کند، حتی آن‌هایی که هنوز سررسیده نشده‌اند. روی سایت زنده این کار می‌تواند چند پشتیبان‌گیری و چند به‌روزرسانی را همزمان روی هم بیندازد. اگر مجبورید از آن استفاده کنید، اول فهرست قلاب‌ها را ببینید و کارهای سنگین را با --exclude کنار بگذارید.

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

اگر SSH ندارید، همین کارها را با افزونه‌ای که فهرست رویدادها را نشان می‌دهد و اجازهٔ اجرای دستی می‌دهد هم می‌شود انجام داد؛ ولی خروجی و کنترلش به دقت خط فرمان نیست. و اگر SSH دارید و تازه‌کارید، راهنمای WP-CLI روی هاست اشتراکی از نصب بدون دسترسی روت شروع می‌کند و به همین دستورها می‌رسد.

چه کارهایی را نباید در کران گذاشت

کران برای کارهای کوتاه و تکرارشدنی است، نه برای هر کاری که می‌شود خودکار کرد. این هفت مورد را از آن بیرون بگذارید:

  • پشتیبان‌گیری کامل و پشت‌سرهم. نسخهٔ کامل دیتابیس و فایل‌ها روی هاست اشتراکی می‌تواند دقیقه‌ها طول بکشد و به سقف منابع بخورد. راه درست: پشتیبان‌گیری تکه‌تکه یا سرویس بیرونی؛ و اگر افزونهٔ داخلی می‌خواهید، روش‌های پشتیبان‌گیری در وردپرس را قبل از زمان‌بندی بخوانید.
  • ارسال انبوه ایمیل. خبرنامه و ایمیل گروهی روی SMTP معمولی به سقف ساعتی میزبان می‌خورد و نیمه‌کاره قطع می‌شود. کار درست، تحویل به سرویس ایمیل تخصصی است.
  • بازسازی تصاویر.wp media regenerate روی کتابخانهٔ بزرگ، ترکیبی از پردازش تصویر و مصرف حافظه است؛ در یک درخواست وب تقریباً همیشه نیمه‌کاره می‌ماند.
  • جایگزینی انبوه در دیتابیس.wp search-replace و مهاجرت دامنه، کار یک‌بارهٔ برنامه‌ریزی‌شده است، نه کار تکرارشونده. اشتباهش هم برگشت‌ناپذیر است.
  • به‌روزرسانی خودکار در ساعت پیک. به‌روزرسانی افزونه‌ها خودش روی کران سوار است؛ اگر زمانش را به ساعتی می‌برید که سایت شلوغ است، خطای یک افزونه در آن ساعت به همهٔ کاربران می‌رسد.
  • کارهای وابسته به نشست کاربر. سبد خرید، توکن ورود، فرم نیمه‌کاره — این‌ها به درخواست همان کاربر گره خورده‌اند و در کران معنا ندارند.
  • هر چیزی که به یک API بیرونی وصل می‌شود و تایم‌اوت ندارد. یک سرویس کند، کل صف را پشت خودش نگه می‌دارد. تایم‌اوت صریح بگذارید و شکست را در لاگ ثبت کنید.

محدودیت هاست اشتراکی در اجرای کارهای سنگین

روی هاست اشتراکی، منابع شما سهمیه‌بندی‌شده است. معمولاً یک لایهٔ محدودسازی مثل CloudLinux سقف مصرف پردازنده، حافظهٔ فیزیکی، تعداد فرایند و ورودی/خروجی دیسک را برای هر اکانت جدا نگه می‌دارد. کران هم از همین سهمیه می‌خورد؛ فرقی نمی‌کند که از پنل اجرا شود یا از درخواست HTTP.

چهار سقف را بشناسید و اندازه بگیرید:

BASH
# سقف‌های مؤثر PHP خط فرمان
php -i | grep -E "memory_limit|max_execution_time|disable_functions"

# همان سقف‌ها، ولی از دید خود وردپرس
wp eval 'echo ini_get("memory_limit"), " | ", ini_get("max_execution_time"), PHP_EOL;'
  • سقف زمان اجرا. برای درخواست وب اغلب ۳۰ ثانیه و برای خط فرمان معمولاً بی‌نهایت است. کاری که در کران وبی نیمه‌کاره می‌ماند، از خط فرمان کامل می‌شود. همین یک تفاوت، دلیل اصلی انتقال کارهای سنگین به کران سیستمی است.
  • سقف حافظه. بازسازی تصویر، ایمپورت و گزارش‌های بزرگ به ۲۵۶ مگابایت یا بیشتر نیاز دارند. وردپرس هنگام اجرای کران سقف حافظه را بالا می‌برد، ولی نه بیشتر از سقف میزبان.
  • کشته شدن فرایند. اگر میزبان به‌خاطر مصرف منابع فرایند را بکشد، وردپرس آن را «خطا» نمی‌بیند؛ رویداد قبلاً برای دورهٔ بعد زمان‌بندی شده، پس دوباره و دوباره تلاش می‌شود و هر بار نیمه‌کاره می‌ماند. این حلقه، هم منابع می‌سوزاند هم لاگ را پر می‌کند.
  • همپوشانی اجراها. قفل ۶۰ ثانیه‌ای فقط جلوی شروع همزمان را می‌گیرد؛ کاری که ۶ دقیقه طول می‌کشد می‌تواند با اجرای بعدی خودش همپوشانی داشته باشد. برای کارهای طولانی، قفل خودتان را بگذارید.

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

سنجش: لاگ اجرای کران

بدون لاگ، هر تغییری در تنظیمات کران حدس است. لاگ باید سه چیز را ثبت کند: چه زمانی، با چه کد خروجی، و چقدر طول کشید. ساده‌ترین راه، نوشتن زمان در خروجی خود کران است. فقط یک تله دارد: در crontab علامت % باید با بک‌اسلش فرار داده شود، وگرنه کران بقیهٔ خط را به‌عنوان ورودی استاندارد می‌خواند و دستور شما نصفه اجرا می‌شود:

BASH
*/10 * * * * { echo "== $(date -u +%F%T) UTC"; curl -s -o /dev/null -w "http=%{http_code} time=%{time_total}sn" "https://example.com/wp-cron.php"; } >> /home/USER/cron.log 2>&1

اگر ترجیح می‌دهید از داخل وردپرس ثبت کنید، یک فایل کوچک در wp-content/mu-plugins/ بگذارید. این فایل فقط وقتی فعال می‌شود که درخواست، درخواست کران باشد و پایان هر اجرا را با اوج مصرف حافظه ثبت می‌کند:

PHP
<?php
/**
 * Plugin Name: Cron Watch
 * Description: ثبت پایان هر اجرای کران در فایل cron-watch.log
 */
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
	add_action( 'shutdown', function () {
		error_log(
			sprintf( "cron finished %s | peak memory %sn", gmdate( 'c' ), size_format( memory_get_peak_usage( true ) ) ),
			3,
			WP_CONTENT_DIR . '/cron-watch.log'
		);
	} );
}

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

BASH
tail -n 20 /home/USER/cron.log

# فاصلهٔ بین اجراها: اگر بیشتر از بازهٔ تنظیم‌شده باشد، اجراها جا افتاده‌اند
awk '/^== /{t=$2; if (p) print t, p; p=t}' /home/USER/cron.log | tail -20

سه چیز را اندازه بگیرید و بقیه را رها کنید: فاصلهٔ واقعی بین اجراها با فاصلهٔ تنظیم‌شده، کد وضعیت پاسخ، و زمان اجرا. اگر فاصله‌ها منظم است ولی کار شما انجام نمی‌شود، مشکل کران نیست و باید در لاگ خودِ افزونه دنبالش بگردید. اگر فاصله‌ها منظم است و کار خطا می‌دهد، فایل debug.log و پیام آخرین خطا را ببینید. و لاگ را چرخشی کنید؛ فایل لاگی که یک‌ساله شده و کسی سراغش نرفته، خودش یک مسئلهٔ منابع است.

بیشتر بخوانید

پرسش‌های متداول WP-Cron

آیا خاموش کردن WP-Cron سایت را سریع‌تر می‌کند؟

به‌طور محسوسی نه. درخواست کران مسدودکننده نیست و با تایم‌اوت ۰٫۰۱ ثانیه رها می‌شود، پس زمان بارگذاری صفحه را عملاً نمی‌خورد. سود واقعی خاموش کردنش قابلیت اعتماد است، نه سرعت: پشتیبان‌گیری و انتشار زمان‌دار دیگر به بازدیدکننده وابسته نمی‌مانند. اگر فقط دنبال سرعت هستید، جای درستش کش، وزن بوت و جدول گزینه‌ها است.

چرا پست زمان‌دار من با اینکه کران تنظیم شده باز هم دیر منتشر می‌شود؟

چون زمان‌بندی بر اساس فاصله است، نه ساعت دقیق. اگر کران را هر ۱۵ دقیقه اجرا کنید، پست شما حداکثر حدود ۱۵ دقیقه بعد از زمان مقرر منتشر می‌شود. تأخیر بیشتر از یک بازه یعنی اجراها جا می‌افتند: فاصلهٔ کران را چک کنید، لاگ را ببینید و مطمئن شوید DISABLE_WP_CRON بدون کران واقعی روشن نشده است.

آیا افزونهٔ پشتیبان‌گیری من از WP-Cron استفاده می‌کند؟

اکثر افزونه‌های پشتیبان‌گیری و اکثر افزونه‌های ارسال ایمیل زمان‌بندی‌شده از همین سیستم استفاده می‌کنند، ولی استثنا هم هست. مطمئن‌ترین راه، دیدن فهرست رویدادها با wp cron event list است: اگر قلابی با نام آن افزونه در صف باشد، از WP-Cron استفاده می‌کند و با کران واقعی قابل اعتماد می‌شود.

چطور بفهمم کران واقعی سرور کار می‌کند؟

سه نشانهٔ هم‌زمان: در لاگ دسترسی، درخواست‌های wp-cron.php در ساعت‌های کم‌بازدید هم دیده شوند؛ در خروجی wp cron event list ستون next_run_relative منفی نداشته باشد؛ و در لاگ خود کران فاصلهٔ اجراها با بازهٔ تنظیم‌شده بخواند. یکی از این سه کافی نیست؛ هر سه با هم یعنی زنجیره سالم است.

آیا برای تنظیم کران واقعی به SSH نیاز دارم؟

نه. ساخت کران در سی‌پنل و DirectAdmin از رابط وب انجام می‌شود و SSH لازم ندارد. SSH فقط برای WP-CLI و اجرای دستی رویدادها لازم است؛ اگر پلن شما SSH ندارد، می‌توانید همان کارها را با افزونه‌ای که فهرست رویدادها را نشان می‌دهد انجام دهید و برای تشخیص، به لاگ دسترسی و خروجی کران تکیه کنید.

جمع‌بندی کوتاه: WP-Cron یک صف است که با بازدید بیدار می‌شود؛ مشکل بیشتر سایت‌ها نه در افزونه، بلکه در همین وابستگی است. اول تشخیص بدهید (فهرست رویدادها، قفل، پاسخ wp-cron.php، لاگ)، بعد کران واقعی را بسازید، و آخر از همه زمان‌بند داخلی را خاموش کنید. لاگ را هم روشن بگذارید؛ بدون آن، دفعهٔ بعد که پشتیبان‌گیری نیامد، دوباره از صفر شروع می‌کنید.

نوشته‌شده و بازبینی‌شده توسط

متخصص سئو، وردپرس و تولید محتوا

اولین نفری باش که نظر می‌ده!

فقط نام لازم است، ایمیل اختیاریه و منتشر نمی‌شه.