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

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

سه مزیت اصلی آن، تفکیک جدول اختصاصی، چرخه وضعیت دقیق برای هر تسک و مکانیزم تلاش مجدد مبتنی بر قاعده است.

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

هدف این نوشته، روشن‌کردن معماری داخلی Action Scheduler و الگوهای عملی استفاده از آن در محیط تولید است.

در پروژه‌ای که با حجم بالای سفارش روزانه روبه‌رو بود، یکی از مشکل‌های تکرارشونده، از دست رفتن ایمیل‌های تأییدیه در ساعات اوج بود. ریشه در WP-Cron بود که تسک‌ها را در همان درخواست کاربر اجرا می‌کرد و در بار سنگین، نیمی از آن‌ها را نیمه‌کاره رها می‌کرد. انتقال به Action Scheduler، نه‌فقط مشکل ایمیل را حل کرد، بلکه امکان ردیابی دقیق هر تسک و تلاش مجدد خودکار را فراهم کرد. این تجربه، نقطه‌ای بود که این کتابخانه به یک انتخاب پیش‌فرض در پروژه‌های جدی تبدیل شد.

چرا Action Scheduler جای WP-Cron را گرفت

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

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

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

محدودیت سوم، نبود ثبت دقیق وضعیت است. WP-Cron تنها می‌داند که یک تسک زمان‌بندی شده است یا نه. نمی‌داند آیا اجرا شده، موفق بوده یا شکست خورده. این نبود ثبت، عیب‌یابی را تقریباً غیرممکن می‌کند و مدیریت خطا را به سطح حدس تقلیل می‌دهد.

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

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

تفاوت اصلی میان یک زمان‌بند و یک صف، در ثبت وضعیت است؛ چیزی که نمی‌توان آن را اندازه گرفت، نمی‌توان مدیریت کرد.

Action Scheduler دقیقاً چیست و چه چیزی نیست

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

نکته مهم این است که Action Scheduler یک سیستم صف خارجی نیست. این کتابخانه در همان پایگاه داده وردپرس کار می‌کند و نیازی به Redis، RabbitMQ یا سرویس‌های مشابه ندارد. همین ویژگی، پیاده‌سازی آن را در پروژه‌های معمولی ساده می‌کند.

مسئله دوم که باید روشن شود، رابطه این کتابخانه با Async Tasks است. Async Task یک الگوی کلی است که می‌تواند با مکانیزم‌های مختلف پیاده شود. Action Scheduler یکی از این مکانیزم‌هاست و شاید کامل‌ترین آن‌ها در اکوسیستم وردپرس. مقایسه این دو سطح تحلیل در Async Tasks در وردپرس انجام شده است.

رابطه آن با Background Processing نیز مشابه است. Background Processing یک معماری کامل است که شامل صف، Worker و محرک می‌شود. Action Scheduler بخش صف و مدیریت چرخه تسک‌ها را فراهم می‌کند، اما محرک همچنان باید از طریق Cron واقعی تأمین شود. این تفکیک نقش‌ها در Background Processing در وردپرس به‌طور کامل بررسی شده است.

نکته آخر، این کتابخانه خودش تسک‌ها را اجرا نمی‌کند؛ فقط آن‌ها را مدیریت می‌کند. اجرای تسک‌ها توسط Runner انجام می‌شود که می‌تواند از طریق یک محرک دوره‌ای، یک درخواست REST یا خط فرمان فعال شود.

معماری داخلی: جداول، Claim و Runner

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

سه جدول اصلی وجود دارد. جدول اول، actionscheduler_actions است که خود تسک‌ها را نگه می‌دارد. هر ردیف این جدول یک تسک را با تمام پارامترهایش نشان می‌دهد. جدول دوم، actionscheduler_claims است که مکانیزم قفل‌گذاری را پیاده می‌کند. جدول سوم، actionscheduler_logs است که رویدادهای هر تسک را ثبت می‌کند.

جدول actionscheduler_actions ستون‌های کلیدی دارد که هر یک نقش مشخصی ایفا می‌کند. ستون hook نام هوکی است که برای اجرای تسک فراخوانی می‌شود. ستون status وضعیت فعلی تسک را نگه می‌دارد. ستون scheduled_date_gmt زمان موعد اجرا را مشخص می‌کند. ستون args پارامترهای تسک را به‌صورت سریالایز شده نگه می‌دارد.

