OpenAI

۱ آبان ۱۴۰۵

مهندسی

گسترش سریع ذخیره‌سازی آنلاین برای بیش از ۱ میلیارد کاربر ChatGPT (بخش دوم)

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

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

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

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

این مجموعهٔ دو‌بخشی توضیح می‌دهد چگونه Habitat را از یک کتابخانهٔ کلاینت و پایگاه دادهٔ ساده به پلتفرم ذخیره‌سازی توزیع‌شده در سراسر جهان تبدیل کردیم که بیش از ۵۰۰ پتابایت داده را در اختیار سرویس‌ها می‌گذارد. بخش اول به تکامل Habitat از یک کلاینت پایتون به یک سرویس پرداخت. همچنین روش‌هایی را شرح داد که به ما اجازه داد سرویس مبتنی بر پایتون را بسیار فراتر از آنچه بسیاری عملی می‌دانند، گسترش دهیم.

در این نوشته، یعنی بخش دوم، بررسی می‌کنیم که چگونه مقیاس مؤلفهٔ ذخیره‌سازی زیربنایی، Azure Cosmos DB، را افزایش دادیم. با همکاری نزدیک تیم Azure، به رویکردی عملی برای چندمستأجری با کانتینرهای تحت مدیریت مشترک رسیدیم که عملکرد خواندن بسیار خوبی در مقیاس عظیم جهانی دارد.

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

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

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

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

کلاینت‌ها

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

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

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

Habitat

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

قابل‌اعتمادکردن معماری چندمستأجری در مقیاس بزرگ

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

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

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

آشنایی ساده با کانتینرهای Azure Cosmos DB

پیش از شرح مسیرمان با Azure Cosmos DB، چند نکتهٔ پایه:

  • هر حساب Azure Cosmos DB مجموعه‌ای از کانتینرها و یک سیاست تکثیر را تعریف می‌کند

  • برای هر کانتینر، مقدار مشخصی توان عملیاتی بر حسب واحد درخواست در ثانیه (RU/s) تأمین می‌شود

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

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

مرحلهٔ اول: تکیه بر قابلیت‌های پایهٔ Azure Cosmos DB

در ابتدا هر میزان بار کلاینت را می‌پذیرفتیم و برای ایجاد فشار برگشتی، به سقف اعلام‌شدهٔ ۱۰ هزار RU/s برای هر کلید پارتیشن منطقی تکیه داشتیم. همچنین در آغاز، در هر حساب فقط از یک کانتینر پیش‌فرض Azure Cosmos DB استفاده می‌کردیم. این روش تا حد قابل‌قبولی کار می‌کرد، اما به چند دلیل مشکل‌ساز شد:

محدودیت‌های کلید پارتیشن منطقی

به‌دلیل تصمیم‌های اولیه در طراحی API، انتخاب کلید پارتیشن منطقی را مستقیماً در اختیار کلاینت‌ها قرار دادیم. در نتیجه، میان کاربردهای مختلف محصولات، تداخل‌های مکرری روی کلیدهای رایج پارتیشن، مانند user_id یا workspace_id، داریم. چون این کاربردها مشمول محدودیت‌های مشترک Azure Cosmos DB روی همان کلیدهای پارتیشن منطقی بودند، این طراحی کل توان عملیاتی هر کاربر را محدود می‌کرد.

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

Figure 02a · Logical partition key limits

Different clients, the same partition key

Cosmos DB containerPhysical partitionPK: user-id-28104CapacityMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedMemory / dreaming : capacity usedConversationsSuccess rateResponses returned in this animation loop—AuthSuccess rateResponses returned in this animation loop—Memory / dreamingSuccess rateResponses returned in this animation loop—

همسایه‌های مزاحم

هرچند محدودیت‌های مستند حجم داده و توان عملیاتی برای کلیدهای پارتیشن منطقی تعریف شده‌اند، در عمل این محدودیت‌ها در پارتیشن فیزیکی اعمال می‌شوند. در عمل، یعنی پارتیشن‌های منطقی نمی‌توانند به ۱۰٬۰۰۰ RU/s برسند. در عوض، سقف ظرفیت پارتیشن فیزیکی میان آن‌ها و همهٔ کلیدهای پارتیشن منطقی دیگری که روی همان پارتیشن فیزیکی قرار دارند مشترک است.

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

Figure 02b · Noisy neighbors

