Back home

قابلیت اطمینان Rust/Wasm زمان اجرا مستلزم رسیدگی به بازیابی هراس و سقط است

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

Wasm در ابتدا می تواند به راحتی به عنوان یک لایه انتقال در نظر گرفته شود: کد را می توان برنامه ریزی کرد، صفحه می تواند اجرا شود، عملکرد خوب است، و به نظر می رسد همه چیز تقریباً یکسان است. معمولاً پس از گذراندن نسخه ی نمایشی، واقعاً شروع به سخت شدن می کند. هنگامی که ماژول‌هایی مانند ویرایشگرها، رندرها و تجزیه‌کننده‌های سند از آزمایش‌های تک صفحه‌ای به زمان‌های اجرای طولانی‌مدت ساکن منتقل شوند، مدل‌های خطا بلافاصله تغییر می‌کنند.

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

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

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

این مشکل پس از شروع استفاده مجدد از نمونه های زمان اجرا رخ می دهد. هنگامی که یک نمونه Wasm به طور مداوم چندین سند را باز می کند، چندین دور از رویدادهای ورودی را دریافت می کند و از چندین تماس پل JS عبور می کند، دامنه تأثیر وحشت و سقط دیگر در عمل فعلی متوقف نمی شود. یک شکست ناقص ممکن است درخواست های بعدی را پایین بیاورد.

چنین خطراتی اغلب در روز اول در معرض خطر قرار نمی گیرند. در مرحله اول، معمولاً فقط گزارش های خطای پراکنده را مشاهده می کنید: گاه به گاه خرابی های رندر، یک صادرات خاص گیر می کند و یک سند خاص پس از بسته شدن و بازگشایی در وضعیت نادرست است. اگر بیشتر بررسی کنید، سرنخ ها به تدریج به همان پدیده همگرا می شوند: اگرچه شکست در یک زنجیره تماس رخ داده است، آسیب در نمونه مشترک باقی مانده است.

در این مرحله، تمرکز بحث دیگر این نیست که “آیا کد Rust دچار وحشت خواهد شد یا خیر”، بلکه “آیا این زمان اجرا واجد شرایط ادامه سرویس تماس بعدی پس از وحشت است یا خیر”.

وحشت را می توان گرفتار کرد، سقط فقط می تواند موارد را تغییر دهد.

مهمترین چیزی که در Rust/Wasm باید از هم جدا شود، دو معنای شکست وحشت و سقط است.

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

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

  • به عنوان یک استثنا معمولی، پرستو سقط کرد، و استخر نمونه به استفاده مجدد از اشیایی که اعتبار خود را از دست داده اند ادامه خواهد داد.
  • با تمام وحشت ها طوری رفتار کنید که گویی نمونه باید از بین برود و توان عملیاتی به طور غیر ضروری کاهش می یابد
  • میزبان JS فقط می داند که “تماس ناموفق بود”، اما نمی داند که آیا باید دوباره تلاش کند، نمونه را از دست بدهد یا جلسه فعلی را قطع کند.

این نیز واقع بینانه ترین چیز در مورد قابلیت اطمینان زمان اجرا Wasm است: قبل از اجرای جداسازی و زمان بندی بعدی، ابتدا باید معنای بازیابی تعریف شود.

اگر لایه binding معنایی بازیابی را ارائه ندهد، لایه میزبان حالت بد را می گیرد و کار را ادامه می دهد.

خطرناک ترین مکان برای این نوع مشکل در کد کسب و کار نیست، بلکه در لایه اتصال است که به نظر می رسد “از قبل مراقبت شده است”. لایه میزبان اغلب فقط یک شی خطای پرتاب شده را می بیند و آن را به عنوان یک شکست عادی تماس ثبت می کند. گزارش وجود دارد و صفحه فوراً خراب نمی شود، اما سیستم ممکن است وضعیت بد را در داخل نمونه ترک کرده باشد.

آنچه واقعاً باید برطرف شود فقط تلاش/گرفتن نیست، بلکه اقدامات مدیریتی پس از شکست است. منطقی شبیه به زیر به تازگی وارد طراحی قابلیت اطمینان شده است:

async function runWithRecovery(instance, input) {
  try {
    return await instance.exports.handle(input)
  } catch (error) {
    if (isAbort(error)) {
      pool.replace(instance.id)
    }
    throw error
  }
}

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

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

نمونه های به اشتراک گذاشته شده مشکل بازیابی را به یک مشکل استراتژی ادغام تقویت می کنند

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

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

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

اینها پاسخ هایی نیستند که لایه زبان به طور خودکار ارسال کند. آنها طرح های زمان اجرا هستند.

به همین دلیل، اگر بحث در مورد قابلیت اطمینان Rust/Wasm فقط در «آیا می توان وحشت را گرفت؟» متوقف شد، به راحتی می توان مشکل را دست کم گرفت. چیزی که واقعاً شکاف هزینه تعمیر و نگهداری را افزایش می دهد این است که آیا مجموعه نمونه می تواند یک مرز اعتماد واضح را پس از شکست حفظ کند یا خیر.

مرز قابل اجرا به شدت با چرخه زندگی مرتبط است

این مجموعه از طرح های مرمت برای هر پروژه Wasm مورد نیاز نیست.

اگر ماژول فقط یک ابزار آفلاین یکبار مصرف باشد، یا وقتی صفحه از بین می‌رود، کل نمونه بازیافت شود، تفاوت بین وحشت و سقط همچنان وجود خواهد داشت، اما سود بازیابی بسیار کمتر خواهد بود. اغلب کافی است صفحه را مستقیماً به روز کنید و کار را مستقیماً مجدداً اجرا کنید.

هنگامی که سیستم دارای ویژگی های زیر باشد، معنای بازیابی به سرعت از یک “اقلام بهینه سازی” به یک “اقلام زیرساختی” تغییر می کند:

  • این نمونه برای مدت طولانی باقی می ماند و همراه با چرخه عمر یک صفحه از بین نمی رود
  • یک نمونه به طور مداوم چندین دور تماس برقرار می کند
  • لایه میزبان نیاز به استفاده از ادغام در ازای زمان راه اندازی و توان عملیاتی دارد
  • از وضعیت جلسه، وضعیت کش و وظایف در صف پس از شکست محافظت کنید

هنگامی که تیم های تلفن همراه قابلیت های بومی را به وب منتقل می کنند، این مرز بیشترین احتمال را دارد که با آن مواجه شوند. رابطه ایزوله‌ای که در ابتدا به‌طور پیش‌فرض در فرآیند App ایجاد شده بود، اغلب باید پس از رسیدن به مرز میزبان JS/Wasm دوباره پر می‌شد.

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