SELECT status, COUNT(*) AS count
FROM wp_actionscheduler_actions
GROUP BY status;

این کوئری ساده، توزیع تسک‌ها بر اساس وضعیت را نشان می‌دهد. اگر تعداد تسک‌های در وضعیت pending بالا باشد، نشانه ظرفیت ناکافی Runner است. اگر تعداد تسک‌های failed بالا باشد، نشانه مشکل در منطق اجرا یا در وابستگی‌های خارجی است.

ستون status مقادیر مشخصی می‌گیرد. مقدار pending یعنی تسک در صف است و هنوز اجرا نشده. مقدار in-process یعنی تسک در حال اجراست. مقدار complete یعنی تسک با موفقیت اجرا شده. مقدار failed یعنی تسک شکست خورده. مقدار canceled یعنی تسک لغو شده است.

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

CREATE TABLE wp_actionscheduler_claims (
    claim_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    date_created_gmt DATETIME NOT NULL,
    PRIMARY KEY (claim_id),
    KEY date_created_gmt (date_created_gmt)
);

Runner فرآیندی است که تسک‌ها را از صف برمی‌دارد و اجرا می‌کند. این فرآیند می‌تواند به‌صورت دوره‌ای توسط Cron واقعی، به‌صورت درخواست REST یا به‌صورت خط فرمان فعال شود. در هر حالت، منطق اجرا یکسان است: دریافت دسته‌ای از تسک‌ها، درج Claim، اجرای تسک و ثبت نتیجه.

پیکربندی درست محرک Runner، بخشی از بهینه‌سازی سرور برای وردپرس است. بدون محرک پایدار، حتی بهترین معماری صف هم بی‌اثر می‌ماند.

API اصلی: از as_enqueue_async_action تا as_schedule_recurring_action

Action Scheduler مجموعه‌ای از توابع را برای مدیریت تسک‌ها فراهم می‌کند. هر تابع برای یک سناریوی خاص طراحی شده است و انتخاب درست میان آن‌ها، بخشی از پیاده‌سازی حرفه‌ای است.

تابع as_enqueue_async_action یک تسک را برای اجرای فوری در صف قرار می‌دهد. تسک در اولین فرصت توسط Runner برداشته می‌شود.

as_enqueue_async_action(
    "wpk_send_welcome_email",
    array( "user_id" => 123 ),
    "wpk_emails"
);

تابع as_schedule_single_action یک تسک را برای اجرا در یک زمان مشخص در آینده ثبت می‌کند. این تابع برای تعویق عملیات مناسب است.

as_schedule_single_action(
    time() + 3600,
    "wpk_followup_email",
    array( "user_id" => 123 ),
    "wpk_emails"
);

تابع as_schedule_recurring_action یک تسک تکرارشونده می‌سازد که در بازه‌های مشخص اجرا می‌شود. این تابع جایگزین مستقیم WP-Cron برای تسک‌های زمان‌بندی‌شده است.

as_schedule_recurring_action(
    time(),
    DAY_IN_SECONDS,
    "wpk_daily_report",
    array(),
    "wpk_reports"
);

تابع as_schedule_cron_action یک تسک را بر اساس یک الگوی Cron استاندارد زمان‌بندی می‌کند. این تابع برای زمان‌بندی‌های پیچیده مناسب است.

as_schedule_cron_action(
    time(),
    "0 8 * * MON",
    "wpk_weekly_cleanup",
    array(),
    "wpk_maintenance"
);

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

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

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

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

چرخه حیات یک Action در پایگاه داده

هر تسک در Action Scheduler یک چرخه حیات مشخص را طی می‌کند که از لحظه ثبت آغاز می‌شود و در لحظه تکمیل یا شکست پایان می‌یابد. درک این چرخه برای عیب‌یابی و بهینه‌سازی ضروری است.

مرحله اول، ثبت است. تابع API یک ردیف جدید در جدول actionscheduler_actions درج می‌کند. وضعیت اولیه این ردیف pending است و زمان موعد اجرا در ستون scheduled_date_gmt ثبت می‌شود.

مرحله دوم، برداشت است. Runner به‌طور دوره‌ای جدول را برای تسک‌های موعد‌رسیده با وضعیت pending بررسی می‌کند. برای هر تسک، ابتدا یک ردیف در جدول actionscheduler_claims درج می‌شود تا از اجرای همزمان جلوگیری شود.