Different users, the same physical partition

Cosmos DB containerPhysical partitionPKs: user-id-28104, user-id-10384Conversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversations user-id-28104: capacity usedConversationsuser-id-28104Success rateResponses returned in this animation loop—Conversationsuser-id-10384Success rateResponses returned in this animation loop—

پارتیشن‌های ضعیف

سقف توان عملیاتی هر پارتیشن فیزیکی نیز متغیر است و به کل توان عملیاتی تخصیص‌یافته به کانتینر Azure Cosmos DB بستگی دارد. توان عملیاتی تأمین‌شده برای کانتینر تقریباً به‌طور مساوی بین پارتیشن‌های فیزیکی آن توزیع می‌شود.

با افزایش حجم داده، پارتیشن‌های فیزیکی تقسیم می‌شوند و تعداد کل پارتیشن‌ها افزایش می‌یابد، اما کل توان عملیاتی تأمین‌شده ثابت می‌ماند. در نتیجه، با افزایش حجم داده، سقف مؤثر توان عملیاتی هر پارتیشن کاهش می‌یابد.

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

Figure 02C · Weak partitions

The limit falls below demand

A partition splits when it reaches its storage limit. Each child receives half the throughput capacity.

Before split: 10K RU/s capacity

After split: 5K RU/s capacity × 2 partitions

RU/s05K6K10K200200429

مرحلهٔ دوم: آزمودن جداسازی بارهای کاری و معماری تک‌مستأجری

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

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

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

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

نبود رشد پایدار در مصرف تجمیعی

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

با کانتینرهای اختصاصی، تیم‌ها باید به خاطر می‌سپردند که پیش از عرضه، توان عملیاتی کانتینرها را به میزان موردانتظار برسانند. تیم‌های محصول اغلب قابلیت‌هایشان را منتشر می‌کردند و افزایش ظرفیت پیش از عرضه را از یاد می‌بردند؛ ما هم روز عرضه در اتاق‌های مدیریت رخداد بحرانی (SEV) می‌نشستیم و در آخرین لحظه برای افزایش ظرفیت کانتینرها تلاش می‌کردیم.

این یکی از مزایای اصلی ترکیب بارهای کاری در سامانه‌های چندمستأجری است که با این راهبرد کانتینری، در ازای جداسازی بهتر از دست می‌دهیم.

Figure 03 · Lack of stable aggregate growth

The same workload, different headroom

1K RU/s during load testing → 1M RU/s at launch

Shared container

100M RU/s capacity · 80% utilized

Load test

1K20M=

0.005%

of headroom

Launch

1M20M=

5%

of headroom

New dedicated container

10K RU/s capacity · no other workloads

Load test

1K10K=

10%

of headroom

Launch

1M10K=

10,000%

of headroom

برنامه‌ریزی ظرفیت

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

می‌توانیم اسکریپتی ساده در لایهٔ کنترل ارائه دهیم که توان عملیاتی تأمین‌شدهٔ هر کانتینر را بر اساس روند رشد افزایش دهد، اما بدون رشد پایدار تجمیعی، تنظیم درست مقیاس عملاً مستلزم مشارکت تمام تیم‌های محصول OpenAI در برنامه‌ریزی ظرفیت بود.

این رویکرد از نظر سازمانی مقیاس‌پذیر نبود؛ میزان دقت تیم مسئول هر سرویس در تأمین منابع Azure Cosmos DB خود بسیار متفاوت بود.

مرحلهٔ سوم: چندمستأجری به‌صورت پیش‌فرض، با امکان جابه‌جایی داده

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

محدودسازی نرخ کلیدهای پرترافیک

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

کیفیت سرویس (QoS)

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

حالت بدون سرور

Azure Cosmos DB از قبل حالت بدون سروری داشت که می‌توانست مشکل پارتیشن‌های ضعیف ما در حالت ظرفیت ازپیش‌تأمین‌شده را حل کند. اما یک عیب بزرگ داشت: فقط بر اساس مصرف فضای ذخیره‌سازی مقیاس می‌گرفت و مصرف RU را به ۵ هزار RU/s برای هر پارتیشن فیزیکی محدود می‌کرد. این روش مشکل پارتیشن‌های ضعیف را حل می‌کرد، اما مانع افزایش مقیاس کاربردهای پرمصرف از نظر RU می‌شد.

