LogoTRUONG PHAM
Home
Projects
Blogs
YouTube
Contact

Newsletter

Stay updated with technical artifacts and engineering insights.

LogoTRUONG PHAM

Building scalable software and sharing insights on technology & life.

Sitemap

  • Home
  • Projects
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back
Idempotency - Why Every API Should Be 'Stubborn'?
Microservice Journey: Lessons & Trade-offs

Idempotency - Why Every API Should Be 'Stubborn'?

In a distributed world, 'retry' is a daily occurrence. How do you ensure a customer isn't charged twice when the network is unstable?

TP
Truong PhamSoftware Engineer
PublishedMarch 27, 2024
Stack
microservice ·backend ·api-design

Idempotency might sound like a distant mathematical term, but in Microservices, it is the only "insurance" that keeps you from burning through your customers' accounts.


Context: When the Network is Unreliable

In a Monolith model, calling a function is instantaneous. In Microservices, calling an API is an adventure through network cables. What happens if:

  1. Service A calls Service B to pay $100.
  2. Service B processes successfully, deducts the money.
  3. But... the network is cut right as B is about to return the result to A.
  4. Service A sees a timeout, thinks to itself: "Maybe it didn't receive it", and automatically calls again.

If Service B isn't "smart," the customer just lost $200 for one item. Welcome to disaster!

1. Solution: Idempotency Key

Every API that performs an action that changes data (POST/PUT/DELETE) must accept an Idempotency Key from the Client (here, usually the calling Service or BFF).

Workflow at the receiving Service (B):

  1. Receive request with Idempotency-Key: xxx-123.
  2. Check in Redis/Database: Has this key been processed?
  3. If already processed: Return the previous result immediately (don't re-run business logic).
  4. If not yet processed: Run logic, save the result along with the key to the DB, and then return.

2. Implementation for Small Teams

Don't try to make the key management system too complex.

  • Use Redis: With a TTL (Time To Live) of about 24h for keys. Enough to handle typical retry cases.
  • Leverage the Database itself: A unique constraint on a request_id column in a Transactions table is also an extremely robust and simple way to implement idempotency.

3. Trade-off: Storage and Latency

  • Storage: You have to spend extra space to store these keys and their corresponding results.
  • Latency: Adds a step to check the DB/Redis before every action.

But trust me: This price is too cheap compared to spending a whole week on manual reconciliation and apologizing to customers for incorrect charges.


Lessons Learned

  1. Design Persistent Clients: Clients must know how to retry when encountering network errors, but should always send the same Key.
  2. Idempotency is Not Just for APIs: It also applies to Message Queue Consumers. If a message is sent twice into a queue (which is quite normal in NATS/RabbitMQ), your service must be sharp enough not to process it a second time.
  3. Learn from Stripe: Stripe's API is the gold standard for Idempotency design. If you have time, read their docs.

Conclusion

In distributed systems, errors are certain to happen. Instead of trying to prevent network errors (which is impossible), design your services to be "stubborn" enough to handle repeated requests safely. That is the key to stability.

Series • Part 10 of 20

Microservice Journey: Lessons & Trade-offs

NextInfra as Code (IaC) - Automate or Die in a Mountain of Config
BFF (Backend for Frontend) - The Savior of UX
05Distributed Transactions - Why 'Immediate Consistency' is a Luxury06Authentication & Authorization - When API Gateway Carries the Team07Docker Compose is Not Enough - The Local Development Story08Database Separation - A Painful but Necessary 'Divorce'09BFF (Backend for Frontend) - The Savior of UX10Idempotency - Why Every API Should Be 'Stubborn'?Reading11Infra as Code (IaC) - Automate or Die in a Mountain of Config12The Pain Named Cloud Bill - Cost Optimization for 'Broke Teams'13When the System Goes Silent - Lessons on Post-mortem and Blame-free Culture14Contract Testing - When Integration Testing Becomes a Burden15AI-Service Integration - Bringing LLM/RAG into Microservice Architecture
TP

Written by Truong Pham

Software Engineer passionate about building high-performance systems and meaningful experiences.

Read more articles