Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungUmfrage: Wo drückt Sie der Schuh im Microsoft-Umfeld?(21.09.2026 um 12:00 Uhr)
Sichere ProgrammierungHow I Built (and Broke) a Production Multi-Agent AI System(21.09.2026 um 12:02 Uhr)
Sichere ProgrammierungResize and strip EXIF metadata from user uploads in Node.js(21.09.2026 um 12:04 Uhr)
Sichere ProgrammierungHandbrake Alternatives: What to Use When You Don't Want to Install It(21.09.2026 um 12:08 Uhr)
Sichere Programmierungwhy on earth does the Claude logo still look like a sphincter?(21.09.2026 um 12:08 Uhr)
Sichere ProgrammierungUmfrage: Wo drückt Sie der Schuh im Microsoft-Umfeld?(21.09.2026 um 12:00 Uhr)
Sichere ProgrammierungHow I Built (and Broke) a Production Multi-Agent AI System(21.09.2026 um 12:02 Uhr)
Sichere ProgrammierungResize and strip EXIF metadata from user uploads in Node.js(21.09.2026 um 12:04 Uhr)
Sichere ProgrammierungHandbrake Alternatives: What to Use When You Don't Want to Install It(21.09.2026 um 12:08 Uhr)
Sichere Programmierungwhy on earth does the Claude logo still look like a sphincter?(21.09.2026 um 12:08 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

🧠 Exactly-Once Processing in Go: The Myth, Reality, and Production Patterns

Duplicate processing is not a bug. It is the default behavior of reliable distributed systems. Every distributed system eventually faces the same uncomfortable truth: The moment you introduce: Retries Failovers Message…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Duplicate processing is not a bug.



It is the default behavior of reliable distributed systems.




Every distributed system eventually faces the same uncomfortable truth:



The moment you introduce:




  • Retries

  • Failovers

  • Message brokers

  • Network partitions

  • Service crashes



duplicates become inevitable.



Yet many engineers still believe that "exactly-once processing" is something a broker can magically provide.



It isn't.



In this article we'll explore:




  • Why exactly-once delivery is mostly a myth

  • Why at-least-once is the real-world standard

  • How idempotency actually works

  • How to build Inbox and Outbox patterns in Go

  • How Kafka, PostgreSQL, and Redis fit together in production









🧨 Exactly-Once Is Not a Transport Property



A message broker cannot guarantee exactly-once processing across your entire system.



Exactly-once delivery requires a Single Point of Truth (SPoT) capable of enforcing uniqueness.



Why?



Because delivery, processing, and persistence are separate failure domains.



Even if a broker behaves perfectly:




  • Your service can crash after processing

  • Your database can commit while ACK fails

  • Your consumer can retry unknowingly



This creates unavoidable duplication scenarios.









📊 Delivery Semantics in Real Systems




























Model Meaning Reality
At-most-once Message may be lost Common in fire-and-forget systems
At-least-once Message may be duplicated Kafka, SQS, RabbitMQ
Exactly-once Message processed once Only possible inside bounded systems


👉 In practice, everything is at-least-once.









💥 Why Duplicates Happen






Consumer Crash After Processing






func handle(msg Message) error {
if err := process(msg); err != nil {
return err
}

return ack(msg)
}






`



Failure window:



text

Process Message ✅

Commit Database ✅

Crash Service ❌

ACK Never Sent ❌



Result:



text

Broker thinks processing failed



Message redelivered



Duplicate processing









Network Timeout After Success



`go

err := process(msg)



if err != nil {

return err

}



return broker.Ack(msg)

`



What happens if:



text

ACK sent



Network timeout



Broker never receives ACK



The broker retries.



The operation runs again.









Retry Storms



A temporary latency spike can trigger:



text

Client Retry



Gateway Retry



Service Retry



Consumer Retry



Result:



text

1 failure



50 duplicate executions









🧠 The Real Solution: Idempotency



Instead of preventing duplicates:



text

Wrong Question:

How do I prevent duplicates?



Ask:



text

Correct Question:

How do I make duplicates harmless?



That's where idempotency comes in.



An idempotent operation produces the same final state no matter how many times it executes.









🚨 Naive Payment Service



Let's start with a broken implementation.



`go

func Charge(

ctx context.Context,

req PaymentRequest,

) error {




_, err := db.Exec(ctx, `
INSERT INTO payments (
id,
amount
)
VALUES ($1, $2)
`,
req.ID,
req.Amount,
)

return err




}

`



Now imagine:



text

Request arrives



Payment inserted



Response lost



Client retries



Payment inserted again



Customer charged twice.









✅ Production Solution #1: Database Unique Constraints



Create an Inbox table.



sql

CREATE TABLE idempotency_keys (

event_id TEXT PRIMARY KEY,

created_at TIMESTAMP DEFAULT now()

);



Attempt to claim the key first.



go

_, err := tx.Exec(ctx,


INSERT INTO idempotency_keys(event_id)

VALUES($1)

`, eventID)



if err != nil {

var pgErr *pgconn.PgError




if errors.As(err, &pgErr) &&
pgErr.Code == "23505" {

return nil
}

return err




}

