shiftonelabs/laravel-sqs-fifo-queueで重複排除とメッセージ順序を制御する
概要
Laravel の標準キュードライバは Amazon SQS の標準キュー(Standard Queue)を前提にしています。標準キューは「少なくとも1回」の配信で順序も保証しないため、「同じジョブが二重実行される」「後から投げたジョブが先に処理される」といった事象が起こり得ます。
SQS にはFIFO キューがあり、メッセージ順序の保証(MessageGroupId 単位)と重複排除(MessageDeduplicationId による5分間の重複排除)を提供します。ただし Laravel 標準の sqs ドライバは FIFO に必要なパラメータを送らないため、そのままでは FIFO キューを使えません。
本記事では shiftonelabs/laravel-sqs-fifo-queue を使い、Laravel のキューから FIFO キューを扱って順序制御と重複排除を構成する手順をハンズオン形式でまとめます。
> 本記事のコードは公開ドキュメントに基づく一般的な構成例です。実運用前に、各自の Laravel/パッケージのバージョンで挙動を確認してください。
標準キューとFIFOキューの違い
| 観点 | 標準キュー | FIFOキュー |
|---|---|---|
| 配信 | 少なくとも1回(重複あり得る) | 1回のみ(重複排除あり) |
| 順序 | ベストエフォート | グループ単位で保証 |
| キュー名 | 任意 | .fifo で終わる必要あり |
| 必須パラメータ | なし | MessageGroupId 必須 |
| per-message delay | 可能 | 不可(キュー全体のみ) |
発生する課題
- 二重実行してはいけないジョブ(決済・ポイント付与・在庫引き当て)が、リトライや多重ディスパッチで複数回走る
- 順序が意味を持つジョブ(同一エンティティの「作成→更新→削除」)が前後して処理される
アプリ側の冪等化が基本ですが、SQS 側で順序・重複排除を担保できると設計の負担を減らせます。
導入
インストール
composer require shiftonelabs/laravel-sqs-fifo-queue
パッケージ自動検出に対応しているため、通常はサービスプロバイダの手動登録は不要です。
接続設定
config/queue.php に sqs-fifo ドライバの接続を追加します。
// config/queue.php
'connections' => [
'sqs-fifo' => [
'driver' => 'sqs-fifo',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.ap-northeast-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'default.fifo'),
'suffix' => env('SQS_SUFFIX', ''),
'region' => env('AWS_DEFAULT_REGION', 'ap-northeast-1'),
'group' => env('SQS_GROUP', 'default'),
'deduplicator' => env('SQS_DEDUPLICATOR', 'unique'),
'allow_delay' => env('SQS_ALLOW_DELAY', false),
],
],
重要な3設定:
queue:.fifoで終わるキュー名(例orders.fifo)group: デフォルトのMessageGroupIddeduplicator: 重複排除方式
順序制御:MessageGroupId
FIFO キューでは同じ MessageGroupId のメッセージは送信順に処理されます。グループが異なれば並列に処理され得ます。したがってグループ ID の設計は、順序保証の粒度とスループットのトレードオフ設計そのものです。
- 全メッセージ同一グループ → 完全直列(順序最強・スループット最小)
- エンティティ単位でグループ分割 → グループ内順序保証・グループ間並列
重複排除:MessageDeduplicationId
FIFO キューは同一 MessageDeduplicationId のメッセージを5分間、重複として無視します。deduplicator 設定で生成方式を選びます。
unique(デフォルト): 毎回ユニーク値 → 実質重複排除は効かないcontent: メッセージ本文のハッシュ → 同一内容の二重ディスパッチを5分間排除
SQS_DEDUPLICATOR=content
> content は本文が1バイトでも違えば別メッセージ扱いです。ペイロードにタイムスタンプやランダム値が混ざると重複排除が効きません。
独自ディデュプリケータ
業務キーで重複排除したい場合は Deduplicator コントラクトを実装します。
<?php
namespace App\Queue\Deduplicators;
use ShiftOneLabs\LaravelSqsFifoQueue\Contracts\Queue\Deduplicator;
class OrderDeduplicator implements Deduplicator
{
public function generate($payload, $queue)
{
$data = json_decode($payload, true);
// ペイロード構造に合わせて業務キーを取り出す(キー構造は要確認=仮置き)
$orderId = data_get($data, 'data.orderId', 'fallback');
return (string) $orderId;
}
}
サービスプロバイダで名前付き登録します。
use ShiftOneLabs\LaravelSqsFifoQueue\SqsFifoQueue;
use App\Queue\Deduplicators\OrderDeduplicator;
public function boot(): void
{
SqsFifoQueue::addDeduplicator('order', fn () => new OrderDeduplicator());
}
SQS_DEDUPLICATOR=order
> generate() に渡る $payload のキー構造はバージョン依存です。一度ダンプして確認してから組み立ててください(本文の該当箇所は仮置きです)。
ジョブをFIFOキューに流す
dispatch((new ProcessOrder($order))->onConnection('sqs-fifo'));
またはデフォルト接続を切り替えます。
QUEUE_CONNECTION=sqs-fifo
delay(遅延配信)の注意
SQS FIFO はメッセージ単位の遅延をサポートしません(キュー全体のみ)。->delay(...) を付けたジョブの扱いは allow_delay 設定で制御されます。遅延を多用する設計なら、挙動を自分のバージョンで確認してください。
動作確認
FIFO キューを作成:
aws sqs create-queue \
--queue-name orders.fifo \
--attributes '{"FifoQueue":"true","ContentBasedDeduplication":"false"}' \
--region ap-northeast-1 --profile your-profile
ワーカー起動:
php artisan queue:work sqs-fifo
確認ポイント:
deduplicator=contentで同一内容を2回ディスパッチ → 1回だけ処理される- 同一グループのジョブが送信順に処理される
- 標準キュー接続と比較して差(重複・順序前後)を確認
注意点
- キュー名は
.fifo必須(取り違えで送信失敗) uniqueがデフォルト。重複排除が効かないときはまずdeduplicatorを疑う- 重複排除は5分ウィンドウ。長時間の冪等性はアプリ側と併用
- 全ジョブ単一グループは直列化してスループット低下。グループ設計が性能を決める
- per-message delay 非対応
contentは本文の揺らぎに弱い。重複排除キーは独自ディデュプリケータで明示するのが堅い
まとめ
- Laravel 標準の
sqsは FIFO キューを扱えない。shiftonelabs/laravel-sqs-fifo-queueのsqs-fifoドライバが解決する。 - 順序は
MessageGroupId(group)、重複排除はMessageDeduplicationId(deduplicator) が肝。 - 重複排除は
unique/content/独自ディデュプリケータから選ぶ。 - グループ設計=順序保証とスループットのトレードオフ。業務キー単位で切ると「グループ内順序保証・グループ間並列」を実現できる。
- FIFO 固有の制約(5分ウィンドウ・delay 非対応)は、アプリ側の冪等化と併せて設計する。
