پرش به محتوای اصلی
OpenAI

۲۰ شهریور ۱۴۰۵

مهندسی

مقیاس‌دهی سریع ذخیره‌سازی آنلاین برای بیش از ۱ میلیارد کاربر ChatGPT

چگونه پلتفرم ذخیره‌سازی برنامه خود، Habitat، را با پایتون برای مدیریت رشدی بی‌سابقه سازگار کردیم.

نوشته جان لی، چائومین یو و بن ریس، اعضای کادر فنی

در حال بارگذاری…

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

Habitat پلتفرم ذخیره‌سازی آنلاینی است که ساختیم تا محصولات OpenAI سریع و مطمئن به اطلاعات مورد نیاز دسترسی یابند. Habitat اکنون در نزدیک به ۴۰ منطقه جغرافیایی، بیش از ۷۰ میلیون درخواست در ثانیه را برای محصولاتی پردازش می‌کند که هر هفته بیش از ۱ میلیارد نفر از آن‌ها استفاده می‌کنند. Habitat نخست برای پشتیبانی از GPTها در DevDay 2023 عرضه شد و کار خود را به‌صورت کتابخانه ساده سمت کلاینت پایتون، متصل به یک پایگاه داده، آغاز کرد. امروز، Habitat سامانه توزیع‌شده پیچیده‌ای است که بیش از ۵۰۰ پتابایت داده را ارائه می‌کند.

شکل ۰۱ · Habitat چیست؟

پلتفرم ذخیره‌سازی آنلاین

Habitat پلتفرم ذخیره‌سازی آنلاینی است که ساختیم تا محصولات OpenAI سریع و مطمئن به اطلاعات مورد نیاز دسترسی یابند.

  • درخواست
  • پاسخ
  • تغییرات (CDC)

کلاینت‌ها

پلتفرم ذخیره‌سازی آنلاین

منابع ذخیره‌سازی

  • ChatGPT
  • API
  • Codex
  • سرویس‌های داخلی
  • و موارد دیگر

Habitat

  • نهان‌سازیحافظه‌های نهان
  • سیاست‌های ACLمجوزدهی
  • جانمایی و محل اقامت دادهمحل اقامت داده
  • رمزنگاریامنیت داده
  • جداسازیچندمستاجری
  • محدودسازی نرخشکل‌دهی درخواست
  • مسیریابیجست‌وجوی الگو · محل اقامت داده
  • Azure Cosmos DBذخیره‌سازی آنلاین
  • Nanobaseذخیره‌سازی آنلاین
  • Valkeyحافظه‌های نهان
  • ذخیره‌سازی Blobمنابع ذخیره‌سازی
سرویس‌های CDCثبت تغییرات داده
  • Databricks
  • Rockset
  • Kafka
  • و موارد دیگر

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

  • بیش از ۷۰ میلیون

    درخواست در ثانیه

  • بیش از ۱ میلیارد

    نفر در هفته

  • بیش از ۵۰۰ پتابایت

    داده

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

در مطلبی آینده، به‌تفصیل از قابلیت اطمینان چندمستاجری در مقیاس بزرگ، راهبرد لایه‌ای بهینه‌سازی عملکرد خواندن و گسترش همکاری با Azure Cosmos DB برای مدیریت مطمئن تقاضایی بی‌سابقه خواهیم گفت.

Habitat چیست؟

Habitat از ایده‌ای ساده آغاز شد: مهندسان محصول نباید درگیر مدیریت پایگاه داده باشند. Habitat نخست در DevDay 2023 برای پشتیبانی از GPTها به‌صورت کتابخانه‌ای کوچک در پایتون عرضه شد که با سرور اصلی ChatGPT تعامل داشت. این کتابخانه از مجموعه کوچکی از عملیات پشتیبانی می‌کرد که در پشت صحنه به برنامه پایگاه داده، Azure Cosmos DB، نگاشت می‌شدند.

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

مهندسان محصول لازم نبود نگران جست‌وجوی الگو، مسیریابی، مجوزدهی، رمزنگاری، سریال‌سازی، شکل‌دهی درخواست و تجمیع اتصال‌ها باشند. حتی لازم نبود به منبع داده فکر کنند: Azure Cosmos DB، حافظه‌های نهان یا انواع دیگر فضای ذخیره‌سازی.

