رفتن به محتوای اصلی
Backend / Integration۹ دقیقه مطالعه

طراحی API Integration پایدار؛ Timeout، Retry، Idempotency و Circuit Breaker

الگوهای production برای اتصال به پرداخت، پیامک، CRM، HIS و سرویس‌های شخص ثالث.

By VOIDRA Engineering · Editorial Standard

طراحی API Integration پایدار؛ Timeout، Retry، Idempotency و Circuit Breaker

درخواست POST /payments Timeout می‌شود. آیا پرداخت انجام نشده یا انجام شده و پاسخ در شبکه گم شده است؟ اگر Client بی‌درنگ Retry کند، ممکن است پرداخت دوم ساخته شود؛ اگر Retry نکند، سفارش در وضعیت نامعلوم می‌ماند. مسئله اصلی Integration فقط خطای 500 نیست؛ مدیریت نتیجه Unknown است.

شبکه قابل اعتماد نیست، Provider همیشه در دسترس نیست و قرارداد API تغییر می‌کند. Integration حرفه‌ای از ابتدا Failure را بخشی از جریان عادی می‌داند.

مدل Failure را بنویسید

حداقل این حالت‌ها را تفکیک کنید:

  • Connection برقرار نمی‌شود.
  • DNS/TLS شکست می‌خورد.
  • Request ارسال می‌شود ولی Timeout رخ می‌دهد.
  • 429 با Retry-After.
  • 4xx ناشی از Contract/Auth/Validation.
  • 5xx موقت یا پایدار.
  • پاسخ 200 با Payload ناقص/نامعتبر.
  • عملیات مقصد موفق است اما ذخیره محلی شکست می‌خورد.
  • Webhook تکراری، دیر یا خارج از ترتیب می‌رسد.

هر حالت رفتار متفاوتی می‌خواهد. catch { retry } معماری Resilience نیست.

Timeout؛ بودجه زمانی، نه عدد تصادفی

Connection timeout و Request timeout را از هم بشناسید. Timeout باید با Latency واقعی Provider و SLO جریان هماهنگ باشد. Timeout خیلی بلند Resource را اشغال و Failure را دیر آشکار می‌کند؛ خیلی کوتاه Failure کاذب و Retry load می‌سازد.

Budget انتهابه‌انتها را تقسیم کنید. اگر API شما باید در دو ثانیه پاسخ دهد، سه Dependency با Timeout دوثانیه‌ای زنجیره‌ای منطقی نیست. Cancellation را تا پایین Stack منتقل کنید و عملیات قابل قطع را واقعاً متوقف کنید.

Retry؛ فقط خطای گذرا و عملیات ایمن

Retry برای خطای احتمالا موقت مانند برخی Timeoutها، 429ها و 5xxهاست. Validation error و Unauthorized با همان درخواست اصلاح نمی‌شوند.

اصول:

  • تعداد و زمان کل محدود.
  • Exponential backoff به‌همراه Jitter.
  • احترام به Retry-After.
  • Retry در یک لایه مالک؛ نه چند SDK/Proxy/Service هم‌زمان.
  • Retry budget برای جلوگیری از Storm جمعی.
  • Telemetry برای Attemptها، نه Log گمراه‌کننده به‌عنوان Request مستقل.

اگر پنج لایه هرکدام سه بار Retry کنند، بار نهایی می‌تواند چند برابر انتظار شود.

Idempotency؛ Retry امن برای Write

Idempotency یعنی تکرار درخواست با همان Intent، Side effect جدید نسازد. Client یک Key یکتا برای عملیات منطقی تولید می‌کند؛ Server نتیجه اولین پردازش را با همان Key و Scope ذخیره و در Retry بازمی‌گرداند.

نکات طراحی:

  • Key به User/Tenant و Operation scope شود.
  • Request fingerprint مانع استفاده همان Key برای Payload متفاوت شود.
  • Stateهای processing/succeeded/failed و Race condition مدیریت شوند.
  • TTL با پنجره Retry و نیاز کسب‌وکار هماهنگ باشد.
  • پاسخ قبلی یا Resource ID قابل بازیابی باشد.

Unique constraint در Database معمولاً بخش مهم تضمین است. Check-then-insert بدون Constraint در Concurrency شکست می‌خورد.

Circuit Breaker با Retry فرق دارد

Retry امیدوار است Failure گذرا باشد؛ Circuit Breaker پس از الگوی Failure، درخواست‌های جدید را موقتاً Fail-fast می‌کند تا Dependency فرصت بازیابی داشته باشد.

سه State رایج:

  • Closed: درخواست عبور می‌کند و Failure سنجیده می‌شود.
  • Open: درخواست سریع رد یا Graceful degradation می‌شود.
  • Half-open: تعداد محدودی Probe برای بررسی Recovery.

Threshold باید براساس نرخ و Window باشد؛ عدد ثابت برای Trafficهای متفاوت رفتار بدی دارد. Breaker باید per dependency/operation مناسب Scope شود و Metric/Alert داشته باشد.

Bulkhead و Backpressure

اگر یک Provider کند تمام Thread/Connection/Worker را مصرف کند، سایر بخش‌ها هم سقوط می‌کنند. Concurrency limit، Pool جدا، Queue محدود و Bulkhead Failure را محصور می‌کنند. Queue بی‌نهایت راه‌حل نیست؛ Latency را پنهان و Recovery را سخت می‌کند. وقتی Capacity پر است باید Reject، Delay یا degrade براساس Business priority مشخص باشد.

Adapter؛ Contract خارجی وارد Domain نشود

DTO و Error provider را پشت Adapter نگه دارید. Domain با مدل داخلی کار کند. Adapter مسئول Mapping، Authentication، Timeout و Normalization است. این مرز Change provider، Test و Mock failure را آسان‌تر می‌کند.

Version API خارجی را Pin و Breaking change را Monitor کنید. «JSON شبیه قبلی است» Contract نیست؛ Schema validation پاسخ ضروری است.

Webhook؛ At-least-once را فرض کنید

Provider ممکن است Event را تکراری، خارج از ترتیب یا با تأخیر بفرستد:

  • Signature را روی Raw body و الگوریتم رسمی بررسی کنید.
  • Timestamp/Replay window در صورت پشتیبانی.
  • Event ID را Deduplicate کنید.
  • سریع 2xx بدهید و پردازش طولانی را Queue کنید.
  • ترتیب را با Version/OccurredAt و State machine مدیریت کنید.
  • Source of truth را بشناسید؛ Webhook گم‌شده با Reconciliation جبران شود.

Webhook endpoint نباید تنها رکورد حقیقت باشد.

Queue و Dead-letter

Queue Request path را از Dependency جدا و Burst را هموار می‌کند، اما Delivery و Poison message ایجاد می‌کند. Consumer باید Idempotent باشد، Attempt metadata و Visibility timeout درست داشته باشد. پس از تلاش محدود، پیام به DLQ/Failure store می‌رود؛ DLQ بدون Owner، Alert و Replay tool فقط قبرستان پیام است.

Reconciliation؛ دفاع در برابر خطای خاموش

Job دوره‌ای Source و Destination را مقایسه می‌کند: پرداخت موفق بدون Order، Lead فرم بدون CRM ID، یا Shipment بدون Status. Reconciliation برای Integration مالی و عملیاتی ضروری است، زیرا همه Failureها Alert فوری تولید نمی‌کنند.

Observability انتهابه‌انتها

  • Correlation/Trace ID در مرزها.
  • Structured log بدون Secret/PII اضافی.
  • Metric latency، availability، retry، breaker state، queue depth و reconciliation gap.
  • Trace برای Dependency call و Attempt.
  • Business metric مثل نرخ Order sync موفق.

Provider 99.9% Availability ممکن است برای Workflow چندمرحله‌ای Outcome پایین‌تری بسازد. SLO را برای نتیجه کاربر تعریف کنید، نه فقط هر Endpoint.

Authentication و Secret

OAuth token refresh باید Race-safe و Observable باشد. Secret در Log، Query string یا Client bundle قرار نگیرد. Scope حداقلی، Rotation و Environment separation لازم است. Clock skew، Certificate expiry و Revocation نیز Failure mode هستند.

تست Resilience

Happy path کافی نیست. تست کنید:

  • Timeout قبل و بعد از Commit مقصد.
  • 429 با Retry-After.
  • 5xx متناوب و پایدار.
  • پاسخ Schema-invalid.
  • Duplicate و out-of-order webhook.
  • Queue consumer crash وسط پردازش.
  • Expired credential و Clock skew.
  • Provider recovery هنگام Circuit half-open.

در Staging یا محیط کنترل‌شده Failure injection انجام دهید؛ Incident اولین تست نباشد.

نمونه Pipeline تصمیم

Validate request
→ assign correlation + idempotency key
→ call adapter with timeout
→ retry only transient failures within budget
→ circuit breaker / concurrency limit
→ persist outcome or UNKNOWN state
→ enqueue recovery/reconciliation when needed
→ expose traceable status to caller

چک‌لیست Production

  • [ ] Contract و Source of truth
  • [ ] Timeout budget و Cancellation
  • [ ] Retry classification، backoff، jitter و budget
  • [ ] Idempotency برای Side effect
  • [ ] Circuit breaker و Bulkhead
  • [ ] Webhook signature، dedupe و order handling
  • [ ] Queue/DLQ با Owner و Replay
  • [ ] Reconciliation و Unknown state
  • [ ] Trace/Metric/Alert و Redaction
  • [ ] Credential rotation و Runbook

جمع‌بندی

API Integration پایدار با «Retry بیشتر» ساخته نمی‌شود. Timeout نتیجه را ممکن است نامعلوم کند؛ Idempotency تکرار را امن می‌کند؛ Circuit Breaker و Bulkhead Failure را محصور می‌کنند؛ Queue و Reconciliation فاصله زمانی و خطای خاموش را مدیریت می‌کنند. مهم‌ترین ویژگی سیستم، قابل توضیح بودن وضعیت هر عملیات است.

سؤالات متداول

چه خطاهایی را Retry کنیم؟

فقط خطاهای موقت با احتمال Recovery مانند برخی Timeoutها، 429 و 5xx؛ با محدودیت، Backoff/Jitter و عملیات Idempotent.

تفاوت Retry و Circuit Breaker چیست؟

Retry همان درخواست را دوباره امتحان می‌کند؛ Circuit Breaker وقتی Dependency احتمالاً خراب است درخواست‌های جدید را موقتاً متوقف می‌کند.

Idempotency Key چیست؟

شناسه یکتای یک عملیات منطقی است که Server با آن تکرار درخواست را تشخیص می‌دهد و Side effect دوم نمی‌سازد.

Timeout یعنی عملیات انجام نشده؟

خیر. ممکن است عملیات مقصد انجام شده و پاسخ گم شده باشد. باید وضعیت Unknown، Query status، Idempotency یا Reconciliation داشته باشید.

آیا Queue همه Integrationها را پایدار می‌کند؟

خیر. Queue نیازمند Consumer idempotent، Retry، DLQ، Monitoring و Backpressure است و Complexity جدید دارد.

Webhook تکراری را چگونه مدیریت کنیم؟

Signature را Verify و Event ID را با Constraint/Dedup store ثبت کنید. Handler باید Idempotent باشد.

منابع رسمی و مطالعه بیشتر

Failure Model اتصال سیستم‌ها را پیش از توسعه مشخص کنید

اگر چند API، Webhook یا سیستم Legacy باید بدون گم‌شدن و تکرار داده به هم متصل شوند، Failure model باید پیش از Code روشن شود. VOIDRA می‌تواند Contract، Idempotency، Recovery و Observability Integration را طراحی یا Audit کند.

شروع مشاوره پروژه