خطأ 429 لا يعني إعادة المحاولة فوراً: اضبط الانتظار المتدرّج

عالج خطأ 429 بإيقاف الطلب مؤقتاً، لا بإعادته فوراً. اقرأ Retry-After أولاً، ثم ترويسات إعادة ضبط الحصة التي يوثقها المزوّد، واستخدم انتظاراً أسياً مع عشوائية زمنية إذا لم تجد إشارة صالحة، مع حد نهائي للمحاولات.
لا تجدول الطلب قبل الموعد الذي حدده الخادم. وإذا تجاوز هذا الموعد المهلة الكلية التي تسمح بها العملية، فأنهها بخطأ واضح أو انقل المهمة إلى طابور مؤجل يستطيع احترام الانتظار، بدلاً من تقصير المهلة وإرسال الطلب مبكراً.
حدّد أولاً ما إذا كان الخطأ قابلاً لإعادة المحاولة
تعني الحالة 429 أن العميل أرسل طلبات أكثر مما تسمح به قواعد تحديد المعدل خلال مدة معينة. وتوضح وثائق Cloudflare للحالة 429 أن الاستجابة قد تتضمن Retry-After لتحديد موعد المحاولة التالية، وأن بعض استدعاءات API قد تكون لها حدود خاصة منفصلة عن الحد العام.
لا تفترض أن نجاح طلب إلى نقطة نهاية أخرى يعني انتهاء التقييد؛ فقد يكون العداد مرتبطاً بالحساب أو رمز الوصول أو عنوان IP أو مورد محدد. خزّن حالة الانتظار وفق النطاق الذي توثقه الخدمة، ونسّق الطلبات التي تشترك في النطاق نفسه.
ولا تُدخل كل استجابة 403 في مسار الانتظار. قد تستخدم بعض الواجهات 403 عند تجاوز حد للمعدل، لكن نقص الصلاحية أو رفض الوصول لن يعالجه التأخير؛ لا تعد المحاولة إلا إذا أكدت رسالة الخطأ أو الترويسات أو وثائق المزوّد أن السبب هو تحديد المعدل.
رتّب الترويسات ولا تخلط الموعد بالمدة

ابدأ بترويسة Retry-After عندما تكون صالحة. يحدد معيار HTTP في RFC 9110 قيمتها بصيغة HTTP-date أو عدداً صحيحاً غير سالب يمثل ثواني التأخير بعد استلام الاستجابة. حوّل الصيغتين إلى موعد داخلي واحد، وارفض التاريخ غير القابل للتحليل أو القيمة السالبة.
إذا غابت Retry-After، فافحص ترويسات الحصة التي يعرّفها المزوّد. في GitHub مثلاً، تمثل x-ratelimit-reset وقتاً مطلقاً بوحدة ثواني UTC epoch، وليست مدة انتظار؛ لذا يجب طرح الوقت الحالي منها، لا تمرير رقمها مباشرة إلى المؤقت.
لا تعمم اسم الترويسة أو وحدتها على جميع الخدمات. ضع محللاً خاصاً بكل مزوّد خلف واجهة موحدة، وقيّد المهل غير المعقولة بسياسة تشغيل واضحة: إذا كانت مهلة الخادم أطول من عمر العملية، لا تختصرها، بل أوقف العملية أو أجّلها خارج مسار الطلب المتزامن.
نفّذ خوارزمية بسقف زمني وعددي
يمكن توحيد القرار داخل عميل API بهذه الخطوات:
- نفّذ الطلب مرة واحدة وسجّل وقت استلام الاستجابة.
- عند النجاح، حدّث حالة الحصة من الترويسات الموثقة وأعد النتيجة.
- عند 429، أو 403 مؤكدة بوصفها تحديداً للمعدل، استخرج Retry-After ثم إشارة إعادة الضبط الخاصة بالمزوّد.
- إن غابت مهلة صالحة، احسب انتظاراً أسياً من مدة أساسية، مع سقف لكل فترة انتظار.
- أضف العشوائية الزمنية، ثم أعد جدولة المهمة من دون إبقاء اتصال أو خيط تنفيذ مشغولاً.
- قبل كل إعادة، افحص عدد المحاولات والمهلة الكلية وإشارة الإلغاء؛ أعد خطأً نهائياً إذا انتهى أي منها.
في مثال شرطي، إذا اختيرت ثانية واحدة أساساً و32 ثانية سقفاً محلياً، تصبح الفترات الخام 1 ثم 2 ثم 4 ثم 8 ثوانٍ قبل العشوائية. هذه إعدادات توضيحية وليست قيماً يفرضها HTTP؛ يحددها التطبيق وفق ميزانية زمن الاستجابة وقدرة النظام المستدعي على الانتظار.
إذا طلب الخادم الانتظار 90 ثانية ولم يبق للعملية سوى عشر ثوانٍ، فلا تخفض الانتظار إلى عشر ثوانٍ. أنهِ العملية بنتيجة قابلة للتصنيف تتضمن سبب التقييد، أو سلّم المهمة إلى طابور مؤجل يحافظ على الموعد.
أضف العشوائية على مستوى النطاق المشترك

الانتظار الأسي وحده قد يجعل العملاء الذين فشلوا معاً يستأنفون معاً، فتتكون موجة جديدة. وزّع مواعيد الإعادة داخل نافذة زمنية باستخدام jitter، مع إبقاء الموعد الذي حدده الخادم حداً لا تبدأ قبله.
عند الاعتماد على انتظار محلي، يمكن اختيار مدة من النصف الأعلى للنافذة الأسية: نصف القيمة المحسوبة، مضافاً إليه مقدار عشوائي بين الصفر والنصف الآخر. يمنع ذلك انتظاراً قريباً من الصفر، مع تفريق العملاء الذين وصلوا إلى رقم المحاولة نفسه.
يجب أن يكون التنسيق مشتركاً بين العمليات التي تستخدم الحصة ذاتها. إذا كانت عدة مهام تعمل برمز وصول واحد، فاجمعها خلف بوابة معدل أو طابور مركزي؛ وإلا قد يلتزم كل عامل بانتظاره منفرداً بينما يستمر مجموع العمال في تجاوز الحد.
ميّز الحد الأساسي من الثانوي ثم أوقف الحلقة

في GitHub، يؤدي تجاوز الحد الأساسي إلى 403 أو 429 مع وصول x-ratelimit-remaining إلى الصفر، ويجب الانتظار حتى x-ratelimit-reset. أما الحد الثانوي فقد يعيد 403 أو 429 أيضاً؛ وتضع تعليمات حدود REST API في GitHub Retry-After أولاً عند وجودها، ثم reset إذا كانت الحصة المتبقية صفراً، وإلا تطلب انتظار دقيقة على الأقل؛ وإذا استمر الخطأ، توصي بزيادة الانتظار أسياً وإنهاء العملية بعد عدد محدد من المحاولات، لأن مواصلة الإرسال أثناء التقييد قد تؤدي إلى حظر التكامل.
لهذا لا يكفي تأخير الطلب الفاشل عند ظهور حد ثانوي. خفّض التوازي ومعدل الإرسال للنطاق المتأثر، ثم أعد رفعهما تدريجياً بعد نجاح الطلبات؛ إبقاء بقية العمال بالمعدل نفسه يلغي أثر الانتظار الفردي.
ثبّت في إعداد العميل سقفاً لعدد المحاولات ومدة كلية للعملية وسقفاً لكل انتظار. وقبل إعادة طلب يغيّر البيانات، تأكد من وجود آلية تمنع تكرار الأثر، مثل مفتاح idempotency تدعمه الخدمة أو استعلام يتحقق من نتيجة المحاولة السابقة؛ فغياب الاستجابة لا يثبت أن العملية لم تُنفذ.
سجّل نطاق الحد ورقم المحاولة وحالة HTTP والترويسات المتاحة والمهلة المختارة، من دون رموز الوصول أو البيانات الحساسة. إذا استمرت استجابات 429 أو تكرر بلوغ السقف، فالمشكلة ليست نقص عدد المحاولات، بل معدل إرسال أعلى من قدرة الحصة، ويجب خفض التوازي أو تجميع الطلبات أو الحصول على حد مناسب.
اشترك في نشرتنا الإخبارية
احصل على أحدث أخبار الويب 3 والذكاء الاصطناعي والعملات المشفرة مباشرة في بريدك.