شکل ۰۲ · سرویس Habitat

جریان ساده‌شده درخواست Habitat

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

  • درخواست
  • پاسخ

کلاینت

OpenAI

Azure Cosmos DB

SDK کلاینت Habitat
envoy
  • habitat-serviceفرایند ۱
  • habitat-serviceفرایند ۲
  • habitat-serviceفرایند ۳
habitat-envoy
  • habitat-cosmos-db-us0
  • habitat-cosmos-db-us1
  • habitat-cosmos-db-eu0

این کتابخانه پایتون خوب کار کرد و Habitat به‌رغم نبود تلاشی متمرکز برای فاصله‌گرفتن از Postgres و Azure Cosmos DB سلف‌سرویس، به‌سرعت میان مهندسان محصول OpenAI فراگیر شد.

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

ساخت سرویس برای پشتیبانی بهتر از چندین محصول پیچیده

تا اواسط ۲۰۲۵، Habitat در قالب پیاده‌سازی سمت کلاینت به مرز توان خود رسیده بود. با پیچیده‌ترشدن لایه Habitat و افزایش شمار سرویس‌های OpenAI، تغییر پروتکل با حفظ سازگاری عقب‌رو دیگر عملی نبود.

در یک مورد، می‌خواستیم با انتقال حیاتی‌ترین مجموعه‌داده‌ها به چند حساب Azure Cosmos DB توزیع‌شده در مناطق مختلف، دامنه اثر قطعی هر منطقه را کاهش دهیم. این تغییر مستلزم افزودن منطق مسیریابی بیشتر به کلاینت، غیرفعال پشت فلگ قابلیت، عرضه آن برای همه کلاینت‌ها و سپس فعال‌کردن فلگ بود.

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

تغییرات کتابخانه کلاینت نیازمند هماهنگی پیچیده میان ده‌ها سرویس بود؛ فرایندی که روزبه‌روز شکننده‌تر، ناکارآمدتر و مستعد خطاهای عملیاتی می‌شد. برای کاهش پراکندگی عملیاتی استقرارهای آینده، تصمیم گرفتیم Habitat را به سرویسی مستقل تبدیل کنیم.

با جداسازی منطق ذخیره‌سازی در یک سرویس مستقل، نقطه کنترل واحدی برای استقرار، مشاهده‌پذیری و بهبودهای پلتفرم ایجاد کردیم. به‌جای مدیریت به‌روزرسانی‌های پراکنده، می‌توانستیم بهبودها را به‌صورت متمرکز اجرا کنیم تا همه محصولات OpenAI فوراً از آن بهره‌مند شوند.

یک سرویس متمرکز همچنین گلوگاه واحدی برای ارائه قوی‌ترین سازوکارهای پایه امنیت و حریم خصوصی داده فراهم می‌کند. در سرویس Habitat می‌توانیم سیاست‌های کنترل دسترسی را متمرکز اعمال کنیم، گزارش حسابرسی بگیریم و دسترسی به منابع ذخیره‌سازی زیربنایی مانند Azure Cosmos DB را محدود کنیم. Habitat در حفاظت از داده کاربران و جلوگیری از دسترسی غیرمجاز بازیگران خارجی، داخلی و عامل‌ها نقشی حیاتی دارد.

راه‌اندازی سرویس پایتون در مقیاس بزرگ

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

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

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

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

ردیابی تأخیر asyncio

Asyncio به پایتون کمک می‌کند بارهای کاری وابسته به ورودی/خروجی را هم‌زمان اجرا کند، اما محدودیت GIL پایتون را دور نمی‌زند و پردازش موازی CPU فراهم نمی‌کند. Habitat افزون بر پراکسی‌کردن درخواست‌های سنگین از نظر ورودی/خروجی، مسئولیت‌ها و وظایف پس‌زمینه سنگین بسیاری برای CPU دارد: مسیریابی، فشرده‌سازی، رمزنگاری، محاسبه جمع کنترلی، بررسی سلامت سرویس‌های پایین‌دستی، سایه‌سازی درخواست و هجینگ.

