انتقل إلى المحتوى

آلات الحالات

تحل آلات الحالات وسير العمل مشكلات مختلفة.

يجيب سير العمل عن سؤال «ما العمل الذي يُنفذ بعد ذلك؟». أما آلة الحالات فتجيب عن «ما حالة هذا الكيان، وما الأحداث الصالحة انطلاقًا منها؟». ويهم هذا الفرق عندما لا يكون ما تبنيه مجرد سلسلة خطوات، بل كيانًا طويل الأمد يستجيب للمدخلات الخارجية مع مرور الوقت، مثل جلسة وكيل أو محادثة أو دورة مراجعة أو تذكرة أو مسار موافقة.

مثال سريع

PHP
use Queuety\Enums\StateMachineStatus;
use Queuety\Queuety;

$machine_id = Queuety::machine( 'agent_session' )
    ->version( 'agent-session.v1' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'awaiting_review' )
    ->state( 'awaiting_review' )
    ->on( 'approve', 'completed', ReviewApprovedGuard::class )
    ->on( 'reject', 'awaiting_user' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );

Queuety::machine_event(
    $machine_id,
    'user_message',
    [ 'message' => 'Find competitors for this product' ]
);

Queuety::machine_event(
    $machine_id,
    'approve',
    [ 'approved' => true, 'reviewer' => 'editor@example.com' ]
);

تبدأ هذه الآلة في awaiting_user، وتنتقل إلى planning عند وصول رسالة من المستخدم، وتتيح للإجراء المدرج في قائمة الانتظار إصدار planned، ثم تنتظر في awaiting_review إلى أن تكمل موافقة خارجية الجلسة. والنقطة المهمة ليست صياغة الكود بحد ذاتها، بل أن دورة الحياة واضحة وصريحة وقابلة للفحص ومدفوعة بالأحداث.

آلية عمل الآلة

عند إرسال نسخة من الآلة، يحفظ Queuety اسم الحالة الحالية، والحمولة العامة للحالة، وإصدار التعريف وتجزئته، والأحداث الواردة الصالحة للحالة الحالية، وسجلًا زمنيًا دائمًا للأحداث.

إذا كانت للحالة التي تم الدخول إليها action()، يضع Queuety مهمة دخول الحالة في قائمة الانتظار. يمكن لهذا الإجراء أن يحدّث الحالة العامة فحسب ويترك الآلة منتظرة الحدث التالي، أو أن يصدر _event و_event_payload اختياريًا لدفع الانتقال التالي فورًا. وإذا لم يكن للحالة إجراء ولا حالة نهائية، تظل الآلة منتظرة الأحداث الخارجية المرسلة باستخدام Queuety::machine_event().

الاختيار بين سير العمل وآلات الحالات

استخدم سير العمل عندما يكون التنفيذ قائمًا أساسًا على الخطوات: جلب البيانات، ومعالجة كل عنصر، والانتظار، والتركيب، ثم الإنهاء. يناسب هذا النموذج الحالات التي لها بداية ونهاية واضحتان، ويكون السؤال الأساسي فيها هو ما العمل الذي ينبغي تنفيذه بعد ذلك.

استخدم آلة الحالات عندما يعتمد السلوك الصالح على مرحلة دورة الحياة الحالية. فهي أنسب عندما تصل الأحداث بترتيبات مختلفة، أو عندما قد يستمر الكيان فترة طويلة، أو عندما تحتاج إلى فحص واضح لدورة الحياة بحالات مثل awaiting_user وplanning وrunning_agents وawaiting_review وcompleted.

كيف يعمل الاثنان معًا

يعمل هذان العنصران الأساسيان على أفضل وجه معًا بدلًا من أن يتنافسا. يمكن لإجراء الحالة إرسال سير عمل عندما يحتاج الانتقال إلى تنفيذ أثقل، ويمكن لسير العمل إصدار حدث إلى الآلة عند انتهاء ذلك العمل. عمليًا، يتيح هذا لآلة الحالات إدارة دورة الحياة، بينما تنفذ تدفقات العمل المهام المكلفة أو ذات البنية المعقدة.

