Webhook til AI-berigelse kører dobbelt og opretter dublerede poster
Hvorfor dubletter opstår i AI-workflows
Et webhook er i praksis blot en besked om, at noget er sket. I WooCommerce kan det være en ordre, et produkt eller en kundeopdatering via Webhooks Documentation – WooCommerce. I WordPress-baserede integrationer kan modtagelsen ske via en REST-endpoint oprettet med plugin-API’et, som beskrevet i REST API – Plugin Handbook | Developer.WordPress.org. Problemet er, at afsender og modtager ofte er bygget med forskellige antagelser om levering, timing og fejlscenarier.
Dubletter opstår typisk, fordi den samme hændelse bliver sendt igen, modtaget igen eller behandlet igen. Mange webhook-systemer leverer mindst én gang, ikke præcis én gang. Hvis modtageren svarer langsomt, fejler midlertidigt eller returnerer en tvetydig statuskode, kan afsenderen forsøge igen. Hvis man samtidig lader AI-berigelsen oprette nye poster uden en entydig kontrolmekanisme, skabes der dubletter.
- Afsender retry’er webhooket efter timeout eller 5xx-fejl
- Samme forretningshændelse udløser flere hooks i kildesystemet
- WordPress-cron eller baggrundsjob behandler samme køelement mere end én gang
- Plugin-kode opretter en ny post i stedet for at opdatere en eksisterende
- AI-svaret gemmes før workflowets status er låst korrekt
- Manglende deduplikering mellem browser-, server- eller backend-events
Det vigtigste princip: gør workflowet idempotent
Hvis et webhook kan ankomme to gange, skal behandlingen kunne tåle det. Det kaldes idempotens: samme input må ikke skabe nye sideeffekter ved gentagen behandling. I praksis betyder det, at AI-berigelse ikke må oprette en ny post, hvis hændelsen allerede er behandlet, eller hvis der allerede findes et resultat for samme forretningsobjekt og samme behandlingsformål.
En almindelig fejl er at bruge tidspunktet for modtagelse som nøgle. Det er sjældent stabilt nok. Brug i stedet en entydig idempotency key, for eksempel en kombination af webhook-event-id, kildesystemets objekt-id, hændelsestype og eventuelt en versionsmarkør. Hvis man AI-beriger et WooCommerce-produkt, kan en nøgle være bygget af produkt-id, webhook-topic og produktets seneste ændringstidspunkt. Målet er, at samme hændelse altid lander i samme nøgle.
Arkitektur, der reducerer risikoen for dobbeltkørsel
Den mest robuste model er at skille modtagelse fra behandling. Webhook-endpointet skal ikke selv køre hele AI-flowet. Det skal validere anmodningen, gemme et køelement med idempotency key og hurtigt kvittere. Selve AI-berigelsen køres derefter asynkront af en worker eller et baggrundsjob. Den opdeling gør timeout mindre sandsynlig og reducerer risikoen for, at afsenderen gensender samme event.
- Webhook modtages og signatur eller oprindelse valideres
- Eventets unikke nøgle beregnes
- Nøglen slås op i en deduplikeringstabel
- Hvis nøglen findes som behandlet eller under behandling, stoppes flowet
- Hvis nøglen er ny, oprettes køelement med status pending
- Worker henter køelementet, låser det og udfører AI-kaldet
- Resultatet gemmes som update eller upsert, ikke blind oprettelse
- Status ændres til completed med logning af tidsstempel og reference
Denne arkitektur passer godt til AI-integrationer, fordi modelkald kan være langsommere og dyrere end almindelige API-kald. OpenAI’s API-dokumentation beskriver de generelle integrationsmønstre i API Overview | OpenAI API Reference. Hvis man bygger mere agentlignende forløb med fortsatte sessioner, skal man være ekstra opmærksom på, at en session ikke utilsigtet fortsættes to gange for samme forretningshændelse, jf. Run and continue sessions | OpenAI API.
Praktisk eksempel i WordPress eller WooCommerce
Antag, at et WooCommerce-webhook ved produktopdatering sender data til et WordPress-plugin, der bruger OpenAI API til at generere en kort produktresume, attributnormalisering eller intern tagging. Hvis endpointet ved hver modtagelse opretter en ny custom post type med AI-resultatet, opstår dubletter straks, når webhooket gensendes. I stedet bør man gemme resultatet på det oprindelige objekt eller i en relateret tabel med en unik nøgle.
function handle_ai_webhook($request) {
$source_id = $request['product_id'];
$event_id = $request['event_id'];
$updated_at = $request['updated_at'];
$idempotency_key = hash('sha256', $source_id . '|' . $event_id . '|' . $updated_at . '|product_enrichment');
if (job_exists($idempotency_key)) {
return ['status' => 'duplicate_ignored'];
}
create_job([
'key' => $idempotency_key,
'source_id' => $source_id,
'status' => 'pending'
]);
return ['status' => 'accepted'];
}
function process_job($job) {
if (!acquire_lock($job['key'])) {
return;
}
if (result_exists($job['key'])) {
mark_job_completed($job['key']);
release_lock($job['key']);
return;
}
$ai_result = call_openai_for_enrichment($job['source_id']);
upsert_enrichment_result($job['source_id'], $job['key'], $ai_result);
mark_job_completed($job['key']);
release_lock($job['key']);
}
Koden illustrerer fire centrale punkter: deduplikering før køoprettelse, låsning før behandling, kontrol af eksisterende resultat og upsert i stedet for ren insert. Det er ofte nok til at fjerne de mest almindelige dubletfejl.
Sådan finder du den reelle fejlårsag
Før man ændrer koden, bør man kortlægge hele kæden. Dubletter skyldes ikke altid webhooket alene. Nogle gange kommer der to forskellige hændelser, som ligner dubletter, men faktisk er legitime opdateringer. Andre gange er problemet i databasen eller i et mellemled som et køsystem, et automation-værktøj eller et plugin.
- Log rå webhook-payloads med event-id, topic og modtaget tidspunkt
- Gem HTTP-statuskode og svartid på endpointet
- Log om samme idempotency key ses flere gange
- Skeln mellem oprettelse, opdatering og genbehandling
- Kontrollér om et plugin både sender webhook og intern hook-baseret behandling
- Undersøg om worker kan køre parallelt på samme job
- Kontrollér databaseindeks og unikke constraints
I WooCommerce bør man især holde øje med, om flere hændelser udløses ved samme redigering, og hvordan webhook-levering genforsøges. Se både den generelle dokumentation og udviklerdokumentationen: Webhooks Documentation – WooCommerce og Webhooks | WooCommerce developer docs. Hvis modtagelsen sker i WordPress, bør endpointet være så simpelt som muligt og følge mønstrene i REST API – Plugin Handbook | Developer.WordPress.org.
Databasebeskyttelse er ikke valgfri
Applikationslogik alene er ikke nok. Hvis to processer rammer databasen samtidigt, kan begge nå at passere en softwarekontrol, før nogen af dem skriver resultatet. Derfor bør man også beskytte sig i datalaget. Den mest praktiske løsning er en unik nøgle på idempotency key eller på kombinationen af kildereference og berigelsestype.
Hvis man gemmer AI-resultater i en specialtabel, bør tabellen have et unikt indeks. Hvis man gemmer på posts eller post meta i WordPress, er det sværere at håndhæve stram unikhed, og i mere forretningskritiske flows er en separat tabel ofte teknisk renere. Her kan man både styre låsning, status og sporbarhed mere præcist.
OpenAI-kald skal være sideeffekt-kontrollerede
Selve AI-kaldet er sjældent årsagen til dubletter, men det forstærker konsekvensen. Hvis et webhook behandles to gange, betaler man typisk også to gange for API-forbrug. Derfor bør OpenAI-kald først ske, når jobbet er låst, og når systemet har besluttet, at der faktisk mangler et resultat. Hvis man bruger webhooks fra OpenAI i et større flow, skal de også valideres og knyttes til entydige interne jobreferencer, som beskrevet i Webhooks | OpenAI API.
Et godt mønster er at gemme både promptversion, modelreference og hash af inputdata. Så kan man afgøre, om to behandlinger reelt er ens, eller om en ny behandling er legitim, fordi input eller prompt er ændret. Det er særligt nyttigt i AI content generation, AI SEO og produktberigelse, hvor den samme kilde kan blive opdateret over tid.
Fejlhåndtering, retries og menneskelig kontrol
Et robust workflow skelner mellem tekniske fejl og forretningsmæssige fejl. En midlertidig netværksfejl bør kunne retries kontrolleret. Et ugyldigt input bør derimod markeres som failed uden automatisk gentagelse. Hvis alt bare retries blindt, kan man få gentagne AI-kald og i værste fald flere dubletter, hvis statusstyringen er svag.
- Brug eksponentiel backoff ved midlertidige fejl
- Sæt maksimum for antal retries pr. job
- Gem sidste fejlbesked og fejlkategori
- Kræv manuel gennemgang ved gentagne fejl eller usædvanligt output
- Overvåg antal dublet-afvisninger som driftssignal
- Adskil test- og produktionswebhooks tydeligt
Menneskelig kontrol er stadig vigtig, især når AI-resultatet påvirker synligt indhold, prisnære tekster, kategorisering eller publicering. Automatisering bør reducere manuelt arbejde, ikke fjerne ansvar for datakvalitet.
Forretningsværdi: færre fejl, lavere omkostning, mere tillid
Når dubletproblemet løses korrekt, er gevinsten større end bare pænere data. Man reducerer API-omkostninger, undgår forvirring i backend, forbedrer sporbarhed og gør workflows mere driftssikre. For bureauer og virksomheder med WordPress AI- eller WooCommerce AI-integrationer er det en forudsætning for at kunne skalere automatisering uden at miste kontrol.
Det praktiske fokus bør være enkelt: behandl hver forretningshændelse én gang, eller gør gentagen behandling harmløs. Hvis den regel er indbygget i arkitektur, database og kode, bliver AI-berigelse langt mere stabil i daglig drift.
Kort tjekliste før du går i produktion
- Har hvert webhook en entydig intern idempotency key?
- Er webhook-modtagelse adskilt fra AI-behandling i kø eller baggrundsjob?
- Findes der låsning mod parallel behandling?
- Skriver databasen med unik constraint eller upsert-logik?
- Kan samme resultat spores til kildeobjekt, inputhash og promptversion?
- Er retries begrænsede og kategoriserede?
- Logger systemet dubletter uden at oprette nye poster?
Kilder og yderligere læsning
Kategorier
- AI & Automatisering (5)
- Joomla (2)
- Kode (6)
- Online markedsføring (3)
- SEO (8)
- Tech (2)
- Webshop (13)
- Website (5)
- WordPress (13)
Denne side er skrevet af
Simon B. H. Christensen
Tlf: 6168 3536