با این تعداد بار کاری سنگین برای CPU و وظیفه پس‌زمینه در سرویس، تأخیر زمان‌بندی asyncio به‌راحتی می‌تواند عامل غالب در تأخیر دنباله‌ای درخواست‌ها شود. پیش از تنظیم سرویس برای عرضه اولیه، در ردگیری درخواست‌هایی با تأخیر p99 و بالاتر دیدیم که با وجود پاسخ سریع فضای ذخیره‌سازی پایین‌دستی، درخواست‌ها اغلب در انتظار زمان‌بندی مجدد کوروتین مسئول برای تجزیه پاسخ متوقف می‌شدند.

شکل ۰۳ · ردیابی تأخیر asyncio

هم‌زمانی به معنای پردازش موازی CPU نیست

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

پردازش درخواست/پاسخ با CPUخواندن/نوشتن شبکه در پایتونانتظار برای Cosmos

پردازش سبک CPU

گام‌های کوتاه پایتون؛ انتظارهای ورودی/خروجی هم‌پوشانی دارند

پردازش سنگین CPU

گام‌های طولانی پایتون پاسخ‌های آماده را منتظر نگه می‌دارند

0.0 از ۴۰ واحد نمونه

در سرویس‌های پایتون OpenAI، افزون بر سنجش معیارهای معمول بهره‌برداری و اشباع حافظه، CPU، شبکه و دیسک، پایش حلقه asyncio و میزان مشغول‌بودن آن و سپس تنظیم متناسب با آن حیاتی است.

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

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

کاهش تأخیر دنباله‌ای در پیکربندی فلگ‌های قابلیت

هنگام عرضه اولیه سرویس، با پروفایل‌گیری زنده CPU یکی از علل ریشه‌ای تأخیر زیاد asyncio و در نتیجه تأخیرهای دنباله‌ای بالا را یافتیم: تجزیه دوره‌ای JSON پیکربندی فلگ‌های قابلیت از طریق Statsig؛ ابزاری برای مدیریت فلگ‌ها، اجرای آزمون A/B و موارد دیگر.

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

پس از آنکه پروفایل‌گیری CPU علت ریشه‌ای را آشکار کرد، راه‌حل ساده بود: استقرار پیکربندی کوچک‌تر و هدفمند، افزایش فاصله نوسازی و افزودن کمی نوسان زمانی به این‌گونه وظایف پس‌زمینه.

توازن بار و مدیریت مجموعه‌های اتصال

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

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

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

به مجموعه اتصال مشکوک شدیم و این فرض را با محدودکردن حداکثر مدت استفاده مجدد از اتصال آزمودیم؛ این کار واقعاً افت را محدود و مسیر بررسی ما را تأیید کرد. بررسی بیشتر نشان داد TCPConnector در aiohttp پایتون به‌طور پیش‌فرض از اتصال‌ها به روش LIFO استفاده مجدد می‌کند: تازه‌ترین اتصال بازگشته برای درخواست بعدی انتخاب می‌شود. این معمولاً پیش‌فرضی منطقی است: استفاده مجدد از اتصال‌های تازه باعث می‌شود اتصال‌های اضافی ایجادشده برای ترافیک جهشی پس از مهلت بیکاری بسته شوند و سربار نگهداری آن‌ها کاهش یابد. اما در این مورد، برای ما خطای فراپایدار ایجاد کرد. هنگام جهش درخواست‌ها، سرورهای کندتر و پربار اتصال‌ها را دیرتر به مجموعه بازمی‌گرداندند؛ بنابراین درخواست‌های بعدی بیشتر آن‌ها را انتخاب می‌کردند و ترافیک به‌تدریج روی پادهای مشکل‌دار متمرکز می‌شد. اصلاح مجموعه اتصال برای استفاده مجدد به روش FIFO این حلقه بازخورد را شکست و حتی پراکندگی درخواست‌ها در حالت پایدار را نیز کاهش داد.

شکل ۰۴A · تجمیع اتصال سمت کلاینت

LIFO کار جدید را دوباره به فرایند کند می‌فرستد

پس از جهش درخواست‌ها، سرورهای کندتر اتصال‌ها را دیرتر به مجموعه بازمی‌گردانند. LIFO باعث می‌شود کار بیشتری روی همان سرورهای کندتر متمرکز شود.

