Reintents i idempotència de webhooks
Com gestiona Feedjolt els errors d'entrega de webhooks avui: un intent, registres, reenviament manual i com crear handlers idempotents i segurs davant desordre.
El lliurament és disparar i registrar: fem POST del teu payload un cop. Si el teu endpoint retorna no-2xx o fa timeout, la fallada es registra i un admin pot replayar el lliurament des del panell.
El que fem avui
- Un intent de lliurament per esdeveniment, amb un timeout de 10 segons.
- Cada intent es registra a
Configuració -> Webhooks -> [endpoint] -> Lliuraments- codi d'estat, cos de resposta (limitat a 4 KB), bandera d'èxit/fallada. - Replay manual: cada fila de log té un botó Replay. Polsar-lo reencua el mateix payload. Útil per a "vaig arreglar el bug, reprocessa els esdeveniments d'ahir".
- Els endpoints es poden desactivar (
is_active = false) manualment al panell.
El que NO fem (encara)
- Sense agenda de reintents automàtica. Una sola fallada transitòria vol dir que no veus aquell esdeveniment sense un replay manual.
- Sense desactivació automàtica d'endpoints poc saludables.
- Sense política de retenció de logs - els logs es conserven indefinidament (això probablement canviarà).
- Sense protecció contra replay a la pròpia firma. Veure Firma -> Protecció contra replay.
Estan al full de ruta. Fins que arribin, els defaults segurs van a baix.
Comportament recomanat del client
Fes el teu handler idempotent
El mateix esdeveniment lògic pot ser replayat manualment. Fes servir el camp id del payload (quan estigui present - és estable entre replays del mateix esdeveniment) com a clau de deduplicació:
async function handle(event: any) {
if (!event.id) {
// esdeveniments antics sense id - processa best-effort
await processEvent(event);
return;
}
const seen = await redis.get(`feedjolt:event:${event.id}`);
if (seen) return;
await processEvent(event);
await redis.set(`feedjolt:event:${event.id}`, "1", "EX", 30 * 24 * 60 * 60);
}Un TTL de 30 dies et dóna marge generós per a replays manuals d'esdeveniments antics.
Per a processat amb base de dades, una restricció UNIQUE a event_id aconsegueix el mateix amb garanties més fortes:
CREATE TABLE feedjolt_events (
event_id TEXT PRIMARY KEY,
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
payload JSONB NOT NULL
);
INSERT INTO feedjolt_events (event_id, payload)
VALUES ($1, $2)
ON CONFLICT (event_id) DO NOTHING;Respon ràpid, posa el treball pesat a la cua
Tenim un timeout de 10 segons. Un handler que triga 8 segons funciona; un que triga 12 es registra com a fallada. Empeny el treball costós a una cua en background i retorna 200 a l'instant.
No 5xx davant bugs d'aplicació
Si la teva BD està caiguda, un 503 és just. Si l'esdeveniment està mal format (segons les teves regles d'aplicació), respon 200 i registra - no hi ha reintent automàtic que mantingui l'esdeveniment trencat fora de la teva cua.
Vigila el log de lliuraments
Fins que arribi la desactivació automàtica, el log de lliuraments és la teva alarma. Una feina setmanal senzilla que escanegi la taxa de fallades per endpoint atraparà problemes abans que s'acumulin. L'exposem via API:
GET /api/v1/webhooks/{endpoint_id}/deliveries?success=falseLliurament fora d'ordre
No garantim l'ordre dels esdeveniments. Dos esdeveniments status.changed per al mateix post poden arribar en qualsevol ordre si els canvis subjacents van ocórrer amb segons de diferència.
Ignora esdeveniments vells fent servir la marca de temps de l'objecte post dins el payload:
async function handleStatusChanged(event: any) {
const post = await db.posts.find(event.post.id);
if (post.last_status_event_at && new Date(event.post.updated_at) < post.last_status_event_at) {
return;
}
await db.posts.update(event.post.id, {
status: event.to_status.name,
last_status_event_at: event.post.updated_at
});
}Firma de webhooks
Verifica webhooks de Feedjolt amb HMAC-SHA256 sobre el cos en brut. Format de capçalera, algorisme de verificació, codi per a Node, Python, Ruby i Go, i errors.
API
Una visió general de l'API REST de Feedjolt: JSON sobre HTTPS amb token bearer. Cobreix la URL base, la referència interactiva, convencions i la versió v1.