تیم Azure Cosmos DB حالت بدون سرور بهبودیافته‌ای برای ما ساخت که ظرفیت هر پارتیشن فیزیکی را تا ۱۰ هزار RU/s افزایش می‌داد و علاوه بر تقسیم بر اساس مصرف ذخیره‌سازی، تقسیم بر اساس مصرف RU را نیز ممکن می‌کرد. به‌این‌ترتیب دیگر نیازی به پایش و افزایش دستی ظرفیت کانتینرها نداشتیم.

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

قابلیت اطمینان بهتر با مدیریت مشترک سرویس‌ها

وقتی شرکت‌ها به مقیاس OpenAI می‌رسند، سرویس‌های مدیریت‌شده دیگر چندان «مدیریت‌شده» نیستند. بسیاری از بارهای کاری در زیرساخت ارائه‌دهندهٔ ابری به مواردی خارج از الگوی معمول تبدیل می‌شوند. با تبدیل‌شدن OpenAI به یکی از بزرگ‌ترین کاربران Azure Cosmos DB، بهره‌برداری موفق از آن به شیوه‌ای متفاوت نیاز داشت: آزمون‌های مشترک، داشبوردهای مشترک، کانال‌های مستقیم مهندسی و مسئولیت مشترک در قبال انواع خرابی که فقط در مقیاس ما رخ می‌دهند. امروز بزرگ‌ترین حساب Azure Cosmos DB ما ده‌ها پتابایت داده ذخیره می‌کند و به بیش از ۲۰ میلیون QPS پاسخ می‌دهد. تا جایی که می‌دانیم، تنها شرکتی هستیم که Azure Cosmos DB را در این مقیاس مستقر کرده است.

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

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

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

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

در سال گذشته همکاری گسترده‌ای برای بهبود مقیاس‌پذیری لایهٔ کنترل و درگاه Azure Cosmos DB انجام شد: افزودن حفاظت در برابر بار اضافی و کش‌کردن فراداده‌های مختلف Azure Cosmos DB در درگاه آن برای محافظت از پارتیشن فراداده؛ کاهش فشار ناشی از مجموعهٔ فرایندهای پایتونی habitat-service با تجمیع اتصال‌ها و استفادهٔ مجدد بیشتر از آن‌ها؛ هم‌مکان‌کردن بارهای کاری در صورت امکان؛ و جداسازی بارهای کاری حیاتی از بارهای کاری آفلاین با استفاده از VPCهای مجزا.

این سطح از همکاری مهندسی برای پایداری Azure Cosmos DB ما در سال گذشته حیاتی بوده است. هر سامانه‌ای که به‌اندازهٔ کافی بزرگ یا حیاتی باشد به چنین کار تیمی نیاز دارد: کنارزدن مرزبندی‌های مسئولیت و همکاری فراتر از مرزهای سازمانی.

بهبود عملکرد در مقیاس جهانی

همان‌طور که در بخش اول گفتیم، فلسفهٔ اصلی ما این است: اول کاری کنید که کار کند، بعد سریعش کنید و سپس کم‌هزینه‌اش کنید. اول قابلیت اطمینان، سپس عملکرد و بعد بهره‌وری. Habitat به زیرساختی حیاتی برای OpenAI تبدیل شده بود، بنابراین اولویت نخست، قابل‌اعتمادکردن آن بود. با پایدارشدن سامانه در سال ۲۰۲۵ و گسترش جهانی OpenAI، تأخیر به محدودیت بعدی برای کاربران و تیم‌های محصول تبدیل شد.

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

Figure 04 · Improving performance at a global scale

Global traffic

Compute and storage capacity were often located in different regions, making cross-region requests unavoidable.

ComputeStorageIllustrative routes · Longer journeys take more time

مکان‌های جغرافیایی پردازش و ذخیره‌سازی صرفاً برای نمایش مفهوم هستند.

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

این موضوع در سال ۲۰۲۶ از محورهای اصلی کار ما بوده و برای بهینه‌سازی عملکرد خواندن، که بیش از ۹۰٪ درخواست‌های Habitat را تشکیل می‌دهد، راهبردی لایه‌ای تدوین کرده‌ایم:

ذخیرهٔ داده نزدیک کاربران، هرجا ظرفیت اجازه دهد

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

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

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

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

تکثیر گسترده فقط برای کوچک‌ترین و حساس‌ترین مجموعه‌داده‌ها به تأخیر

