Idempotency and deduplication, in depth
How XTRACKER guarantees a conversion is counted exactly once: the dedup key, the role of eid vs occurred_at_ms, and how retries stay safe end to end.
Money events must be counted exactly once. Networks retry postbacks, your app retries on timeouts, and the delivery worker retries failed dispatches. XTRACKER is built so none of that double-counts a deposit. Here's the mechanism.
The dedup key
Every incoming event computes a deterministic key:
dedup_key = sha256(space_id | uid | canonical | discriminator)
The row is inserted with ON CONFLICT DO NOTHING on that key. A second event with the same key returns "status": "duplicate" and changes nothing.
The discriminator is where you control behaviour:
- if you pass an
eid, the discriminator is theeid; - otherwise it falls back to
occurred_at_ms.
eid — time-independent dedup
eid is your unique id for a conversion — typically the network's trade/transaction id. Because it's the discriminator, a postback that re-fires at a different time still dedups:
…&event=deposit&uid=abc&value=25&eid=tx-42 (10:00:00) → queued
…&event=deposit&uid=abc&value=25&eid=tx-42 (10:00:07) → duplicate ✓
This is the safe default for registration and deposit. Always send it when the source has a unique id.
occurred_at_ms — pinned-time dedup
Without an eid, two events dedup only if they share the same occurred_at_ms. That's fine for a client you control: pin the timestamp once per logical event and reuse it on retries.
ts = int(time.time() * 1000) # compute ONCE
await send_event("deposit", uid=uid, value="25", occurred_at_ms=ts)
# ... on a retry, reuse the SAME ts → dedups
The supplied client does this for you across its internal retries. If you omit the time entirely on a postback, the server stamps receive-time — fine for a single clean fire, but eid is stronger.
End-to-end safety
Dedup at ingest is only half the story. Delivery is idempotent too:
- The event is written to
event_inboxand committed before anything is published — a crash can't lose an accepted event. - Fan-out creates one
event_outboxrow per destination. - The worker retries failed rows with backoff. Your webhook receives a stable
event_idso you can dedup on your side too.
So a conversion survives a Redis blip, a worker restart, or a destination outage — and still lands once.
Checklist
- Send
eidfor everyregistrationanddeposit. - If you can't, pin
occurred_at_msand reuse it on retries. - Dedup webhooks on
event_idin your handler. - Treat
"duplicate"as success — it means the event was already recorded.
Next
- Canonical events — what carries
valueand needs aneid. - Webhook payload reference — the
event_idyou dedup on.
Keep reading
How to find and fix the leak in your funnel
A practical walkthrough: use the funnel breakdown and conversion gauge to pinpoint exactly where users drop off, form a hypothesis, fix one thing, and measure the lift.
PlaybookKeep your finger on the pulse: real-time conversion monitoring
A live dashboard isn't a vanity metric — it's an early-warning system. Here's how watching conversions in real time catches broken funnels and dead campaigns before they burn your budget.
PlaybookSame budget, more deposits: optimizing creatives with per-link data
Clicks lie. Deposits don't. Use per-link attribution to see which creative actually drives revenue — then move spend to the winner and kill the losers.