تعامل مع عدم العثور وإعادة المحاولة دون دفع مرتين
ما ستبنيه: غلاف استدعاء يميز ثلاث نتائج — وُجد، ولم يُوجد، وفشل — ويعيد المحاولة للثالثة فقط، مع مفتاح تكرار يمنع أي خصم مزدوج.
| القسم | أساسيات الواجهة |
|---|---|
| الإجراءات المستخدمة | POST /v1/data/contacts/find-email |
| الائتمانات | 5 (أسوأ الحالات) |
| آمن مع مفتاح الاختبار | نعم — يعمل على qk_test_ بصفر ائتمان |
| مدة الإنجاز | ~10 دقيقة |
| المتطلبات | مفتاح، وعميل يستطيع ضبط ترويسات الطلب. |
ثلاث نتائج لا نتيجتان
النتيجة الموجودة هي HTTP 200 مع data ممتلئة وخصم غير صفري. وعدم العثور هو HTTP 200 مع data: null و X-Credits-Charged: 0 — جرى البحث بأمانة ولم يجد شيئًا، فلم يُحاسَب شيء. أما الفشل فهو 4xx أو 5xx. الثالثة وحدها تستحق إعادة المحاولة.
# FOUND — data populated, charged
# < 200 X-Credits-Charged: 5
{ "status": "OK", "data": { /* … */ } }
# MISS — ran honestly, found nothing, billed zero
# < 200 X-Credits-Charged: 0
{ "status": "OK", "data": null }
# FAILURE — nothing billed, safe to retry
# < 503لا تُعِد المحاولة عند عدم العثور
عدم العثور إجابة حقيقية عن الواقع، لا خطأ عابرًا. إعادة المحاولة لن تستحضر نتيجة؛ إنما تضيف زمن انتظار. خزِّن النتيجة السالبة لمدة معقولة وامضِ.
# Branch on the payload BEFORE the status code:
# a miss and a hit are both HTTP 200.
if res.status_code >= 500:
return retry_later() # transient
if res.json()["data"] is None:
cache_miss(query, ttl=86_400) # a real answer — do not retry
return None
return res.json()["data"]أعد المحاولة عند الفشل مع مفتاح تكرار
تقبل نقاط البيانات ترويسة Idempotency-Key. أرسل مفتاحًا ثابتًا لكل عملية منطقية، فتؤول إعادة المحاولة بعد انتهاء المهلة إلى النتيجة الأصلية بدل استدعاء ثانٍ مدفوع — وهو الأهم حين لا ترى الاستجابة الأولى أصلًا.
# A stable key per logical operation. If you never saw the first
# response, the retry resolves to it instead of billing twice.
curl -X POST https://api.qannasapi.com/v1/data/contacts/find-email \
-H "Authorization: Bearer $QANNAS_API_KEY" \
-H "Idempotency-Key: signup-8f21c4" \
-H "Content-Type: application/json" \
-d '{"query": "Jane Doe, Acme Logistics"}'تراجع تدريجيًا، ولا تُعِد أبدًا عند 4xx
أعد المحاولة عند 5xx وانتهاء المهلة بتراجع أسّي مع تشويش. أما 422 فيعني أن جسم الطلب خاطئ: أصلحه بدل إعادة إرساله. أعطال المصادر لا تُحاسَب أبدًا، فإعادة المحاولة بعد فشل حقيقي لا تكلفك شيئًا إضافيًا.
import random, time
def call_with_retry(send, *, max_attempts=5):
for attempt in range(max_attempts):
res = send()
if res.status_code == 422:
raise ValueError(res.text) # malformed — never resend
if res.status_code < 500:
return res
# 5xx: upstream failures are never billed, so retrying is free
time.sleep(0.5 * 2 ** attempt + random.uniform(0, 0.5))
raise RuntimeError("exhausted retries")الخلاصة
| النمط | الطريقة |
|---|---|
| وُجد | 200، data ممتلئة، X-Credits-Charged > 0 |
| لم يُوجد | 200، data: null، X-Credits-Charged: 0 — لا تُعِد المحاولة |
| فشل | 4xx / 5xx — أعد المحاولة عند 5xx وانتهاء المهلة فقط |
| إعادة محاولة آمنة | ترويسة Idempotency-Key، ثابتة لكل عملية منطقية |
| طلب خاطئ | 422 — أصلح الجسم، ولا تعد إرساله كما هو |
| التراجع | أسّي مع تشويش، وبعدد محاولات محدود |
قائمة التحقق قبل الإنتاج
- تفرَّع على data == null قبل أن تتفرع على رمز الحالة.
- أرسل Idempotency-Key مع أي شيء قد تعيد محاولته.
- أعد المحاولة عند 5xx وانتهاء المهلة؛ ولا تعدها أبدًا عند 422.
- خزِّن نتائج عدم العثور حتى لا تعيد السؤال نفسه طوال اليوم.
الخطوات التالية
شغِّل التقدير أولًا. لا يكلف شيئًا، وهو الحساب نفسه الذي سيطبقه العدّاد.