امتحان مجدد توابع ناهمزمان

این سند توضیح می‌دهد که چگونه می‌توانید درخواست کنید که عملکردهای پس‌زمینه ناهمگام (غیر HTTPS) درصورت ناموفق بودن دوباره امتحان شوند.

چرا توابع رویدادمحور تکمیل نمی‌شوند

در موارد نادر، ممکن است عملکردی به‌دلیل خطای داخلی زودتر از موعد خارج شود، و به‌طور پیش‌فرض ممکن است عملکرد به‌طور خودکار دوباره امتحان شود یا نشود.

به‌طور معمول، یک تابع رویدادمحور ممکن است به‌دلیل خطاهایی که در خود کد تابع ایجاد می‌شود، نتواند با موفقیت تکمیل شود. دلایل این اتفاق عبارت‌اند از:

  • تابع حاوی اشکال است و زمان اجرا استثنایی را ایجاد می‌کند.
  • تابع نمی‌تواند به نقطه پایانی سرویس دسترسی پیدا کند یا هنگام تلاش برای انجام این کار، زمان آن تمام می‌شود.
  • تابع به‌طور عمدی استثنایی را ایجاد می‌کند (برای مثال، وقتی پارامتری اعتبارسنجی نمی‌شود).
  • تابع Node.js وعده ردشده‌ای را برمی‌گرداند یا مقدار غیرnull را به فراخوانی برمی‌گرداند.

در هریک از موارد بالا، تابع اجرا نخواهد شد و خطایی برمی‌گرداند. راه‌اندازهای رویداد که پیام‌ها را تولید می‌کنند دارای خط‌مشی‌های تکرار مجدد هستند که می‌توانید آن‌ها را سفارشی‌سازی کنید تا نیازهای تابع شما را برآورده کنند.

معناشناسی امتحان مجدد

Cloud Functions اجرای حداقل یک‌باره تابع رویدادمحور را برای هر رویداد منتشرشده توسط منبع رویداد ارائه می‌دهد. به‌طور پیش‌فرض، اگر فراخوانی تابع با خطا خاتمه یابد، تابع دوباره فراخوانی نمی‌شود و رویداد حذف می‌شود. وقتی تلاش مجدد را در تابع رویدادمحور فعال می‌کنید، Cloud Functions فراخوانی تابع ناموفق را تا زمانی که با موفقیت تکمیل شود یا پنجره تلاش مجدد منقضی شود تکرار می‌کند.

وقتی تلاش مجدد برای تابعی فعال نشده باشد (که حالت پیش‌فرض است)، تابع همیشه گزارش می‌دهد که با موفقیت اجرا شده است و ممکن است کدهای پاسخ 200 OK در گزارش‌های آن ظاهر شود. این اتفاق حتی اگر تابع با خطایی مواجه شود رخ می‌دهد. برای اینکه مشخص شود تابع شما چه زمانی با خطا مواجه می‌شود، حتماً خطاها را به‌درستی گزارش کنید.

پیکربندی تلاش‌های مجدد از کد تابع

با Cloud Functions for Firebase، می‌توانید تلاش‌های مجدد را در کد برای یک تابع فعال کنید. برای انجام این کار برای رویداد پس‌زمینه‌ای مثل ایجاد سند Firestore جدید، گزینه خط‌مشی failurePolicy (نسل اول) یا retry (نسل دوم) را روی true تنظیم کنید:

نسل اول

exports.docCreated = functions
  .runWith({
    // retry on failure
    failurePolicy: true,
  })
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    /* ... */
  });

نسل دوم

const { onDocumentCreated } = require("firebase-functions/firestore");

exports.docCreated = onDocumentCreated(
  {
    // retry on failure
    retry: true,
  },
  "my-collection/{docId}",
  (event) => {
    /* ... */
  },
);

تنظیم true به‌صورت نشان‌داده‌شده، تابعی را برای امتحان مجدد درصورت ناموفق بودن پیکربندی می‌کند.

پنجره امتحان مجدد

برای توابع نسل دوم، این پنجره تکرار مجدد پس‌از ۲۴ ساعت منقضی می‌شود. برای توابع نسل اول، پس‌از ۷ روز منقضی می‌شود. Cloud Functions توابع رویدادمحور جدیداً ایجادشده را بااستفاده از راهبرد عقب‌نشینی نمایی، با افزایش عقب‌نشینی بین ۱۰ تا ۶۰۰ ثانیه، دوباره امتحان می‌کند. این خط‌مشی برای اولین‌باری که توابع جدید را مستقر می‌کنید برای آن‌ها اعمال می‌شود. این تغییرات به‌صورت عطف به‌ماسبق برای توابع موجودی که برای اولین‌بار قبل‌از اعمال تغییرات شرح‌داده‌شده در این یادداشت‌های انتشار مستقر شده‌اند اعمال نمی‌شود، حتی اگر توابع را دوباره مستقر کنید.

روال‌های مطلوب

این بخش روال‌های مطلوب برای استفاده از تلاش‌های مجدد را شرح می‌دهد.

برای رسیدگی به خطاهای گذرا، از امتحان مجدد استفاده کنید