یک جهش اولیه به A، B و فرایند کندتر C می‌رسد.

شکل ۰۴B · تجمیع اتصال سمت کلاینت

FIFO حلقه بازخورد استفاده مجدد از اتصال را می‌شکند

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

یک جهش اولیه به A، B و فرایند کندتر C می‌رسد.

امروز در سراسر زیرساخت OpenAI عمدتاً به Istio و Envoy متکی هستیم تا تجمیع اتصال و راهبردهای بهتر توازن بار با آگاهی از بار سرور را فراهم کنند و اساساً از این مشکل جلوگیری شود.

جلوگیری از سرازیرشدن بیش‌ازحد درخواست‌ها به منابع پایین‌دستی

یکی از پیامدهای تنظیم سیستم برای کاهش تأخیر asyncio و داشتن این تعداد فرایند Python این است که حجم بسیار زیاد اتصال‌ها می‌تواند به‌راحتی وابستگی‌های پایین‌دستی را تحت فشار قرار دهد؛ پدیده‌ای که به «هجوم هم‌زمان درخواست‌ها (thundering herd)» معروف است.

یک استقرار روزانه معمولی، اگر برای اجرای آهسته تنظیم نشده باشد، می‌تواند بر اثر گردش اتصال‌ها نوسان شدیدی در CPU ایجاد کند. یا نشت اتصال می‌تواند با اشباع دروازه NAT، شبکه را از کار بیندازد. این مشکلات برای سرویس‌های دیگر هم نامعمول نیست، اما یک مرتبه بزرگی فرایند بیشتر، آستانه وقوع را به‌شدت پایین می‌آورد و اغلب منابع شبکه‌ای را اشباع می‌کند که کلاینت‌ها بر پایه توان عملیاتی صرف، انتظار ندارند در حالت پایدار مجبور به مدیریتشان باشند.

برای به‌حداکثررساندن تجمیع ورودی اتصال‌ها نیز به Envoy متکی هستیم. از آن برای ارتقای اتصال‌های HTTP/1 پایتون به HTTP/2 و بهره‌گیری از مالتی‌پلکس‌کردن استفاده می‌کنیم؛ سپس اتصال‌ها را تجمیع و عمرشان را طولانی‌تر می‌کنیم. Envoy همچنین نقطه‌ای مرکزی برای پیاده‌سازی محدودیت نرخ و قطع‌کننده‌های مدار فراهم می‌کند که در هر فرایند مستقل پایتون اثربخشی کمتری داشتند.

شکل ۰۵ · تجمیع ورودی اتصال‌ها

همان درخواست‌ها، اتصال‌های کمتر

تجمیع اتصال و مالتی‌پلکس‌کردن اتصال HTTP/2 به کاهش بار اتصال روی سرویس‌های پایین‌دستی کمک می‌کند.

درخواستپاسخاتصال پایدار بیکار

چرا Habitat کارهای کمتری انجام می‌دهد

یکی از دلایل امکان مقیاس‌دهی پایتون تا این حد، API محدود Habitat بود که هزینه درخواست را قابل پیش‌بینی نگه می‌دارد. Habitat به‌جای آنکه به کلاینت‌ها اجازه دهد پرس‌وجوهای دلخواه SQL بسازند که ممکن است به پیمایش جداول بزرگ یا اتصال چندین جدول منجر شود، یک API ساده NoSQL ارائه می‌کند. نداشتن API قدرتمند، مصالحه‌ای آگاهانه در طراحی Habitat است.

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

پیش از مهاجرت به Habitat و Azure Cosmos DB، بیشتر داده‌های آنلاین OpenAI در Postgres ذخیره می‌شد. آن زمان بررسی همه تغییرات پرس‌وجو و الگو آسان بود و می‌توانستیم پیش از استقرار عملیاتی مطمئن شویم رفتار مناسبی دارند و روی داده‌های نمایه‌شده اجرا می‌شوند. با رشد تیم و محصولات، این کار به‌سرعت مهارنشدنی شد و بارها قطعی‌هایی رخ داد که در آن یک پرس‌وجوی پرهزینه جدید در مسیر پرترافیک، پایگاه داده را از کار انداخت.

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

Habitat یک API از نوع NoSQL ارائه می‌کند که با الهام از TAO(در یک پنجره جدید باز می‌شود)، حول انواع شیء و یال تعریف‌شده توسط کلاینت طراحی شده است. کلاینت‌ها اشیا، یال‌ها و رابطه میان آن‌ها را از پیش تعریف می‌کنند، اما محتوای هر نوع را نه. روابط حاصل شبیه گراف‌اند، اما خود Habitat جز پرس‌وجوی یال‌های مستقیم یک شیء مشخص، از پرس‌وجوهای متداول پیمایش گراف پشتیبانی نمی‌کند.

این گراف را طوری پارتیشن‌بندی می‌کنیم که هر شیء و یال‌های متناظر آن در یک پارتیشن ذخیره‌سازی کنار هم باشند، اما در سطح پایگاه داده تلاش ویژه‌ای برای هم‌مکانی اشیا با اشیای دوردستی که یال‌هایشان به آن‌ها اشاره می‌کند نداریم. در نتیجه، مدل برای مقیاس‌پذیری افقی به‌آسانی پارتیشن‌بندی می‌شود، اما پیمایش گراف ناکارآمد است؛ زیرا هر گام میان اشیا ممکن است به دریافت داده از دو حساب کاملاً متفاوت Azure Cosmos DB در مناطق مختلف نیاز داشته باشد.

برای کلاینت‌هایی با نیازهای پیچیده‌تر پرس‌وجو، نمای ثانویه آفلاینی از Habitat نیز از طریق Rockset ارائه می‌کنیم. از ثبت تغییرات داده (CDC) استفاده می‌کنیم تا تغییرات فضای ذخیره‌سازی آنلاین را تقریباً بلادرنگ به نمونه‌های مجزای Rockset جریان دهیم. هر تیم کلاینت مسئول مقیاس‌دهی نمونه Rockset خود برای نیازهای پیچیده پرس‌وجویش است.

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

مهاجرت از پایتون به Rust

یک سال به تعویق انداختن بازنویسی پایتون به ما اجازه داد هنگام رشد فوق‌سریع، بر چالش‌های فوری‌تر و اثرگذارتر تمرکز کنیم. با بلوغ پلتفرم و شتاب‌گرفتن رشد، و درحالی‌که از نظر تعداد هسته دومین سرویس بزرگ OpenAI بودیم و از نظر گستره Envoy رتبه چهارم را داشتیم، سرانجام زمان عبور از پایتون فرا رسید. پایتون در اوج خود به ما کمک کرد بیش از ۲۰ میلیون درخواست را در هر ثانیه پاسخ دهیم.

در سه‌ماهه دوم ۲۰۲۶، تنها با ۲ مهندس، Codex و GPT‑5.5 توانستیم کل سرویس را با Rust بازنویسی کنیم. سرویس جدید Rust اکنون ۹۵٪ درخواست‌های عملیاتی ما را پردازش می‌کند و در هفته‌های آینده پایتون را کاملاً کنار می‌گذاریم. داده‌های ما نشان می‌دهد سرویس Rust از نظر CPU شش برابر و از نظر حافظه پانزده برابر کارآمدتر از نسخه پایتون است و میانگین تأخیر و تأخیر دنباله‌ای بسیار کمتری دارد. قصد داریم در مطلبی آینده آموخته‌های بیشتری به اشتراک بگذاریم.

بهینه‌سازی لایه پایگاه داده ما، Azure Cosmos DB

سرویس پایتون ــ و اکنون Rust ــ تنها یکی از ابعاد Habitat است. در بخش دوم این مجموعه درباره مقیاس‌دهی سریع فضای ذخیره‌سازی آنلاین برای بیش از ۱ میلیارد کاربر ChatGPT، از لایه ذخیره‌سازی و نحوه مدیریت بیش از ۵۰۰ پتابایت داده و ۷۰ میلیون درخواست در ثانیه توسط Habitat خواهیم گفت.

اگر می‌خواهید روی سیستم‌های OLTP در مقیاس پیشرو کار کنید و به این نوع مهندسی علاقه دارید، این موقعیت شغلی باز در تیم ما را ببینید.

نویسنده‌ها

Jon Lee،‏ Chaomin Yu،‏ Ben Ries