Lewati ke konten

Job

Job adalah unit kerja inti di Queuety. Kirim job dengan nama handler dan payload, lalu worker akan mengambil dan menjalankannya di latar belakang.

Mengirim job

PHP
use Queuety\Queuety;

Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

dispatch() mengembalikan builder PendingJob. Anda dapat merangkai opsi sebelum job dikirim secara otomatis:

PHP
use Queuety\Enums\Priority;

Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High )
    ->delay( 300 )
    ->max_attempts( 5 );

Untuk langsung mendapatkan ID job, panggil ->id():

PHP
$job_id = Queuety::dispatch( 'send_email', $payload )->id();

Menulis handler

Handler adalah kelas apa pun yang mengimplementasikan antarmuka Queuety\Handler:

PHP
use Queuety\Handler;

class SendEmailHandler implements Handler {
    public function handle( array $payload ): void {
        wp_mail( $payload['to'], $payload['subject'], $payload['body'] );
    }

    public function config(): array {
        return [
            'queue'           => 'emails',
            'max_attempts'    => 5,
            'backoff'         => 'exponential',
        ];
    }
}

Metode handle() menerima payload job dan menjalankan pekerjaan. Metode config() mengembalikan nilai default opsional:

Kunci Tipe Deskripsi
queue string Nama antrean default
max_attempts int Jumlah maksimum eksekusi, termasuk yang pertama, sebelum job dikubur
backoff string Strategi jeda percobaan ulang: exponential, linear, atau fixed
rate_limit array Batas laju sebagai [max_executions, window_seconds]
concurrency_group string Grup penerimaan worker bersama untuk handler yang mahal
concurrency_limit int Jumlah maksimum job pemrosesan serentak di dalam grup tersebut
cost_units int Biaya eksekusi relatif yang digunakan oleh anggaran antrean atau penyedia berbobot, anggaran alur kerja, dan heuristik penerimaan

Kunci sumber daya ini merupakan bagian dari model Manajemen Sumber Daya Queuety yang lebih luas, yang menyatukan metadata job, anggaran alur kerja, dan penerimaan worker.

Daftarkan handler:

PHP
Queuety::register( 'send_email', SendEmailHandler::class );

Atau gunakan atribut PHP untuk pendaftaran otomatis.

Prioritas

Job memiliki empat tingkat prioritas. Worker mengambil job berprioritas lebih tinggi terlebih dahulu.

PHP
use Queuety\Enums\Priority;

Queuety::dispatch( 'handler', $payload )->with_priority( Priority::Urgent );
Tingkat Nilai Deskripsi
Priority::Low 0 Default. Pekerjaan latar belakang yang dapat menunggu.
Priority::Normal 1 Prioritas standar.
Priority::High 2 Diproses sebelum Low dan Normal.
Priority::Urgent 3 Diproses lebih dahulu. Gunakan seperlunya.

Penundaan

Tunda job agar hanya tersedia setelah jumlah detik yang ditentukan:

PHP
Queuety::dispatch( 'send_reminder', $payload )->delay( 3600 ); // 1 hour

Job langsung dimasukkan, tetapi worker tidak akan mengambilnya sampai waktu penundaan berlalu.

Job unik

Cegah pengiriman duplikat untuk handler dan payload yang sama. Jika sudah ada job tertunda atau sedang diproses dengan handler dan payload yang sama, ID job yang ada akan dikembalikan alih-alih membuat job baru.

PHP
Queuety::dispatch( 'sync_inventory', [ 'sku' => 'ABC-123' ] )->unique();

Dependensi job

Sebuah job dapat bergantung pada job lain. Worker tidak akan mengambilnya sampai dependensinya selesai.

PHP
$first_id = Queuety::dispatch( 'import_data', $payload )->id();

Queuety::dispatch( 'send_notification', [ 'import_id' => $first_id ] )
    ->after( $first_id );

Pengiriman batch

Kirim beberapa job dalam satu INSERT multi-baris untuk throughput maksimum:

PHP
$job_ids = Queuety::batch( [
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'a@example.com' ] ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'b@example.com' ] ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'c@example.com' ] ],
] );

Setiap item dapat menyertakan kunci opsional: queue, priority, delay, max_attempts.

Percobaan ulang dan jeda

Saat job memunculkan pengecualian, Queuety mengembalikannya ke pending dengan available_at tertunda hingga total dijalankan sebanyak max_attempts kali (eksekusi pertama ikut dihitung, jadi max_attempts( 3 ) berarti hingga dua kali percobaan ulang). Penundaan sebelum setiap percobaan ulang ditentukan oleh strategi jeda, dengan attempt sebagai nomor eksekusi yang baru saja gagal:

Strategi Rumus penundaan Contoh (setelah eksekusi 1, 2, 3)
exponential 2^attempt * 30 detik, dibatasi hingga 3600 60s, 120s, 240s
linear attempt * 60 detik 60s, 120s, 180s
fixed 60 detik 60s, 60s, 60s

Tetapkan default global di wp-config.php:

PHP
define( 'QUEUETY_RETRY_BACKOFF', 'linear' );

Atau per handler di config():

PHP
public function config(): array {
    return [ 'backoff' => 'exponential', 'max_attempts' => 5 ];
}

Atau per pengiriman:

PHP
Queuety::dispatch( 'handler', $payload )->max_attempts( 10 );

Pekerjaan yang dikarantina

Setelah semua percobaan ulang habis, job ditandai sebagai dikubur. Job yang dikubur tetap berada di database untuk diperiksa, tetapi tidak diproses. Anda dapat mencoba ulang secara manual. Percobaan ulang manual membuat job pending kembali tanpa mengatur ulang jumlah percobaannya, sehingga job yang dikubur dan dicoba ulang akan mendapat satu kali eksekusi lagi dan dikubur kembali jika eksekusi itu gagal:

PHP
// Retry a specific buried job
Queuety::retry( $job_id );

// Retry all buried jobs
$count = Queuety::retry_buried();

Atau melalui CLI:

Shell
wp queuety retry 42
wp queuety retry-buried

Batas waktu

Queuety mengaktifkan pcntl_alarm() selama waktu eksekusi maksimum dan dirancang untuk menggagalkan percobaan dengan TimeoutException jika batas waktu terlampaui. Batas waktu tersebut saat ini belum diterapkan (#10): worker tidak mengaktifkan sinyal asinkron, sehingga handler yang berjalan lama tetap berjalan hingga selesai, sedangkan handler yang terblokir di sleep() terhenti saat alarm berbunyi lalu ditandai selesai. Konfigurasikan batas waktu secara global:

PHP
define( 'QUEUETY_MAX_EXECUTION_TIME', 300 ); // 5 minutes

Job yang terjebak dalam status processing lebih lama dari QUEUETY_STALE_TIMEOUT detik akan dipulihkan secara otomatis:

PHP
define( 'QUEUETY_STALE_TIMEOUT', 600 ); // 10 minutes

Pulihkan job usang secara manual:

Shell
wp queuety recover

Antrean

Job dikirim ke antrean bernama. Setiap worker memproses satu antrean atau daftar antrean berurutan (lihat Worker):

PHP
Queuety::dispatch( 'handler', $payload )->on_queue( 'emails' );
Shell
wp queuety work --queue=emails

Antrean default adalah default. Anda dapat menjalankan proses worker terpisah untuk setiap antrean guna mengisolasi beban kerja.

Pembatasan laju

Batasi seberapa sering handler dapat dijalankan dalam jangka waktu tertentu:

PHP
Queuety::dispatch( 'call_api', $payload )
    ->rate_limit( 10, 60 ); // max 10 executions per 60 seconds

Lihat Pembatasan Laju untuk detailnya.

Kelas job yang dapat di-dispatch

Queuety v0.6.0 memperkenalkan API job berbasis kelas. Alih-alih memisahkan nama handler, payload, dan kelas handler, Anda dapat menentukan satu kelas yang merangkum semuanya: payload (sebagai properti publik bertipe), logika eksekusi, dan middleware opsional.

Antarmuka Contracts\Job

PHP
use Queuety\Contracts\Job;

interface Job {
    public function handle(): void;
}

Kelas Job menyimpan payload sebagai properti publik dan menjalankan logikanya sendiri di handle(). Metode config() terpisah atau pendaftaran handler tidak diperlukan.

Trait Dispatchable

Tambahkan trait Dispatchable untuk mendapatkan metode statis dispatch() pada kelas job Anda:

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class SendEmailJob implements Job {
    use Dispatchable;

    public function __construct(
        public readonly string $to,
        public readonly string $subject,
        public readonly string $body,
    ) {}

    public function handle(): void {
        wp_mail( $this->to, $this->subject, $this->body );
    }
}

Kirim job dengan memberikan argumen konstruktor:

PHP
SendEmailJob::dispatch( 'user@example.com', 'Welcome!', 'Hello there.' )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High );

Trait Dispatchable memanggil Queuety::dispatch() secara internal, sehingga semua opsi PendingJob (queue, priority, delay, max_attempts, unique, after) berfungsi sama persis.

