ماجرای کُندی Restore بسته‌های NuGet چی بود؟ الگوریتم چجوری اصلاح شد؟

شاید شما هم امروز توی وبلاگ دات‌نت یا پُست‌های شبکه‌های اجتماعی، تیتر «Dramatically faster package restores with .NET 9’s new NuGet resolver» رو دیده باشید، راستش پُست خیلی واضح نبود، من کمی به issueهای مرتبطش توی گیت‌هاب سرک کشیدم و تا دقیق‌تر داستان رو متوجه شم. این مطلب در راستای همین موضوعه!

مایکروسافت توی NET 9. و با 6.12 NuGet عملکرد فرایند Restore بسته‌ها رو بهبود داد و مدعی افزایش سرعت ۱۵ برابری شد. این پیشرفت، نتیجه بازطراحی کامل الگوریتم مدیریت وابستگی‌ها توی NuGet بود، که بعد از سال‌ها ناکارآمدی توی پروژه‌های بزرگ به یکی از گلوگاه‌های اصلی توسعه تبدیل شده بود. حالا این مطلب، بررسی دقیق مشکلات اولیه، چگونگی شناسایی و حلشون، و تاثیر این تغییراته…


چرا سرعت Restore مهمه؟

توی اکوسیستم دات‌نت ‎‎NuGet وظیفه مدیریت‌ وابستگی‌ها رو به عهده داره. هر بار که روی یه پروژه‌ی دات‌نتی کار می‌کنیم، موقع توسعه، اجرا یا تست، دستور dotnet restore به‌طور خودکار اجرا می‌شه تا وابستگی‌ها رو بررسی و در صورت نیاز دانلود کنه. این فرآیند علاوه بر دانلود کتابخانه‌ها، شامل بررسی سازگاری نسخه‌ها و شناسایی آسیب‌پذیری‌های امنیتی هم می‌شه؛ دقت کنید که یک بسته خودش می‌تونه وابسته به بسته‌های دیگه‌ای باشه و اون بسته‌ها خودشون وابسته به بسته‌های دیگه‌ای و این روند می‌تونه خیلی تو در تو بشه…

توی پروژه‌های کوچیک، این عملیات سریع و بدون مشکل انجام می‌شه. اما وقتی پای پروژه‌های بزرگ و پیچیده با هزاران وابستگی به میون میاد، عملکرد این فرآیند به شدت کند می‌شه. توی برخی پروژه‌های داخلی مایکروسافت، زمان Restore به بیش از ۳۰ دقیقه می‌رسیده!! (من ردی از این پروژه‌ها پیدا نکردم ولی شاید بشه به Dynamics 365 یا شیرپوینت یا اکسچینج فکر کرد) این موضوع باعث اتلاف وقت و کاهش بهره‌وری توسعه‌دهنده‌ها می‌شده و نیاز به بازنگری توی این فرآیند رو ضروری می‌کرده.


مشکل از کجا شروع شده؟

مایکروسافت توی فرآیند مهاجرت هزاران پروژه از NET Framework. به NET Core.، با مشکلات عمده‌ای توی عملکرد NuGet روبه‌رو شد. تیم توسعه‌دهنده‌ها متوجه می‌شن دلیل این معطلی‌ها و طولانی شدن Restore ها از ناکارآمدی الگوریتم مدیریت وابستگی‌ها بوده.

الگوریتم قبلی:
الگوریتم قدیمی برای هر وابستگی، یک گراف بزرگ شامل چندمیلیون‌ نود ایجاد می‌کرد. گرافی که نه تنها خیلی پیچیده بود، بلکه برای حل هر تعارض یا بررسی هر وابستگی، بارها از روی گراف عبور می‌کرد!! به عنوان مثال، توی پروژه‌ای با بیش از ۲۵۰۰ ماژول، این گراف شامل ۱.۶ میلیون نود می‌شده و زمان Restore به حدود ۱۵ دقیقه می‌رسیده!


راه‌حل: بازطراحی الگوریتم

مهندس‌های مایکروسافت تصمیم می‌گیرن تا الگوریتم مدیریت وابستگی‌ها رو از پایه دوباره طراحی کنن. هدف این بود که:

  1. گراف وابستگی‌ها کوچیک‌تر و ساده‌تر بشه.
  2. تعداد عبورهای مکرر از گراف کم بشه.
  3. عملکرد کلی بهبود پیدا کنه، بدون تغییر توی نتایج نهایی.

ویژگی‌های الگوریتم جدید:

  • کاهش تعداد نودها: توی پروژه‌ای مشابه، گراف وابستگی از ۱.۶ میلیون نود به ۱,۲۰۰ نود کاهش پیدا می‌کنه.
  • حل تعارض در زمان ساخت گراف: به جای عبورهای مکرر، تعارض‌ها همزمان با ساخت گراف حل می‌شن.
  • صرفه‌جویی در منابع سیستم: روش جدید باعث کاهش مصرف حافظه و پردازش می‌شه.

نتیجه: زمان Restore توی همون پروژه‌ی بزرگ از ۱۵ دقیقه به ۲ دقیقه کاهش پیدا می‌کنه. این تغییر برای پروژه‌های بزرگ یک نقطه عطف محسوب می‌شه.


چالش‌ها و مشکلات جانبی

هر تغییر بزرگی با چالش‌های خاص خودش همراهه. الگوریتم جدید هم توی برخی موارد باعث بروز مشکلاتی شد:

  • دانلود بسته‌های اضافی: توی بعضی سناریوها، نسخه‌های قدیمی یک بسته که دیگه استفاده نمی‌شن هم دانلود می‌شن! برای مثال، بسته jQuery.Validation به نسخه‌ای از jQuery وابسته بوده که دیگه توی مخزن موجود نبوده، و فرآیند Restore با خطای “verify the package exists” متوقف می‌شده.
  • راه‌حل موقت: برای رفع این مشکلات، مایکروسافت گزینه‌ای ارائه داده که توسعه‌دهنده‌ها بتونن با تنظیم RestoreUseLegacyDependencyResolver=true به الگوریتم قدیمی برگردن. البته این کار با هزینه کاهش سرعت همراه می‌شه.

نکات فنی و ابزارهای بررسی

مایکروسافت ابزارهایی برای بررسی تفاوت‌های بین الگوریتم جدید و قدیمی ارائه کرده و می‌تونیم با مقایسه فایل‌های project.assets.json در دو حالت، تفاوت‌ها را ببینیم. به این ترتیب، می‌شه تشخیص داد که که آیا مشکلات، ناشی از الگوریتم جدیده یا نه.


آینده NuGet و NET.

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


نتیجه‌گیری

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

از طرفی برای یه عده سواله چرا شرکت‌هایی مثل گوگل و مایکروسافت و… توی آزمون مصاحبه‌هاشون مسایل الگوریتم و ساختمان‌داده و… رو اینقدر مهم می‌دونن. درسته که خیلی شرکت‌ها ادایی‌طور اینا رو به فرایندشون اضافه می‌کنند و ته تهش CRUD دارن، ولی شرکت‌های زیادی حتی بسیار بسیار کوچک‌تر از غول‌های بزرگ، به صورت روزانه با چنین مسایلی درگیر هستن. و خیلی مهمه که نگاه مهندسی و اصولی به مسایل داشته باشیم…


منابع:

دیدگاهتان را بنویسید