تخطَّ إلى المستندات
Python

إعادة المحاولة واللاتكرارية

ما يُعاد، وما لا يُعاد عن قصد، ولماذا لا يمكن لإرسال مُعاد أن يتضاعف.

الإرسالات

يرفق العميل ترويسة Idempotency-Key بكل إرسال (emails.send وemails.send_batch وtemplates.send وbroadcasts.send)، تُولَّد مرة واحدة لكل **نداء** وتعيد محاولات ذلك النداء استخدامها. ويحجز API ذلك المفتاح قبل أن يرسل أي شيء، فتعيد المحاولة تشغيل الرسالة الأصلية بدل إرسال ثانية، بينما يظل نداءا send() المتعمّدان يرسلان مرتين. فهاتان نيتان مختلفتان وتبقيان مختلفتين.

مرّر idempotency_key الخاص بك لمدّ ذلك الضمان عبر العمليات، فتُعيد مهمة انهارت ثم عملت من جديد تشغيل إرسالاتها بدل تكرارها.

idempotency.py
invoice_id = 'inv_4192' client.emails.send(    {'from': sender, 'to': recipient, 'subject': subject, 'text': text},    idempotency_key=f'invoice:{invoice_id}',)

اشتقّه مما جعل الإرسال ضروريًا. ولا تشتقّه من ساعة أبدًا. وإعادة استخدام مفتاح بمتن مختلف تُرفض بـidempotency_key_reuse بدل أن تُعاد صامتةً.

كل ما عدا ذلك

تُعاد كل قراءة. أما الكتابة فلا تُعاد إلا حيث لا يمكن لطلب ثانٍ مطابق أن يعني شيئًا مختلفًا عن الأول، والإرسال مؤهل لذلك لأن مفتاح اللاتكرارية لديه يحوّل التكرار إلى إعادة تشغيل.

الاستدعاءيُعادالسبب
كل قراءةنعملا شيء يتغير.
emails.send، emails.send_batch، templates.send، broadcasts.sendنعممفتاح اللاتكرارية يجعل التكرار إعادة تشغيل.
emails.cancel، emails.reschedule، broadcasts.cancel، forms.publish، forms.pause، forms.resume، forms.approve_submission، account.accept_invitation، account.decline_invitationنعمضبط خالص لحالة مسمّاة.
threads.update، threads.trash، threads.restoreنعمضبط تسميات. وتطبيقه مرتين هو تطبيقه مرة واحدة.
threads.snooze, threads.unsnoozeنعملحظة الاستيقاظ موجودة في المتن، وليست مشتقة من وقت الوصول.
labels.update، webhooks.update، settings.update، roles.update، members.update، domains.update، contacts.update، audiences.update، keys.update، emails.update، forms.update، branding.update، chats.rename، threads.update_note، domains.update_address، domains.update_address_forward، account.set_email_notification، account.set_push_muted، app_host.set، workspaces.set_activeنعمضبط خالص لحقول مسمّاة.
members.grant_address، members.grant_domain، rules.reorder، threads.reorder_notesنعمالمنحة إدراج أو تحديث، والترتيب مذكور بالكامل.
templates.publish, imports.startنعمنشر رأس منشور أصلًا، أو بدء استيراد بدأ أصلًا، يعيده دون تغيير.
templates.preview، templates.render، broadcasts.preview، rules.testنعمتعرض أو تعدّ أو تقيّم، ولا تكتب شيئًا.
domains.verify, app_host.verifyنعمالفحص المكرَّر لا يغيّر شيئًا سوى وقت الفحص.
contacts.save، contacts.set_audiences، contacts.remove_photo، contacts.block، contacts.unblock، contacts.delete_many، keys.revoke، account.remove_photo، branding.remove_image، domains.remove_logo، domains.remove_logo_certificate، domains.remove_address_photo، app_host.delete، files.revoke_link، files.revoke_all_links، subscriptions.moveنعمكلٌّ منها يصرّح بالنتيجة النهائية، فيترك النداء الثاني ما تركه الأول.
audiences.add_contact، audiences.add_contacts، audiences.remove_contacts، audiences.import_contacts، suppressions.add، domains.create_address، senders.researchنعميجد التكرار عمل النداء الأول منجَزًا فيبلّغ عنه بدل أن يؤديه مرتين.
contacts.set_photo، imports.upload_chunk، account.set_photo، branding.upload_image، domains.set_logo، domains.set_logo_certificate، domains.set_address_photoنعمالبايتات المرسلة مجددًا تستبدل ما خزّنته المحاولة الأولى.
drafts.create، labels.create، webhooks.create، templates.create، rules.create، roles.create، temp_mail.create، files.upload، templates.design، forms.designلاإعادة المحاولة تترك كائنين.
drafts.updateلااقرأ المعرّف من نتيجة كل كتابة بدل إعادة استخدام المعرّف الذي أرسلته.
drafts.delete، labels.delete، webhooks.delete، templates.delete، rules.delete، roles.delete، members.remove، members.revoke_address، temp_mail.delete، temp_mail.delete_messageلاإعادة المحاولة بعد استجابة ضائعة تبلّغ بإخفاق عن عمل قد نجح.
webhooks.rotate_secretلاالتدوير الثاني يبطل السر الذي أعادته المحاولة الأولى.
webhooks.testلاسيرسل تسليمًا اصطناعيًا ثانيًا.
webhooks.replay_deliveryلاسترسل الحدث إلى مستقبِلك مرة ثانية.
emails.translateلاينفق نداءات نموذج، فإعادة المحاولة بعد طلب بلا جواب تشتري الجواب نفسه مرتين.
emails.compose، emails.rewrite، emails.suggest_subjectلاكل محاولة تستهلك إجراء ذكاء اصطناعي آخر وتعود بإجابة مختلفة.
كل استدعاء آخرلاتُرسل مرة واحدة، ويُبلَّغ عن الإخفاق بدل تكراره.

يُعاد forms.update حتى مع expectedUpdatedAt، لذا قد تعود إعادة المحاولة بعد استجابة ضائعة بالخطأ 409 version_conflict لأن المحاولة الأولى نجحت. اقرأ النموذج قبل أن تحاول مجددًا.

يعيد client.raw.request محاولة طلب GET ويرسل أي شيء آخر مرة واحدة، ما لم تمرّر repeatable=True.

التراجع التدريجي

  • محدودة بـmax_retries على العميل، وافتراضها محاولتان إضافيتان.
  • لا تحدث إلا بعد إخفاق في الشبكة أو 408 أو 500 أو 502 أو 503 أو 504. ولا يُعاد 429 إلا حين يحمل ترويسة Retry-After، وهذا API لا يرسلها، فيرفع حدّ المعدل استثناءً فورًا. وأي رمز حالة آخر يرفع استثناءً في الحال.
  • أسّية من نصف ثانية حتى ثماني ثوانٍ، مع تشويش زمني، كي لا يعيد أسطول من العملاء مزامنة نفسه عند التعافي.
  • تُضبط وتيرتها بـRetry-After بأي من صيغتيها، delay-seconds وHTTP-date. وحين يحدّد الخادم مدة انتظار، ينتظر العميل تلك المدة بالضبط بدل التراجع التدريجي.
  • الخادم الذي يطلب أكثر من دقيقة يُعامَل كمن يقول للعميل توقّف لا نَمْ، فيُرفع الخطأ ومعه retry_after_seconds. فالعودة قبل ما طلب ليست احترامًا لطلبه.
  • يحدّ timeout كل محاولة، لذا قد يستغرق الاستدعاء الذي يستهلك محاولتَي الإعادة كلتيهما ثلاث مهل كاملة إضافةً إلى فترات الانتظار بينها.
  • لا يُعاد أبدًا استدعاء أُلغي على AsyncOpenEmail. فالإلغاء ينتشر في الحال، من الطلب أو من الانتظار السابق للمحاولة التالية.