Queue
La queue livre une charge utile (payload) à un worker. Elle est at-least-once et fire-and-forget : il n'y a aucun stockage de résultat et aucun statut par message. Un message est mis en file, puis le handler HTTP d'un push-worker est invoqué pour le traiter.
Enqueue
import { createSdk } from "@bext-stack/framework";
const sdk = createSdk("<app-id>");
const { id } = await sdk.queue.push("emails", { to: "a@b.com" }, /* delaySecs */ 30);
Ou en HTTP :
curl -s http://127.0.0.1/__bext/sdk/queue/push \
-H "X-Bext-App-Id: <app-id>" \
-H "Content-Type: application/json" \
-d '{"queue":"emails","payload":{"to":"a@b.com"},"delay_seconds":30}'
Endpoints
Restreints par X-Bext-App-Id. POST sauf mention GET :
| Endpoint | Corps / notes | Retourne |
|---|---|---|
/queue/push |
{ queue, payload, delay_seconds? } |
{ id } |
/queue/pull |
récupérer des messages | — |
/queue/ack |
acquitter un message | — |
/queue/stats |
GET | statistiques de la queue |
/queue/list |
GET | queues |
/queue/dead/list |
lister les messages en dead-letter | — |
/queue/dead/retry |
réessayer les messages en dead-letter | — |
/queue/dead/purge |
purger les messages en dead-letter | — |
/queue/worker/register |
{ queue, handler_url, concurrency?, visibility_timeout_secs?, max_attempts? } |
— |
/queue/worker/unregister |
supprimer un worker | — |
/queue/worker/list |
lister les workers enregistrés | — |
Contrat du handler de push-worker
Enregistrez un push-worker avec un handler_url. Enregistrez-le comme l'URL du vhost public
de l'application — par exemple https://<app>.inklura.fr/api/queue/<name> — et non une adresse
loopback :
curl -s http://127.0.0.1/__bext/sdk/queue/worker/register \
-H "X-Bext-App-Id: <app-id>" \
-H "Content-Type: application/json" \
-d '{
"queue": "emails",
"handler_url": "https://<app>.inklura.fr/api/queue/emails",
"concurrency": 4,
"visibility_timeout_secs": 60,
"max_attempts": 5
}'
Le dispatcher envoie ce corps en POST à votre handler_url :
{ "id": "<message-id>", "queue": "emails", "payload": { }, "attempts": 1 }
Le statut de réponse de votre handler décide de l'issue :
| Réponse | Signification |
|---|---|
2xx |
ack — message traité |
408, 429, 5xx |
retry — redélivré plus tard |
tout autre 4xx |
dead-letter — déplacé vers la queue morte |
Implémentez le handler sous forme de route.ts :
export async function POST(request: Request) {
const { id, payload, attempts } = await request.json();
try {
await sendEmail(payload);
return new Response(null, { status: 200 }); // ack
} catch (e) {
return new Response(String(e), { status: 500 }); // retry
}
}
Sémantique à prendre en compte dans la conception
- At-least-once — un handler peut recevoir le même message plus d'une fois. Rendez les handlers
idempotents (par ex. dédupliquez sur
id). - Fire-and-forget — celui qui met en file obtient un
idmais rien d'autre ; il n'existe aucun moyen intégré de demander « le message X a-t-il réussi ? ». - Dead-letter — les messages qui épuisent leurs retries (ou renvoient un
4xxnon réessayable) atterrissent dans la queue morte ; inspectez / réessayez / purgez avec/queue/dead/*.
Superposez un enregistrement de job quand vous avez besoin d'un statut
Comme il n'y a pas de store de résultats, les applications qui ont besoin des issues conservent un
enregistrement de job durable en
KV — des clés comme job:<id> mises à jour par le
handler (queued → running → complete / fail).
Queue vs Tasks
Si vous avez besoin de traitements de longue durée avec statut et progression intégrés, utilisez plutôt le task-executor chaud — voir Tasks & Planificateur. La queue est idéale pour une livraison à haut volume, fire-and-forget, vers un handler public.
Et ensuite
| Page | Ce qu'elle couvre |
|---|---|
| Tasks & Planificateur | Traitements longs avec statut/progression ; cron |
| KV Store | Le motif d'enregistrement de job durable |
| Queue Workers | Exploiter et observer les workers |