← Semua pembelajaran / Laravel Nol → Enterprise
Fase 9 · Deploy di AWS

SQS & worker antrean

SQS memberi jaminan penyimpanan yang jauh lebih kuat daripada Redis, dengan imbalan beberapa batasan. Dua setelan — visibility timeout dan dead letter queue — menentukan apakah pekerjaanmu selesai atau berputar-putar diam-diam.

Intisari

  • Standard queue: throughput tinggi, urutan tidak dijamin, at-least-once — job harus idempoten.
  • Visibility timeout wajib lebih panjang daripada durasi job terlama, atau job diproses berkali-kali.
  • Dead letter queue menangkap job yang gagal berulang — tanpa itu, ia berputar selamanya.
  • Batas payload 256 KB; kirim ID, bukan data.
  • Penundaan maksimum 15 menit — delay() yang lebih panjang tidak berperilaku seperti dugaanmu.

Konfigurasi Laravel

QUEUE_CONNECTION=sqs
SQS_PREFIX=https://sqs.ap-southeast-1.amazonaws.com/123456789012
SQS_QUEUE=portal-default
AWS_DEFAULT_REGION=ap-southeast-1
// config/queue.php — tanpa key/secret; kredensial dari task role
'sqs' => [
    'driver' => 'sqs',
    'prefix' => env('SQS_PREFIX'),
    'queue'  => env('SQS_QUEUE', 'default'),
    'suffix' => env('SQS_SUFFIX'),        // mis. "-staging"
    'region' => env('AWS_DEFAULT_REGION'),
    'after_commit' => true,               // job hanya dikirim setelah transaksi commit
],

'after_commit' => true layak dinyalakan secara global. Ia menghapus seluruh kelas bug "worker tidak menemukan baris yang baru dibuat" — job baru dikirim setelah transaksi benar-benar commit. Dipasangkan dengan replication lag Aurora dari materi sebelumnya, ini menyelesaikan separuh masalah; sisanya ditangani penundaan singkat atau membaca dari writer.

Visibility timeout — setelan yang paling sering salah

Job diambil worker
  └─ pesan menjadi TIDAK TERLIHAT selama visibility timeout
       ├─ worker selesai → pesan dihapus                          ✓
       └─ timeout habis sebelum selesai
             └─ pesan MUNCUL LAGI → worker lain mengambilnya      ✗ diproses dua kali
NilaiAkibat
Lebih pendek dari durasi jobJob diproses berkali-kali secara bersamaan
Sama dengan durasi jobTerlalu ketat; sedikit keterlambatan sudah memicu duplikat
2–3× durasi job terlamaTitik awal yang benar
Sangat panjangJob yang benar-benar macet menahan slotnya lama
aws sqs set-queue-attributes \
  --queue-url https://sqs.ap-southeast-1.amazonaws.com/123456789012/portal-default \
  --attributes VisibilityTimeout=180,MessageRetentionPeriod=1209600

Gejalanya khas dan membingungkan: email terkirim dua kali, poin bertambah dobel, atau berkas ditulis berulang — sementara log menunjukkan job "berhasil" beberapa kali. Penyebabnya hampir selalu visibility timeout yang lebih pendek daripada durasi job sesungguhnya. Selaraskan tiga angka: --timeout pada queue:work, visibility timeout SQS, dan stopTimeout ECS.

Dead letter queue

aws sqs set-queue-attributes \
  --queue-url .../portal-default \
  --attributes '{
    "RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:…:portal-dlq\",\"maxReceiveCount\":\"3\"}"
  }'

Setelah gagal tiga kali, pesan dipindahkan ke DLQ alih-alih terus diulang. Tanpa DLQ, job yang selalu gagal — misalnya karena data yang rusak — akan diambil, gagal, dan kembali lagi tanpa henti, memakan kapasitas worker selamanya.

Alarm CloudWatch:
  ApproximateNumberOfMessagesVisible pada portal-dlq  > 0  →  beri tahu tim

DLQ yang tidak dipantau sama saja dengan tidak punya DLQ. Ia menjadi tempat pekerjaan menghilang dengan rapi. Alarm pada jumlah pesannya adalah satu dari sedikit alarm yang benar-benar wajib ada — karena isinya adalah pekerjaan nyata yang tidak pernah selesai: email yang tidak terkirim, pembayaran yang tidak tercatat.

Batasan SQS yang mengubah cara menulis job

BatasKonsekuensi di Laravel
Payload 256 KBKirim ID; jangan serialisasi koleksi besar
Penundaan maksimum 15 menitdelay(now()->addHours(2)) tidak bekerja seperti dugaanmu
Urutan tidak dijamin (Standard)Jangan andalkan urutan pemrosesan
At-least-onceJob harus idempoten — tanpa kecuali
Tanpa HorizonPantau lewat metrik CloudWatch
Retensi maksimum 14 hariPesan yang lebih tua dari itu hilang
// Untuk penundaan panjang: jadwalkan, jangan menunda pesan
Schedule::call(function () {
    Langganan::query()
        ->where('berakhir_pada', '<=', now()->addDay())
        ->where('diingatkan', false)
        ->each(fn ($l) => KirimPengingat::dispatch($l->id));
})->hourly();

Beberapa antrean dengan prioritas

portal-penting     visibility 60s    worker terpisah, selalu ada
portal-default     visibility 180s   worker utama
portal-media       visibility 900s   worker besar untuk pemrosesan gambar
portal-dlq         retensi 14 hari   diawasi alarm
php artisan queue:work sqs --queue=portal-penting,portal-default \
  --tries=3 --backoff=10,60,300 --max-time=3600 --timeout=120

Satu service ECS per kelompok antrean. Worker media yang butuh memori besar tidak perlu berjumlah banyak; worker notifikasi yang cepat perlu banyak tapi kecil. Memisahkannya berarti setiap kelompok bisa diskalakan dan diberi ukuran sesuai sifatnya — dan job berat tidak pernah menahan yang ringan.

Redis atau SQS

KebutuhanPilih
Dasbor bawaan, penundaan panjang, batchRedis + Horizon
Pekerjaan tidak boleh hilang sama sekaliSQS
Lonjakan sangat besar dan tidak terdugaSQS — tidak ada kapasitas yang perlu disiapkan
Menyeberang ke layanan atau tim lainSQS
Ingin sesedikit mungkin infrastrukturSQS — tidak ada klaster yang dikelola

Keduanya bisa dipakai bersamaan. Laravel mendukung banyak koneksi antrean, jadi Queue::route() dari Fase 3 bisa mengarahkan job penting ke SQS dan job biasa ke Redis. Yang perlu dihindari cuma satu: memakai satu klaster Redis untuk cache dan antrean dengan kebijakan allkeys-lru.

Latihan: buat antrean SQS dengan DLQ dan maxReceiveCount=3. Kirim job yang selalu gagal, amati ia berpindah ke DLQ setelah percobaan ketiga, lalu pasang alarm CloudWatch untuk jumlah pesan di DLQ.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.