مرحله سوم، اجرا است. Runner وضعیت تسک را به in-process تغییر می‌دهد و هوک مربوطه را با پارامترهای تسک فراخوانی می‌کند. اگر هوک با موفقیت اجرا شود، تسک به وضعیت complete منتقل می‌شود.

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

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

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

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

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

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

Runner و مکانیزم اجرای تسک‌ها

Runner قلب اجرایی Action Scheduler است. این جزء، تسک‌ها را از جدول برداشت می‌کند و با فراخوانی هوک مربوطه، به اجرا درمی‌آورد. شناخت رفتار Runner، بخش کلیدی طراحی یک سیستم پایدار است.

Runner به سه روش می‌تواند فعال شود. روش اول، از طریق WP-Cron است که به‌صورت دوره‌ای فراخوانی می‌شود. روش دوم، از طریق درخواست REST که یک محرک خارجی می‌تواند فراخوانی کند. روش سوم، از طریق خط فرمان که با دستورات WP-CLI اجرا می‌شود.

wp action-scheduler run --batch=25 --batches=10

این دستور، ده دسته بیست‌وپنج‌تایی تسک را اجرا می‌کند. پارامتر --batch اندازه هر دسته و پارامتر --batches تعداد دسته‌ها را تعیین می‌کند. انتخاب اندازه دسته، تصمیم مهمی است که بر مصرف حافظه و بار پایگاه داده اثر می‌گذارد.

مکانیزم Claim در Runner از چند جنبه اهمیت دارد. اول، جلوگیری از اجرای همزمان یک تسک توسط چند Runner. دوم، امکان بازیابی پس از شکست است. اگر Runner در میانه اجرا از کار بیفتد، Claim آن پس از بازه مشخصی منقضی می‌شود و Runner دیگری می‌تواند تسک را بردارد.

نکته ظریف در مکانیزم Claim، تعیین بازه انقضا است. اگر این بازه کوتاه باشد، احتمال اجرای همزمان افزایش می‌یابد. اگر طولانی باشد، تسک‌های متوقف‌شده مدت زمان زیادی در حالت معلق می‌مانند. بازه پیش‌فرض پنج دقیقه است که برای بیشتر سناریوها متعادل است.

Runner در هر چرخه، تسک‌های را به ترتیب زمان موعد برداشت می‌کند. این ترتیب FIFO (First In First Out) برای بیشتر سناریوها مناسب است، اما در سناریوهای اولویت‌دار، ممکن است نیاز به پیاده‌سازی سفارشی داشته باشد.

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

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

تلاش مجدد، شکست و سیاست‌های بازیابی

مکانیزم تلاش مجدد یکی از متمایزکننده‌های Action Scheduler است. در WP-Cron، یک تسک شکست‌خورده به سادگی از دست می‌رود. در Action Scheduler، تسک می‌تواند برای چند بار تلاش شود و در نهایت نتیجه آن به‌طور دقیق ثبت گردد.

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

add_action( "action_scheduler_failed_execution", function ( $action_id, $e, $context ) {
    $retries = (int) get_action_scheduler_meta( $action_id, "retry_count", 0 );
    if ( $retries < 3 ) {
        update_action_scheduler_meta( $action_id, "retry_count", $retries + 1 );
        $action = ActionScheduler::store()->fetch_action( $action_id );
        ActionScheduler::factory()->store()->save_action( clone $action );
    }
}, 10, 3 );

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

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

تصمیم دوم، بازه فاصله بین تلاش‌هاست. اگر بازه کوتاه باشد، تسک‌های شکست‌خورده می‌توانند به‌طور مکرر منابع سرور را مصرف کنند. اگر بازه طولانی باشد، تأخیر در بازیابی افزایش می‌یابد. الگوی استاندارد، افزایش تصاعدی فاصله بین تلاش‌ها است.

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

پایش تسک‌های شکست‌خورده یکی از بخش‌های کلیدی نگهداری این سیستم است. اگر این تسک‌ها پایش نشوند، شکست‌های تکراری می‌توانند برای مدت طولانی پنهان بمانند. پایش دوره‌ای با استفاده از دستورات CLI، یک اقدام پایه است.

wp action-scheduler list --status=failed --per_page=50
wp action-scheduler list --status=pending --per_page=50

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

وابستگی به Cron واقعی و پیکربندی سرور

Action Scheduler بدون یک محرک منظم به‌طور مؤثر کار نمی‌کند. حتی اگر صف به‌درستی پر شود، بدون Runner تسک‌ها انباشته می‌شوند. به همین دلیل، پیکربندی محرک یکی از بخش‌های حیاتی راه‌اندازی این کتابخانه است.

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

// In wp-config.php
define( "DISABLE_WP_CRON", true );

// Real system cron entry
# /etc/cron.d/wp-cron
*/2 * * * * www-data /usr/bin/php /var/www/html/wp-cron.php >/dev/null 2>&1

این پیکربندی، اطمینان می‌دهد که Runner هر دو دقیقه یک‌بار فعال می‌شود، مستقل از بازدید کاربران. در سایت‌های پربازدید، می‌توان این بازه را کوتاه‌تر کرد تا تسک‌ها با تأخیر کمتری اجرا شوند.

مسئله مهم، جلوگیری از اجرای همزمان چند Cron است. اگر اجرای قبلی هنوز در حال اجرا باشد و اجرای بعدی آغاز شود، دو Runner همزمان روی صف کار می‌کنند. مکانیزم Claim از اجرای همزمان یک تسک جلوگیری می‌کند، اما بار اضافی روی سرور ایجاد می‌شود. استفاده از قفل در سطح اسکریپت Cron، این مشکل را حل می‌کند.

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

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

در پروژه‌هایی که از طریق خط لوله استقرار مدیریت می‌شوند، تنظیم Cron باید بخشی از اسکریپت راه‌اندازی باشد. این موضوع در پیاده‌سازی CI/CD برای پروژه‌های وردپرسی به‌عنوان یک مرحله استاندارد توصیه شده است.

پیامدهای عملکردی روی پایگاه داده

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

سه نوع کوئری اصلی در این کتابخانه وجود دارد. کوئری درج برای ثبت تسک جدید، کوئری به‌روزرسانی برای تغییر وضعیت تسک و کوئری انتخاب برای برداشت تسک‌های موعد‌رسیده. هر سه این کوئری‌ها باید بهینه باشند تا بار پایگاه داده در حد قابل قبول بماند.

حجم جدول actionscheduler_actions یکی از پارامترهای کلیدی است. اگر این جدول حجیم شود، کوئری‌های برداشت کند می‌شوند و Runner برای پیدا کردن تسک‌های موعد‌رسیده زمان بیشتری صرف می‌کند.

SELECT COUNT(*) AS total,
       SUM(CASE WHEN status = "complete" THEN 1 ELSE 0 END) AS completed,
       SUM(CASE WHEN status = "pending" THEN 1 ELSE 0 END) AS pending,
       SUM(CASE WHEN status = "failed" THEN 1 ELSE 0 END) AS failed
FROM wp_actionscheduler_actions;

این کوئری، توزیع تسک‌ها را بر اساس وضعیت نشان می‌دهد. اگر تعداد کل تسک‌ها در میلیون‌ها باشد و بخش بزرگی از آن‌ها در وضعیت complete باقی مانده باشند، نشانه‌ای است که پاک‌سازی دوره‌ای به‌درستی انجام نمی‌شود.

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

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

بهینه‌سازی این جدول در چارچوب کلی بهینه‌سازی پایگاه داده قرار می‌گیرد. اصول کلی ایندکس‌گذاری و تحلیل کوئری در بهینه‌سازی جداول MySQL برای سرعت آمده و در این حوزه مستقیماً کاربرد دارد.

نکته مهم دیگر، رفتار جدول در زمان بار بالاست. اگر تعداد بالای تسک‌ها همزمان ثبت شوند، رقابت بر روی درج می‌تواند به کندی منجر شود. استفاده از تراکنش‌های کوچک و ثبت تسک‌ها به‌صورت دسته‌ای، این رقابت را کاهش می‌دهد. اصول کلی این موضوع در تراکنش‌ها در MySQL بررسی شده است.

صف تسک، هرچه بزرگ‌تر شود، بار آن سنگین‌تر می‌شود؛ پاک‌سازی دوره‌ای، هزینه‌ای است که همیشه باید پرداخت شود.

کاربرد در فروشگاه ووکامرس