برای حساس‌ترین بارهای کاری به تأخیر، مانند فرادادهٔ احراز هویت که در هر درخواست لازم است، با استفاده از حساب‌های Azure Cosmos DB دارای نسخه‌های متعدد در مناطق ثانویهٔ فراوان، زمان رفت‌وبرگشت (RTT) بین بخش پردازش و Habitat را کاهش می‌دهیم. برخی حساب‌ها در هر ۴۰ منطقهٔ Azure که در آن‌ها به ترافیک پاسخ می‌دهیم تکثیر می‌شوند؛ بقیه در دست‌کم یک منطقه از هر قاره.

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

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

تکیه بر کش یکپارچه برای سایر موارد

بارهای کاری Habitat عمدتاً خواندن‌محورند و برنامه‌ها پیوسته تنظیمات، وضعیت و سایر ویژگی‌ها را واکشی می‌کنند؛ سهم بزرگی از ترافیک به مجموعهٔ فعال نسبتاً کوچکی از انواع دادهٔ پرمراجعه مربوط است. این ویژگی، سامانه را برای کش توزیع‌شده بسیار مناسب می‌کند. کش در زیرساخت تک‌منطقه‌ای عملکرد را بهتر می‌کند، اما در محیط چندمنطقه‌ای مزایای آن بیشتر هم می‌شود، چون می‌تواند تأخیر بین‌منطقه‌ای را «پنهان» کند.

یک لایهٔ کش جانبی توزیع‌شده را در Habitat یکپارچه کرده‌ایم. این لایه نسخه‌ای از اشیا و یال‌های اخیراً خوانده‌شده را در سرورهای Valkey ذخیره می‌کند که در زیرساخت ما، نزدیک محل پردازش درخواست کاربران قرار دارند. معمولاً این کش‌ها از زمان انقضا (TTL) برای برقراری توازن میان نرخ اصابت به کش و کهنگی داده استفاده می‌کنند. TTL کوتاه کهنگی داده را کاهش می‌دهد، اما اثر مثبت کش را هم کم می‌کند، چون داده‌ها زود منقضی می‌شوند و باید بیشتر واکشی شوند. TTL بلندتر نرخ اصابت را بهبود می‌دهد، اما دادهٔ کهنه را مدت بیشتری ارائه می‌کند و به تجربهٔ کاربری ضعیف و رفتار پیش‌بینی‌ناپذیر محصول می‌انجامد.

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

ما با الگوبرداری از سامانه‌هایی مانند TAO متعلق به Meta، رویکرد پیشرفته‌تری برای ابطال کش در پیش گرفته‌ایم. کش یکپارچهٔ Habitat به ثبت تغییرات داده (CDC) از طریق خوراک تغییرات Azure Cosmos DB متکی است تا ابطال‌ها را ظرف چند ثانیه در سراسر جهان منتشر کند و ضمن حفظ کهنگی اندک، نرخ اصابت بالایی داشته باشد. سرویسی در زیرساخت ما این خوراک تغییرات را مصرف می‌کند و با به‌روزرسانی دادهٔ زیربنایی، کش‌های محلی را باطل می‌کند. به‌این‌ترتیب، خواندن بعدی به عدم اصابت به کش می‌انجامد و کش با تازه‌ترین نسخهٔ داده پر می‌شود.

کاهش سربار پراکسی با Rust

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

این تغییرات در مجموع تأخیر p50 خواندن در سراسر جهان را از حدود ۱۲۰ به حدود ۱۰ میلی‌ثانیه کاهش داده‌اند؛ بهبودی ۱۲ برابری از آغاز تمرکزمان بر عملکرد در اوایل امسال.

Figure 05 · Improving performance at a global scale

p50 latency for reads

Three major design changes improved read latency

roughly 120 ms → roughly 10 ms

12x improvement

Milliseconds020406080100120140160زمان123
  1. critical data replicated into nearby geos
  2. in-region caching launched
  3. rust rewrite launched

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

افزایش مقیاس ثبت تغییرات داده

CDC قابل‌اعتماد و مقیاس‌پذیر—یعنی انتشار تغییرات پایگاه داده به سامانه‌های پایین‌دستی—فقط برای راهبرد کش Habitat حیاتی نیست. میلیون‌ها تغییر ثبت‌شده در ثانیه، از هزاران کانتینر و میلیون‌ها پارتیشن آن‌ها، از بیش از صد تاپیک Kafka عبور می‌کنند و نمایه‌های ثانویه با سازگاری نهایی، خط‌لوله‌های جابه‌جایی داده، تحلیل‌های آفلاین و سایر قابلیت‌های بلادرنگ محصول را نیز تغذیه می‌کنند. با گسترش جهانی زیرساخت زیربنایی ما، دنبال‌کردن تغییرات ردیف‌ها پیچیده‌تر شده است. اگر کلاینتی بخواهد تابع بازخوانی‌ای ثبت کند که با هر تغییر در ردیف‌های نوع دادهٔ او اجرا شود، ابتدا باید بداند کدام حساب‌ها و کانتینرهای Cosmos حاوی داده‌هایش هستند، کدام استقرار سرویس CDC در مناطق مربوط تغییرات آن کانتینرها را دنبال می‌کند، و باید مقیاس مجموعهٔ پردازشگرهای CDC، Kafka و مصرف‌کنندهٔ خود را درست تنظیم کند. هدف پلتفرم CDC ما پنهان‌کردن همهٔ این جزئیات از توسعه‌دهندگان محصول و ارائهٔ رابطی ساده است: کلاینت‌ها تابع دلخواهشان را برای ردیف‌های تغییریافته ثبت می‌کنند و ما تمام زیرساخت ارتباطی و مقیاس‌دهی را مدیریت می‌کنیم.

لایهٔ کنترل داخلی پیوسته اطمینان می‌دهد تغییرات تمام کانتینرهای لازم ثبت می‌شوند، مناطق Cosmos برای خواندن را انتخاب می‌کند و کار را به‌طور کارآمد میان هزاران پردازشگر در چند منطقهٔ جغرافیایی توزیع می‌کند. استفاده از مناطق دارای نسخهٔ تکراری و خواندن خوراک با اولویت پایین‌تر، ترافیک حساس به تأخیر Habitat را از اثر بارهای پس‌زمینهٔ ثبت تغییرات حفظ می‌کند. در مجموعه‌ای به این بزرگی، ازکارافتادن پردازشگرها، مشکلات کانتینر، خطاهای مخزن اجاره، پیکربندی نامعتبر و خرابی مقصد پیوسته رخ می‌دهند. چرخهٔ عمر پردازشگرهای CDC و مدیریت اجاره را مقاوم‌تر کردیم، خرابی جریان‌ها را از هم جدا کردیم و برای تغییرات پیکربندی و انتشار نسخه‌ها سازوکارهای حفاظتی ساختیم. از صف پیام‌های تحویل‌ناپذیر استفاده می‌کنیم تا شکست دائمی تحویل، ابتدای صف را مسدود نکند. با انتقال خواننده‌ها به مناطق جایگزین Cosmos، قطعی‌های منطقه‌ای را تاب می‌آوریم. در مقیاس ما، حتی مجبور شدیم SDK مربوط به CDC در Azure را تغییر دهیم تا ثبت نقاط بازیابی کارآمدتر شود: آن‌ها را در حافظه دسته‌بندی می‌کنیم و فقط دوره‌ای روی ذخیره‌ساز می‌نویسیم.

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

نگاهی به آینده

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

برای افزایش مقیاس Habitat و ذخیره‌سازی گفتگوها جهت خدمت‌رسانی به یک میلیارد کاربر بعدی، باید دشواری تکثیر داده در سراسر زیرساختمان را در مقیاسی باز هم بزرگ‌تر حل کنیم. برای پیش‌دستی بر این چالش، در حال ساخت سامانهٔ اختصاصی ذخیره‌سازی آنلاین درون‌سازمانی در مقیاس اگزابایت هستیم که برای انواع دادهٔ حجیم Habitat بهینه شده است. این سامانهٔ در حال توسعه، هستهٔ RocksDB Cloud متعلق به Rockset را با تکثیر مبتنی بر Raft ترکیب می‌کند. این سامانه همان مدل سند Azure Cosmos DB را پیاده می‌کند، اما با ذخیره‌سازی تفکیک‌شده و تکثیر فیزیکی، ظرفیت فلش موردنیاز را در ازای کاهش اوج نرخ پرس‌وجو در ثانیه کم می‌کند.

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

نویسنده

OpenAI