본문으로 건너뛰기

작업

작업은 Queuety의 핵심 작업 단위입니다. 핸들러 이름과 페이로드를 지정해 작업을 디스패치하면 워커가 이를 가져와 백그라운드에서 실행합니다.

작업 디스패치

PHP
use Queuety\Queuety;

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

dispatch()은 PendingJob 빌더를 반환합니다. 자동으로 디스패치되기 전에 옵션을 연결할 수 있습니다:

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 );

작업 ID를 즉시 가져오려면 ->id()을 호출하세요:

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

핸들러 작성

핸들러는 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',
        ];
    }
}

handle() 메서드는 작업 페이로드를 받아 작업을 수행합니다. config() 메서드는 선택적 기본값을 반환합니다:

키 유형 설명
queue string 기본 큐 이름
max_attempts int 작업이 묻히기 전 최초 실행을 포함한 최대 실행 횟수
backoff string 재시도 백오프 전략: exponential, linear 또는 fixed
rate_limit array [max_executions, window_seconds] 형식의 속도 제한
concurrency_group string 비용이 큰 핸들러를 위한 공유 워커 승인 그룹
concurrency_limit int 해당 그룹에서 동시에 처리할 수 있는 최대 작업 수
cost_units int 가중치 기반 큐 또는 제공자 예산, 워크플로우 예산 및 승인 휴리스틱에 사용되는 상대적 실행 비용

이러한 리소스 키는 작업 메타데이터, 워크플로우 예산 및 워커 승인이 함께 작동하는 Queuety의 더 광범위한 리소스 관리 모델의 일부입니다.

핸들러를 등록하세요:

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

또는 자동 등록에는 PHP 속성을 사용하세요.

우선순위

작업에는 네 가지 우선순위 수준이 있습니다. 작업자는 우선순위가 높은 작업부터 가져갑니다.

PHP
use Queuety\Enums\Priority;

Queuety::dispatch( 'handler', $payload )->with_priority( Priority::Urgent );
수준 값 설명
Priority::Low 0 기본값. 기다려도 되는 백그라운드 작업입니다.
Priority::Normal 1 표준 우선순위입니다.
Priority::High 2 Low 및 Normal보다 먼저 처리됩니다.
Priority::Urgent 3 가장 먼저 처리됩니다. 필요한 경우에만 사용하세요.

지연

지정한 시간(초)이 지난 후에만 작업을 사용할 수 있도록 지연하세요:

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

작업은 즉시 삽입되지만, 지연 시간이 경과할 때까지 작업자가 가져가지 않습니다.

고유 작업

동일한 핸들러와 페이로드로 작업이 중복 디스패치되지 않도록 방지합니다. 동일한 핸들러와 페이로드를 가진 대기 중이거나 처리 중인 작업이 이미 있으면 새 작업을 만들지 않고 기존 작업 ID를 반환합니다.

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

작업 종속성

작업은 다른 작업에 종속될 수 있습니다. 종속 작업이 완료될 때까지 작업자가 해당 작업을 가져가지 않습니다.

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

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

일괄 디스패치

처리량을 극대화하려면 여러 작업을 한 번의 다중 행 INSERT로 디스패치하세요:

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' ] ],
] );

각 항목에는 선택적 키 queue, priority, delay, max_attempts를 포함할 수 있습니다.

재시도 및 백오프

작업에서 예외가 발생하면 Queuety은 작업을 pending에 다시 넣고 지연된 available_at을 적용하여 총 max_attempts회 실행될 때까지 재시도합니다(첫 실행도 횟수에 포함되므로 max_attempts( 3 )는 최대 두 번 재시도한다는 뜻입니다). 재시도 전 지연 시간은 백오프 전략에 따라 결정되며, attempt는 방금 실패한 실행의 번호입니다:

전략 지연 시간 공식 예시 (1, 2, 3회 실행 후)
exponential 2^attempt * 30초, 최대 3600 60s, 120s, 240s
linear attempt * 60초 60s, 120s, 180s
fixed 60초 60s, 60s, 60s

전역 기본값은 wp-config.php에서 설정하세요:

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

핸들러별 설정은 config()에서 하세요:

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

또는 디스패치별로 설정할 수 있습니다:

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

보관된 작업

모든 재시도 횟수를 소진하면 작업은 묻힌 상태로 표시됩니다. 묻힌 작업은 검사할 수 있도록 데이터베이스에 남지만 처리되지는 않습니다. 수동으로 재시도할 수 있습니다. 수동 재시도는 시도 횟수를 초기화하지 않고 작업을 다시 pending 상태로 만들므로, 재시도된 묻힌 작업은 한 번 더 실행되고 그 실행이 실패하면 다시 묻힙니다:

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

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

또는 CLI을 통해:

셸
wp queuety retry 42
wp queuety retry-buried

시간 초과

Queuety은 최대 실행 시간 동안 pcntl_alarm()을 설정하며, 시간이 초과되면 TimeoutException로 시도를 실패 처리하도록 설계되었습니다. 현재 이 시간 초과는 적용되지 않습니다(#10). 작업자가 비동기 신호를 활성화하지 않으므로 장시간 실행되는 핸들러는 끝까지 실행되고, sleep()에서 차단된 핸들러는 알람이 울리면 중단된 뒤 완료된 것으로 표시됩니다. 전역 시간 초과를 설정하세요:

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

QUEUETY_STALE_TIMEOUT초보다 오래 processing 상태에 머무는 작업은 자동으로 복구됩니다:

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

오래된 작업을 수동으로 복구합니다.

셸
wp queuety recover

큐

작업은 이름이 지정된 큐로 디스패치됩니다. 각 작업자는 하나의 큐 또는 순서가 지정된 큐 목록을 처리합니다(작업자 참고):

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

기본 큐는 default입니다. 워크로드를 격리하려면 큐마다 별도의 작업자 프로세스를 실행할 수 있습니다.

속도 제한

시간 구간 내에서 핸들러가 실행되는 횟수를 제한합니다:

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

자세한 내용은 속도 제한을 참고하세요.

디스패치 가능한 작업 클래스

Queuety v0.6.0은 클래스 기반 작업 API을 도입합니다. 핸들러 이름, 페이로드, 핸들러 클래스를 각각 분리하는 대신 페이로드(타입이 지정된 public 속성), 실행 로직, 선택적 미들웨어를 모두 캡슐화하는 단일 클래스를 정의합니다.

Contracts\Job 인터페이스

PHP
use Queuety\Contracts\Job;

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

Job 클래스는 페이로드를 public 속성으로 보유하고 handle()에서 자체 로직을 실행합니다. 별도의 config() 메서드나 핸들러 등록은 필요하지 않습니다.

Dispatchable 트레이트

Dispatchable 트레이트를 추가하면 작업 클래스에 정적 dispatch() 메서드가 생깁니다:

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 );
    }
}

생성자 인수를 전달해 디스패치하세요:

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

Dispatchable 트레이트는 내부적으로 Queuety::dispatch()을 호출하므로 모든 PendingJob 옵션(queue, priority, delay, max_attempts, unique, after)이 동일하게 작동합니다.

직렬화 작동 방식

Job 인스턴스를 디스패치하면 Queuety의 JobSerializer가 정규화된 클래스 이름을 핸들러로, 모든 public 속성을 페이로드로 추출합니다. 작업자 측에서 직렬화기는 페이로드 키를 생성자 매개변수 이름에 맞춰 인스턴스를 재구성합니다. 백드 enum은 내부 값으로 직렬화되고 자동으로 복원됩니다.

작업 속성

작업 클래스는 재시도 및 시간 초과 동작을 제어하는 public 속성을 선언할 수 있습니다. 작업자는 리플렉션으로 이러한 속성을 읽어 자동 적용합니다.

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...
    }
}
속성 유형 설명
$tries int 작업이 묻히기 전까지의 최대 시도 횟수입니다. 디스패치 시점의 max_attempts 값을 덮어씁니다.
$timeout int 최대 실행 시간(초)입니다. 전역 QUEUETY_MAX_EXECUTION_TIME 상수를 덮어씁니다. pcntl이 필요하며, 아직 적용되지 않습니다(시간 초과 참고).
$backoff array 각 재시도에 적용되는 지연 시간(초)의 배열입니다. 이후 재시도에는 마지막 값이 반복해서 사용됩니다.
$concurrency_group string 비용이 큰 작업에 사용하는 공유 작업자 허용 그룹입니다.
$concurrency_limit int 해당 그룹에서 동시에 실행할 수 있는 최대 processing 작업 수입니다. $concurrency_group이 없으면 무시됩니다.
$cost_units int 워크플로우 예산 및 작업자 허용에 사용되는 상대 실행 비용입니다.

$backoff 배열은 점차 증가하는 재시도 지연 시간을 제공합니다. 예를 들어 [10, 30, 60, 120]이면 첫 번째 재시도는 10초, 두 번째는 30초, 세 번째는 60초를 기다리며 그 이후 재시도는 120초를 기다립니다.

작업 클래스가 서로 다른 비용 특성을 나타낼 때 리소스 관련 속성이 유용합니다. 빠른 캐시 새로고침과 대규모 공급자 호출은 모두 “작업 하나”일 수 있지만, 워크플로우 예산이나 작업자 허용 판단에서 프로세스가 안전한 범위 안에 있도록 관리할 때 두 작업을 같은 비용으로 계산할 필요는 없습니다.

전체 모델과 워크플로우 예산 및 작업자 측 허용에 관한 내용은 리소스 관리를 참고하세요.

실패 후크

모든 재시도 횟수를 소진해 작업이 묻히면 Queuety은 작업 인스턴스에 정의된 경우 failed() 메서드를 호출합니다. 정리, 알림 또는 로깅에 사용하세요.

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()}" );
    }
}

failed() 메서드는 최종 실패를 일으킨 예외를 받습니다. 작업 인스턴스는 원래 페이로드와 함께 역직렬화되므로 모든 public 속성을 사용할 수 있습니다.

조건부 디스패치

Dispatchable 트레이트는 조건부 디스패치를 위한 dispatch_if() 및 dispatch_unless()를 제공합니다. 두 메서드 모두 첫 번째 인수로 불리언 조건을 받고, 이어서 작업 생성자 인수를 받습니다.

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 );

조건이 true이면 dispatch_if()이 디스패치하고 PendingJob를 반환합니다. 조건이 false이면 null을 반환합니다.

조건이 false이면 dispatch_unless()이 디스패치하고 PendingJob를 반환합니다. 조건이 true이면 null을 반환합니다.

두 메서드는 디스패치할 때 PendingJob 옵션을 연결해서 사용할 수 있습니다:

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

동기식 디스패치

Queuety::dispatch_sync()을 사용하면 큐에 추가하지 않고 현재 프로세스에서 작업을 즉시 실행할 수 있습니다. 테스트하거나 즉시 실행해야 하는 작업에 유용합니다.

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' ] );

작업의 handle() 메서드를 직접 호출합니다. 데이터베이스 행을 만들지 않고, 워커를 사용하지 않으며, 미들웨어를 적용하지 않습니다.

작업 체이닝

작업 체이닝은 일련의 작업을 차례로 실행합니다. 체인의 각 작업은 이전 작업이 성공적으로 완료되어야 실행됩니다. 작업 하나라도 실패하면 이후 작업은 대기 상태로 남습니다.

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()은 큐와 실패 핸들러를 설정하는 fluent 메서드가 있는 ChainBuilder을 반환합니다. dispatch() 메서드는 순차적인 depends_on 관계로 모든 작업을 생성하고 첫 번째 작업의 ID를 반환합니다.

with_chain() 사용

Dispatchable 트레이트는 with_chain() 메서드를 제공합니다. 현재 이 메서드는 Queuety::chain( $jobs )와 동일하게 작동합니다. 호출된 클래스는 체인에 추가되지 않으므로(#13) 배열에 첫 번째 작업도 나열해야 합니다:

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

체인 실패 처리

catch()을 사용해 실패 핸들러 클래스를 등록하세요. 핸들러 클래스에는 handle() 메서드가 있어야 합니다. 체인의 작업이 영구적으로 묻히면 이 메서드가 호출됩니다. 작업자는 handle(), handle( Job $job ), handle( Job $job, \Throwable $exception )를 지원합니다.

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

미들웨어 추가

작업 클래스는 실행을 감싸는 미들웨어를 반환하는 middleware() 메서드를 정의할 수 있습니다:

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 ),
        ];
    }
}

내장 미들웨어 및 사용자 지정 미들웨어 작성에 관한 자세한 내용은 미들웨어를 참고하세요.

핸들러 기반 작업과 비교

핸들러 기반 (v0.1+) 클래스 기반 (v0.6+)
디스패치 Queuety::dispatch( 'send_email', $payload ) SendEmailJob::dispatch( $to, $subject )
핸들러 Handler를 구현하는 별도 클래스 동일한 클래스가 Contracts\Job를 구현
페이로드 타입이 지정되지 않은 array $payload 타입이 지정된 public 속성
등록 Queuety::register() 또는 #[QueuetyHandler] 필요 없음 (FQCN 사용)
미들웨어 사용할 수 없음 선택적 middleware() 메서드
구성 config() 메서드 작업 속성 + PendingJob 체인

두 접근 방식 모두 완전히 지원됩니다. 기존 핸들러 기반 작업은 변경 없이 계속 작동합니다. 같은 프로젝트에서 두 스타일을 함께 사용할 수도 있습니다.

작업 수명 주기

코드
dispatch() -> pending -> processing -> completed
                  |           |
                  |           +-> exception -> pending (retry after backoff)
                  |                       |
                  |                       +-> buried (max attempts reached)
                  |
                  +-> delayed (available_at in the future)
상태 설명
pending 작업자가 가져가기를 기다리는 중
processing 현재 실행 중
completed 성공적으로 완료됨
failed 예약됨. 현재 작업자는 이 상태를 설정하지 않습니다. 재시도할 실패 작업은 pending로 돌아갑니다.
buried 모든 재시도 횟수를 소진함