ووکامرس یکی از بزرگ‌ترین مصرف‌کننده‌های Action Scheduler در اکوسیستم وردپرس است. بیشتر عملیات پس‌زمینه این افزونه از طریق این کتابخانه مدیریت می‌شود.

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

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

راه‌حل این مشکل در سه سطح قابل پیاده‌سازی است. سطح اول، افزایش ظرفیت Runner با کوتاه‌کردن بازه محرک. سطح دوم، افزایش تعداد تسک‌ها در هر دسته با تنظیم پارامترهای اجرا. سطح سوم، پیاده‌سازی چند Runner همزمان با استفاده از مکانیزم Claim.

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

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

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

مقایسه با پیاده‌سازی سفارشی و صف‌های خارجی

Action Scheduler یکی از سه گزینه اصلی برای پیاده‌سازی صف تسک در وردپرس است. مقایسه دقیق این سه گزینه به انتخاب درست کمک می‌کند.

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

گزینه دوم، Action Scheduler است. مزیت این رویکرد، پایداری، پشتیبانی گسترده و یکپارچگی با اکوسیستم است. هزینه آن، وابستگی به پایگاه داده و بار اضافی روی آن در حجم بالا است.

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

معیارپیاده سفارشیAction Schedulerصف خارجی
کنترلکاملمتوسطمتوسط
پیچیدگی راه‌اندازیبالاپایینبالا
عملکرد در حجم بالامتوسطمتوسطبالا
بار نگهداریبالاپایینمتوسط
یکپارچگی با اکوسیستمپایینبالاپایین

انتخاب میان این سه گزینه به سه معیار بستگی دارد. معیار اول، حجم تسک‌ها است. برای حجم متوسط، Action Scheduler معمولاً کافی است. برای حجم بسیار بالا، صف خارجی مناسب‌تر است.

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

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

در بیشتر پروژه‌های وردپرسی، Action Scheduler یک نقطه شروع متعادل است. با رشد پروژه، امکان مهاجرت به صف خارجی وجود دارد. اصول این مهاجرت در Background Processing در وردپرس به‌عنوان یک مسیر تکاملی معرفی شده است.

پایش، CLI و اشکال‌زدایی

پایش یک سیستم Action Scheduler نیازمند چند ابزار مشخص است. ترکیب این ابزارها، تصویر کاملی از وضعیت صف ارائه می‌دهد.

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

wp action-scheduler list --status=pending --per_page=20
wp action-scheduler list --status=failed --per_page=20
wp action-scheduler run --batch=25 --batches=10
wp action-scheduler clean --before="30 days ago"

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

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

SELECT hook, COUNT(*) AS count
FROM wp_actionscheduler_actions
WHERE status = "failed"
GROUP BY hook
ORDER BY count DESC
LIMIT 10;

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

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

ابزار چهارم، هدرهای REST است. اگر Runner از طریق REST API فعال می‌شود، وضعیت این Endpoint باید در پایش قرار بگیرد. تعداد درخواست‌های موفق و ناموفق، شاخص‌های کلیدی این لایه هستند.

در لایه تجربه کاربر، اثر Action Scheduler روی زمان پاسخ و پایداری سایت اندازه‌گیری می‌شود. بهبود این معیارها، هدف اصلی پیاده‌سازی صف است. معیارهای مرتبط با این اندازه‌گیری در کاهش زمان TTFB و تأثیر سرعت سایت بر سئو بررسی شده است.

جدول تصمیم‌گیری استفاده از Action Scheduler

سناریونوع تسکگروه پیشنهادیریسک اصلی
ارسال ایمیل پس از ثبت‌نامas_enqueue_async_actionwpk_emailsانباشت در اوج ثبت‌نام
یادآوری سبد رهاشدهas_schedule_single_actionwpk_emailsتأخیر در اجرای زمان‌بندی
گزارش روزانه فروشas_schedule_recurring_actionwpk_reportsبار پایگاه داده در حجم بالا
همگام‌سازی با سرویس خارجیas_enqueue_async_actionwpk_syncشکست مکرر در اختلال سرویس
پاک‌سازی دوره‌ای دادهas_schedule_cron_actionwpk_maintenanceاجرای همزمان با اوج ترافیک

اشتباهات رایج

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

دومین اشتباه، نادیده گرفتن پاک‌سازی دوره‌ای است. جدول actionscheduler_actions می‌تواند در طول چند ماه به میلیون‌ها ردیف برسد. این حجم، کوئری‌های برداشت و به‌روزرسانی را کند می‌کند و بار پایگاه داده را چند برابر می‌کند.

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

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

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

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

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

هشتمین اشتباه، نبود پایش تسک‌های شکست‌خورده است. اگر نرخ شکست به‌طور دوره‌ای بررسی نشود، روند مشکل‌دار می‌تواند برای مدت طولانی پنهان بماند.

نهمین اشتباه، نادیده گرفتن امنیت Endpointهای فعال‌سازی Runner است. اگر این Endpointها محافظت نشوند، امکان سوءاستفاده از آن‌ها برای اشباع سرور فراهم می‌شود. رعایت اصول مشابه آنچه در امن‌سازی ورود ادمین وردپرس آمده، در این لایه نیز ضروری است.

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

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

آیا Action Scheduler جایگزین WP-Cron است؟

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

Action Scheduler چطور با تسک‌های شکست‌خورده برخورد می‌کند؟

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

چگونه بفهمم Action Scheduler در حال اجراست؟

سه نشانه مشخص وجود دارد: تعداد تسک‌های pending در جدول کاهش می‌یابد، تسک‌های complete در حال افزایش هستند و لاگ Cron اجرای دوره‌ای را نشان می‌دهد. اگر هیچ‌کدام از این‌ها دیده نشود، احتمالاً محرک از کار افتاده است.

آیا Action Scheduler برای سایت‌های کوچک مناسب است؟

برای سایت‌های کوچک با تسک‌های سبک، ممکن است WP-Cron کافی باشد. اما اگر تسک‌های سنگین یا حساس به زمان وجود دارد، Action Scheduler حتی در سایت‌های کوچک نیز مفید است.

تفاوت as_enqueue_async_action و as_schedule_single_action چیست؟

as_enqueue_async_action تسک را برای اجرا در اولین فرصت ممکن در صف قرار می‌دهد. as_schedule_single_action تسک را برای اجرا در یک زمان مشخص در آینده ثبت می‌کند.

چگونه تسک‌های قدیمی را پاک کنم؟

با دستور wp action-scheduler clean --before="30 days ago". این دستور تسک‌های تکمیل‌شده و شکست‌خورده قدیمی‌تر از بازه مشخص را از جدول حذف می‌کند.

آیا Action Scheduler روی سرعت سایت اثر منفی دارد؟

در طراحی درست، بار صف از مسیر پاسخ کاربر جدا می‌شود و اثر منفی ندارد. اگر صف بسیار حجیم باشد یا پاک‌سازی دوره‌ای انجام نشود، بار پایگاه داده می‌تواند افزایش یابد.

آیا Action Scheduler از چند سرور پشتیبانی می‌کند؟

بله، اما نیازمند پیکربندی دقیق است. جدول صف باید مشترک باشد و مکانیزم Claim باید هماهنگی بین چند Runner را تضمین کند. در این حالت، استفاده از یک سرور اختصاصی برای اجرای Cron توصیه می‌شود.

چگونه تسک‌های یک افزونه را پاک کنم؟

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

آیا Action Scheduler امن است؟

این کتابخانه از مکانیزم‌های امنیتی استاندارد وردپرس استفاده می‌کند، اما Endpointهای فعال‌سازی Runner باید به‌درستی محافظت شوند. احراز هویت با توکن و محدودسازی نرخ درخواست، دو اقدام ضروری هستند.

آیا می‌توان Action Scheduler را برای حجم بسیار بالا استفاده کرد؟

تا حجم مشخصی بله. برای حجم‌های بسیار بالا (میلیون‌ها تسک در روز)، استفاده از یک صف خارجی مانند Redis Queue یا سیستم‌های پیام‌رسان مناسب‌تر است.

چگونه بین Action Scheduler و صف خارجی انتخاب کنیم؟

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

یک نکته برای ادامه مسیر

Action Scheduler یکی از آن کتابخانه‌هایی است که پیکربندی درست آن، تفاوت محسوسی در پایداری سایت می‌سازد. صف تسک، جدول اختصاصی و مکانیزم تلاش مجدد، یک زیرساخت کامل برای اجرای پس‌زمینه فراهم می‌کنند. اما این زیرساخت بدون پایش، پاک‌سازی و محرک پایدار، در طول زمان به یک بدهی فنی تبدیل می‌شود.

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