Bug: Idempotency Key Not Checked Before Processing in order_service
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 58/100
Direzione di ricerca
Inizia in order_service/app.py, in create_order(), soprattutto nella gestione di Idempotency-Key e nella ricerca nella tabella orders descritta nell’issue. Esegui docker compose up -d --build e riproduci la richiesta duplicata POST /api/v1/orders per osservare le chiamate esterne e la prenotazione delle scorte. Il lavoro è completato quando una chiave ripetuta restituisce l’id e lo stato originali con HTTP 200 e non causa chiamate a servizi esterni né effetti collaterali sulle scorte.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Bug: Idempotency Key Not Checked Before Processing in order_service
Summary
The POST /api/v1/orders endpoint accepts an Idempotency-Key header to prevent duplicate order creation. However, the key is only enforced at the database INSERT level via a UNIQUE KEY constraint — after all expensive and side-effectful operations have already been executed. This means duplicate requests will call the User Service, call the Product Service, and reserve stock before ultimately failing at the DB insert, leaving the inventory in a corrupted state.
Affected File
order_service/app.py→create_order()function
Steps to Reproduce
- Start all services with
docker compose up -d --build. - Login and obtain a JWT token via
POST /api/v1/login. - Send
POST /api/v1/orderswith a valid body and anIdempotency-Key: test-key-123header. - Immediately send the exact same request again with the same
Idempotency-Key: test-key-123. - Observe that on the second request, the system still calls the Product Service and reserves stock again before returning an error.
Expected Behavior
If an Idempotency-Key is provided and a matching order already exists in the database, the endpoint should:
- Return the original order's response (
id+status) immediately with HTTP200. - Skip all external service calls — no User Service call, no Product Service call, no stock reservation.
- Be a pure no-op with zero side effects.
Actual Behavior
The duplicate request goes through the full execution pipeline:
- ✅ Idempotency key is read from the header.
- ❌ No early DB lookup is performed.
- ❌ User Service is called unnecessarily.
- ❌ Product Service is called unnecessarily.
- ❌ Stock is reserved again for all items in the order.
- ❌ DB
INSERTfails with aUNIQUE KEYconstraint violation. - ❌ Stock that was just reserved is released (rollback), but this adds extra load and a race condition window.
Root Cause
In order_service/app.py, inside create_order(), the Idempotency-Key is read early but never looked up in the database until the INSERT statement:
# Line 105 — key is read idmp_key = request.headers.get('Idempotency-Key')Lines 111–210 — full business logic runs regardless of whether key is duplicate:
user validation → address validation → product validation → stock reservation → DB insert
The UNIQUE KEY constraint on the idempotency_key column only prevents the duplicate record from being saved — it does not prevent the side effects that already happened before the insert.
Fix
Add an early database lookup for the Idempotency-Key immediately after reading it, before any external service calls:
idmp_key = request.headers.get('Idempotency-Key')Check idempotency key FIRST — before any external service calls
if idmp_key:
conn = get_db_connection()
if not conn:
return jsonify({'error': 'Database connection failed'}), 500
try:
cur = conn.cursor(dictionary=True)
cur.execute(
"SELECT id, status FROM orders WHERE idempotency_key = %s LIMIT 1",
(idmp_key,)
)
existing = cur.fetchone()
finally:
conn.close()
if existing:
# Return the original order response — idempotent, no side effects
return jsonify({'id': existing['id'], 'status': existing['status']}), 200
Impact
| Scenario | Before Fix | After Fix |
|---|---|---|
| Duplicate request with same key | Reserves stock again, then fails | Returns cached response immediately ✅ |
| Network retry by client | May cause double stock deduction | Safely returns original order ✅ |
| First-time request | Works normally | Works normally ✅ |
| Request without key | Works normally | Works normally ✅ |
Severity: 🔴 Critical — In a production environment with retrying clients or unreliable networks, this bug can cause inventory to be incorrectly decremented on every duplicate request, leading to stock going negative or orders being unfulfillable.
Environment
- Service:
order_service - Language: Python 3 / Flask
- Database: MySQL 8.0
- Relevant endpoint:
POST /api/v1/orders
- Lingua principale
- Python
- Stelle
- 3
- Fork
- 8
- Merge medio
- 1h 6m
- PR unite (30g)
- 3
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Nessun modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di keploy/ecommerce_sample_app
-
Improve and restructure README documentation for better clarity Issue 161Forse già presa @Jerry-145 l’ha presa 244 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
-
Hardcoded Credentials Security Issue T152Forse di nuovo libera @Talaviya-Sarthak l’ha presa 245 giorni fa e non c’è nessuna pull request aperta. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 32/100
keploy/ecommerce_sample_app#7 · 1 commento ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
keploy/ecommerce_sample_app#6 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 55/100
keploy/ecommerce_sample_app#5 · 1 commento ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 42/100
keploy/ecommerce_sample_app#4 · 1 commento ·
Tutte le issue di keploy/ecommerce_sample_app
Issue simili
-
tests/strategies_test.py:174 goes uncovered on about 1 run in 100, failing the coverage ratchetApertaflaky-watch
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
btclib-org/btclib#2839 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
nion-software/nionswift#2018 ·
I maintainer di solito rispondono entro 1 giorno
-
Deleting a member fails when another member's share transfer points to themForse già presa @Beerlesklopfer l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
FoodCoopX/wirgarten-tapir#1336 ·
I maintainer di solito rispondono entro 3 giorni
-
pydanty:is-working
Difficoltà 2/5 1-3 ore Idoneità per principianti 71/100
pydantic/pydantic-ai#11467 ·
I maintainer di solito rispondono entro 1 giorno
-
Link Checker ReportApertaautomated issue linkchecker report
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
QuantEcon/lecture-python.myst#1098 ·
I maintainer di solito rispondono entro 1 giorno