چون تابع شما به‌طور مداوم تا اجرای موفقیت‌آمیز تکرار می‌شود، خطاهای دائمی مانند اشکالات باید ازطریق آزمایش قبل‌از فعال کردن تکرارها از کد شما حذف شوند. تلاش مجدد بهترین روش برای مدیریت خرابی‌های متناوب یا گذرا است که احتمال حل شدن آن‌ها با تلاش مجدد زیاد است، مانند نقطه پایانی سرویس ناپایدار یا اتمام مهلت.

شرط پایانی تنظیم کنید تا از حلقه‌های تکرار بی‌نهایت جلوگیری شود

بهترین روش این است که هنگام استفاده از تکرار مجدد، از تابع خود دربرابر حلقه مداوم محافظت کنید. می‌توانید این کار را با گنجاندن یک شرط پایان به‌خوبی تعریف‌شده، قبل‌از شروع پردازش تابع انجام دهید. توجه داشته باشید که این تکنیک فقط درصورتی کار می‌کند که تابع شما با موفقیت شروع شود و بتواند شرط پایان را ارزیابی کند.

یک رویکرد ساده اما مؤثر این است که رویدادهایی را که مُهر زمان آن‌ها قدیمی‌تر از زمان خاصی است کنار بگذارید. این کار کمک می‌کند تا از اجرای بیش‌ازحد در مواقعی که خرابی‌ها مداوم یا طولانی‌تر از حد انتظار هستند جلوگیری شود.

برای مثال، این تکه‌کد همه رویدادهای قدیمی‌تر از ۱۰ ثانیه را حذف می‌کند:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

استفاده از catch با Promises

اگر تابع شما بازامتحان را فعال کرده باشد، هر خطای مدیریت‌نشده‌ای باعث فعال شدن بازامتحان می‌شود. مطمئن شوید که کد شما هرگونه خطایی را که نباید منجر به تکرار شود ضبط می‌کند.

در اینجا مثالی از آنچه باید انجام دهید آورده شده است:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

توابع رویدادمحور قابل‌تکرار را یکسان‌توان کنید

توابع رویدادمحور که می‌توانند دوباره امتحان شوند باید یکسان‌توان باشند. در اینجا چند دستورالعمل کلی برای یکسان‌توان کردن چنین تابعی آورده شده است:

  • بسیاری از میاناهای برنامه‌سازی کاربردی خارجی (مانند Stripe) به شما امکان می‌دهند کلید هم‌توانی را به‌عنوان پارامتر ارائه دهید. اگر از چنین میانای برنامه‌سازی کاربردی‌ای استفاده می‌کنید، باید از شناسه رویداد به‌عنوان کلید هم‌توانی استفاده کنید.
  • Idempotency با حداقل یک‌بار تحویل به‌خوبی کار می‌کند، زیرا امتحان مجدد را ایمن می‌کند. بنابراین، یک روال مطلوب کلی برای نوشتن کد قابل‌اعتماد، ترکیب کردن هم‌پایی با تکرار مجدد است.
  • مطمئن شوید که کدتان ازنظر داخلی یکسان‌توان باشد. برای مثال:
    • مطمئن شوید که جهش‌ها می‌توانند بیش‌از یک بار بدون تغییر نتیجه اتفاق بیفتند.
    • قبل‌از تغییر دادن وضعیت، وضعیت پایگاه داده را در تراکنش پُرسمان کنید.
    • مطمئن شوید که همه عوارض جانبی خودشان هم‌پایا باشند.
  • بررسی تراکنشی را خارج از تابع و مستقل از کد اعمال کنید. برای مثال، وضعیت را در جایی حفظ کنید که ثبت کند شناسه رویداد داده‌شده قبلاً پردازش شده است.
  • با فراخوانی‌های تابع تکراری به‌صورت خارج از باند برخورد کنید. برای مثال، فرایند پاک‌سازی جداگانه‌ای داشته باشید که پس‌از فراخوانی‌های تابع تکراری پاک‌سازی کند.

پیکربندی خط‌مشی تلاش مجدد

بسته به نیازهای تابع، ممکن است بخواهید خط‌مشی تکرار مجدد را مستقیماً پیکربندی کنید. این کار به شما امکان می‌دهد هر ترکیبی از موارد زیر را راه‌اندازی کنید:

  • پنجره تکرار را از ۷ روز به ۱۰ دقیقه کاهش دهید.
  • حداقل و حداکثر زمان عقب‌نشینی را برای راهبرد تلاش مجدد عقب‌نشینی نمایی تغییر دهید.
  • استراتژی تکرار را به تکرار فوری تغییر دهید.
  • موضوع پیام‌های ناموفق را پیکربندی کنید.
  • حداکثر و حداقل تعداد تلاش برای ارسال را تنظیم کنید.

برای پیکربندی خط‌مشی تلاش مجدد:

  1. تابع HTTP بنویسید.
  2. از Pub/Sub API برای ایجاد اشتراک Pub/Sub استفاده کنید و نشانی وب تابع را به‌عنوان هدف مشخص کنید.

برای کسب اطلاعات بیشتر درباره پیکربندی مستقیم Pub/Sub، Pub/Sub اسناد مربوط به رسیدگی به خرابی‌ها را ببینید.