بنية المنشئ

يبقى StateMachineBuilder صغيرًا عمدًا. يعرّف initial() وstate() مخطط دورة الحياة. ويربط action() العمل المدرج في قائمة الانتظار بحالة واحدة. ويحدد on() الأحداث التي يمكنها نقل الآلة إلى الحالة التالية. ويتحكم on_queue() وwith_priority() وmax_attempts() في كيفية تنفيذ إجراءات الحالة، بينما يربط version() وidempotency_key() بيانات الإرسال الدائمة. وينشئ dispatch() نسخة الآلة.

يمكن لإجراءات الحالة أيضًا إتاحة طريقة اختيارية config() باستخدام مفاتيح سياسة الموارد نفسها التي تستخدمها خطوات سير العمل ومعالجاته. يتيح ذلك إبقاء انتقال مكلف واحد ضمن مجموعة تزامن مشتركة، أو إسناد وزن تكلفة أعلى عندما يستهلك الإجراء ميزانية أكبر من خطوة عادية.

يُشرح نموذج الموارد الأوسع في Resource Management.

العقود

تطبق إجراءات دخول الحالة Queuety\Contracts\StateAction:

PHP
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}

$payload هي الحمولة المنظّمة من تعريف الإجراء، وتكون فارغة عندما يُمرر الإجراء كاسم فئة عادي. ينشئ Queuety الإجراءات باستخدام new $class()، لذا تحتاج فئة الإجراء إلى مُنشئ لا يتطلب وسائط.

يمكن لإجراءات الحالة أيضًا تعريف طريقة اختيارية config() تضم concurrency_group وconcurrency_limit وcost_units عندما ينبغي لإجراء الدخول المدرج في قائمة الانتظار المشاركة في قبول العامل، أو ميزانيات قوائم الانتظار أو المزودين الموزونة، أو سياسات الموارد الأخرى.

تطبق حواجز الانتقال Queuety\Contracts\StateGuard:

PHP
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}

واجهات برمجة الفحص

استخدم الواجهة الوسيطة لفحص الآلات قيد التشغيل:

PHP
$status   = Queuety::machine_status( $machine_id );
$machines = Queuety::list_machines();
$events   = Queuety::machine_timeline( $machine_id );
$trace    = Queuety::machine_trace( $machine_id );
$state    = Queuety::machine_state_at( $machine_id, $event_id );

لا توجد أوامر WP-CLI لآلات الحالات: يغطي wp queuety workflow ... تدفقات العمل فقط. استخدم واجهات برمجة التطبيقات PHP هذه، مثلًا عبر wp eval، لفحص الآلات من سطر الأوامر.

تكشف واجهات برمجة التطبيقات هذه اسم الحالة الحالية، وحالة دورة الحياة، والحالة العامة، والأحداث المتاحة، وبيانات التعريف الخاصة بالتعريف، والسجل الزمني الكامل للانتقالات، وحزمة تتبع موحّدة لواجهات مصحح الأخطاء. وتتضمن صفوف قائمة الآلات إصدار التعريف وتجزئته ومفتاح منع التكرار، كي تعرض شاشات القوائم إصدار المصدر ووقت التشغيل دون تحميل كل آلة على حدة. وتضم حزمة التتبع حالات مجمعة، وأحداثًا خامًا، ومهام إجراءات مترابطة، وسجلات، ومراجع للملفات الاصطناعية، ومراجع للمقاطع، ومدخلات المعالج ومخرجاته، والحالة قبل الإجراء وبعده، والسياق، والتوقيتات، والأخطاء المنظّمة.

جلسات الوكلاء

تفيد آلات الحالات خصوصًا في أنظمة الوكلاء التي تعمل كجلسات أكثر من كونها خطوط معالجة. فعادةً ما يكون من الأسهل فهم جلسة تنتقل عبر awaiting_user وplanning وrunning_agents وawaiting_review وcompleted بوصفها دورة حياة صريحة، بدلًا من تعريف سير عمل طويل جدًا يضم فترات انتظار كثيرة وانتقالات رجعية.

راجع أيضًا