Cara kerja serialisasi

Saat Anda mengirim instance Job, QueuetyJobSerializer mengekstrak nama kelas berkualifikasi penuh sebagai handler dan semua properti publik sebagai payload. Di sisi worker, serializer membangun kembali instance dengan mencocokkan kunci payload dengan nama parameter konstruktor. Enum backed diserialisasi menjadi nilai dasarnya dan dipulihkan secara otomatis.

Properti pekerjaan

Kelas job dapat mendeklarasikan properti publik untuk mengendalikan perilaku percobaan ulang dan batas waktu. Worker membacanya melalui refleksi dan menerapkannya secara otomatis.

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class ImportProductsJob implements Job {
    use Dispatchable;

    public int $tries = 5;
    public int $timeout = 120;
    public array $backoff = [ 10, 30, 60, 120 ];
    public string $concurrency_group = 'imports';
    public int $concurrency_limit = 2;
    public int $cost_units = 4;

    public function __construct(
        public readonly int $supplier_id,
    ) {}

    public function handle(): void {
        // Import products...
    }
}
Properti Tipe Deskripsi
$tries int Jumlah maksimum percobaan sebelum job dikubur. Menggantikan nilai max_attempts saat pengiriman.
$timeout int Waktu eksekusi maksimum dalam detik. Menggantikan konstanta QUEUETY_MAX_EXECUTION_TIME global. Memerlukan pcntl dan belum diterapkan (lihat Batas Waktu).
$backoff array Array nilai penundaan dalam detik untuk setiap percobaan ulang. Nilai terakhir digunakan kembali untuk percobaan berikutnya.
$concurrency_group string Grup penerimaan worker bersama untuk job yang mahal.
$concurrency_limit int Jumlah maksimum job processing yang berjalan serentak dalam grup tersebut. Diabaikan tanpa $concurrency_group.
$cost_units int Biaya eksekusi relatif yang digunakan oleh anggaran alur kerja dan penerimaan worker.

Array $backoff menyediakan penundaan percobaan ulang yang makin lama. Misalnya, [10, 30, 60, 120] berarti percobaan ulang pertama menunggu 10 detik, kedua 30 detik, ketiga 60 detik, dan percobaan selanjutnya menunggu 120 detik.

Properti terkait sumber daya berguna ketika kelas job mewakili profil biaya yang berbeda. Penyegaran cache yang cepat dan pemanggilan penyedia berskala besar sama-sama dapat disebut "satu job", tetapi belum tentu semestinya dihitung dengan cara yang sama ketika anggaran alur kerja atau keputusan penerimaan worker berupaya menjaga proses tetap dalam batas aman.

Untuk model lengkap, termasuk anggaran alur kerja dan penerimaan di sisi worker, lihat Manajemen Sumber Daya.

Hook kegagalan

Saat job dikubur setelah semua percobaan ulang habis, Queuety memanggil metode failed() pada instance job jika metode tersebut didefinisikan. Gunakan untuk pembersihan, pemberian peringatan, atau pencatatan log.

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class ProcessPaymentJob implements Job {
    use Dispatchable;

    public int $tries = 3;

    public function __construct(
        public readonly int $order_id,
    ) {}

    public function handle(): void {
        // Process the payment...
    }

    public function failed( \Throwable $exception ): void {
        // Notify the team that payment processing failed permanently.
        error_log( "Payment failed for order {$this->order_id}: {$exception->getMessage()}" );
    }
}

Metode failed() menerima pengecualian yang menyebabkan kegagalan terakhir. Instance job dideserialisasi dengan payload aslinya, sehingga semua properti publik tersedia.

Pengiriman bersyarat

Trait Dispatchable menyediakan dispatch_if() dan dispatch_unless() untuk pengiriman bersyarat. Keduanya menerima kondisi boolean sebagai argumen pertama, diikuti argumen konstruktor job.

PHP
// Only dispatch if the user has opted in to notifications.
SendEmailJob::dispatch_if( $user->wants_notifications, $user->email, 'Update', $body );

// Dispatch unless the feature flag is disabled.
SyncInventoryJob::dispatch_unless( $maintenance_mode, $sku );

dispatch_if() mengirim job jika kondisi bernilai true dan mengembalikan PendingJob. Metode ini mengembalikan null jika kondisi bernilai false.

dispatch_unless() mengirim job jika kondisi bernilai false dan mengembalikan PendingJob. Metode ini mengembalikan null jika kondisi bernilai true.

Kedua metode mendukung perangkaian opsi PendingJob saat mengirim job:

PHP
SendEmailJob::dispatch_if( $should_send, $to, $subject, $body )
    ?->on_queue( 'emails' )
    ?->with_priority( Priority::High );

Pengiriman sinkron

Gunakan Queuety::dispatch_sync() untuk menjalankan job segera dalam proses saat ini tanpa menambahkannya ke antrean. Ini berguna untuk pengujian dan job yang harus dijalankan secara inline.

PHP
use Queuety\Queuety;

// With a Job instance
Queuety::dispatch_sync( new SendEmailJob( to: 'user@example.com', subject: 'Hello', body: 'Hi!' ) );

// With a handler name and payload
Queuety::dispatch_sync( 'send_email', [ 'to' => 'user@example.com' ] );

Metode handle() milik job dipanggil secara langsung. Tidak ada baris database yang dibuat, worker tidak dilibatkan, dan middleware tidak diterapkan.

Perantaian job

Perantaian job menjalankan serangkaian job satu per satu. Setiap job dalam rantai bergantung pada keberhasilan penyelesaian job sebelumnya. Jika ada job yang gagal, job berikutnya dalam rantai tetap tertunda.

Menggunakan Queuety::chain()

PHP
use Queuety\Queuety;

$first_job_id = Queuety::chain( [
    new FetchDataJob( $url ),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->catch( ChainFailedHandler::class )
    ->dispatch();

Queuety::chain() mengembalikan ChainBuilder dengan metode fluent untuk mengatur antrean dan handler kegagalan. Metode dispatch() membuat semua job dengan relasi depends_on berurutan dan mengembalikan ID job pertama.

Menggunakan with_chain()

Trait Dispatchable menyediakan metode with_chain(). Saat ini, metode tersebut setara dengan Queuety::chain( $jobs ): kelas tempat metode dipanggil tidak ditambahkan ke rantai (#13), jadi sertakan juga job pertama dalam array:

PHP
FetchDataJob::with_chain( [
    new FetchDataJob(),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->dispatch();

Penanganan kegagalan rantai

Daftarkan kelas handler kegagalan menggunakan catch(). Kelas handler harus memiliki metode handle(). Metode ini dipanggil saat job mana pun dalam rantai dikubur secara permanen. Worker mendukung handle(), handle( Job $job ), dan handle( Job $job, \Throwable $exception ).

PHP
class ChainFailedHandler {
    public function handle(): void {
        // Alert, clean up, etc.
    }
}

Menambahkan middleware

Kelas job dapat mendefinisikan metode middleware() untuk mengembalikan middleware yang membungkus eksekusi:

PHP
use Queuety\Contracts\Job;
use Queuety\Dispatchable;
use Queuety\Middleware\RateLimited;
use Queuety\Middleware\Timeout;

class CallOpenAIJob implements Job {
    use Dispatchable;

    public function __construct(
        public readonly string $prompt,
    ) {}

    public function handle(): void {
        // Call the API...
    }

    public function middleware(): array {
        return [
            new RateLimited( max: 60, window: 60 ),
            new Timeout( seconds: 120 ),
        ];
    }
}

Lihat Middleware untuk detail tentang middleware bawaan dan cara menulis middleware khusus.

Perbandingan dengan job berbasis handler

Berbasis handler (v0.1+) Berbasis kelas (v0.6+)
Dispatch Queuety::dispatch( 'send_email', $payload ) SendEmailJob::dispatch( $to, $subject )
Handler Kelas terpisah yang mengimplementasikan Handler Kelas yang sama mengimplementasikan Contracts\Job
Payload array $payload tanpa tipe Properti publik bertipe
Registrasi Queuety::register() atau #[QueuetyHandler] Tidak diperlukan (menggunakan FQCN)
Middleware Tidak tersedia Metode middleware() opsional
Konfigurasi Metode config() Properti job + rangkaian PendingJob

Kedua pendekatan didukung sepenuhnya. Job berbasis handler yang sudah ada tetap berfungsi tanpa perubahan. Anda dapat mencampur kedua gaya dalam proyek yang sama.

Siklus hidup job

Kode
dispatch() -> pending -> processing -> completed
                  |           |
                  |           +-> exception -> pending (retry after backoff)
                  |                       |
                  |                       +-> buried (max attempts reached)
                  |
                  +-> delayed (available_at in the future)
Status Deskripsi
pending Menunggu untuk diklaim oleh worker
processing Sedang dijalankan
completed Berhasil diselesaikan
failed Dicadangkan; worker tidak menetapkannya saat ini. Percobaan yang gagal dan akan dicoba ulang kembali ke pending
buried Semua percobaan ulang telah habis