`



The database becomes the source of truth.









⚠️ Common pgx Pitfall



This subtle bug catches many Go teams.



Wrong:



go

import "github.com/jackc/pgconn"



Correct:



go

import "github.com/jackc/pgx/v5/pgconn"



Otherwise:



go

errors.As(err, &pgErr)



silently fails.



Your duplicate protection stops working.









🧪 Testing Duplicate Safety



Let's simulate 100 concurrent requests.



`go

func TestIdempotency(

t *testing.T,

) {

var wg sync.WaitGroup




for i := 0; i < 100; i++ {
wg.Add(1)

go func() {
defer wg.Done()

processOrder(
context.Background(),
"same-event-id",
)
}()
}

wg.Wait()




}

`



Expected outcome:



text

100 requests



1 insert succeeds



99 rejected safely









⚡ Redis Optimization



Postgres guarantees correctness.



Redis improves performance.



Use Redis only as:



text

Fast Lock Layer



Not as:



text

Source of Truth



Atomic Lua Guard:



`lua

local key = KEYS[1]



if redis.call("GET", key) then

return 1

end



redis.call(

"SET",

key,

"PENDING",

"EX",

60

)



return 0

`



This protects the database from thundering herds.









📦 Kafka Exactly-Once: What It Actually Means



Many engineers believe:



`text






Kafka Exactly Once



Business Logic Executes Once

`



Wrong.



Kafka guarantees:



text

Producer



Kafka



Consumer



Kafka does NOT guarantee:



text

Producer



Kafka



Consumer



Postgres



The moment you touch an external database:



exactly-once disappears.









⚠️ Disable Auto Commit



Never rely on Kafka auto commits.



Bad:



go

enable.auto.commit=true



Good:



go

consumer, _ := kafka.NewConsumer(

&kafka.ConfigMap{

"enable.auto.commit": false,

},

)



Process first.



Commit later.



`go

err := handleOrder(

ctx,

db,

msg,

)



if err == nil {

consumer.CommitMessage(msg)

}

`









📤 Solving the Dual-Write Problem



This architecture is broken:



text

Insert Order



Publish Event



What if Kafka is down?



text

Order exists



Event lost forever



That's the Dual Write Problem.









🚀 Transactional Outbox Pattern



Store event publishing intent in the same transaction.



sql

CREATE TABLE outbox (

id UUID PRIMARY KEY,

payload JSONB,

status TEXT DEFAULT 'PENDING'

);



go

_, err = tx.Exec(ctx,


INSERT INTO orders(...)

VALUES(...)

`)



_, err = tx.Exec(ctx,

INSERT INTO outbox(...)

VALUES(...)

)

`



Commit both together.









🔄 Outbox Worker



`go

type OutboxWorker struct {

db *pgxpool.Pool

broker Broker

}



func (w *OutboxWorker) Run(

ctx context.Context,

) {

ticker := time.NewTicker(

time.Second,

)




defer ticker.Stop()

for {
select {
case <-ctx.Done():
return

case <-ticker.C:
w.processBatch(ctx)
}
}




}

`









⚡ Scaling Workers Safely



Use PostgreSQL native locking.



sql

SELECT *

FROM outbox

WHERE status = 'PENDING'

FOR UPDATE SKIP LOCKED

LIMIT 10;



Benefits:




  • No deadlocks

  • No duplicate workers

  • Infinite horizontal scaling









💳 The Hardest Part: External Side Effects



Database writes are easy.



External systems are not.



Examples:




  • Stripe

  • Twilio

  • SendGrid

  • Payment Gateways



Without idempotency:



text

Duplicate Message



Duplicate Payment



Real Money Lost



Always verify external APIs support idempotency keys.









🛡️ Graceful Shutdown



Kubernetes gives you roughly:



text

30 seconds



before SIGKILL.



Handle shutdown properly.



`go

ctx, stop := signal.NotifyContext(

context.Background(),

syscall.SIGTERM,

)



defer stop()

`



Allow in-flight transactions to finish.









📈 Structured Observability



Track duplicates explicitly.



go

slog.Info(

"duplicate detected",

"event_id",

eventID,

)



If you can't measure duplicates:



you can't prove idempotency works.









🏗️ Final Production Architecture



text

Kafka





Inbox Pattern

(Idempotency)





PostgreSQL





Outbox Pattern





Kafka / RabbitMQ



This architecture embraces reality:



`text

At-Least-Once Delivery

+

Idempotency

+






Recovery



Production Reliability

`









🚀 Key Takeaways




  • Exactly-once delivery does not exist across distributed systems.

  • At-least-once delivery is the industry standard.

  • Idempotency transforms duplicates into harmless events.

  • PostgreSQL UNIQUE constraints should be the source of truth.

  • Redis is an optimization layer, not a consistency layer.

  • Kafka auto commits can cause data loss.

  • Inbox + Outbox patterns form the backbone of reliable event-driven systems.

  • Reliability is not about preventing failures.

  • Reliability is about remaining correct despite failures.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten 🧠 Exactly-Once Processing in Go: The Myth, Reality, and Production Patterns

Thematisch verwandte Begriffe: ExactlyOnce, Processing, Myth, Reality · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94036 | A security flaw has been discovered in D-Link DIR-X1860 and DIR-X1860Z u…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick