راهنمای جامع Ceph
Englishشروع کنید

فهرست شکل‌ها

همه‌ی شکل‌های کتاب به ترتیب فصل. روی هر شکل در متن کلیک کنید تا بزرگ شود.

۱-۱لایه‌های ‏Ceph‏: سه رابط بلوک، شیء و فایل همگی روی یک لایه‌ی مشترک ‏RADOS‏ ساخته شده‌اند
۱-۲بازسازی چند به یک در ‏RAID‏ در برابر بازیابی چند به چند در ‏Ceph‏: در ‏Ceph‏ همه‌ی ‏OSD‏های سالم در ترمیم مشارکت می‌کنند
۱-۳نقش ‏daemon‏ها: کلاینت نقشه‌ها را از ‏MON‏ها می‌گیرد و سپس مستقیم با ‏OSD‏ها کار می‌کند
۱-۴مسیر جای‌گذاری: نام شیء به یک ‏PG‏ نگاشت می‌شود و ‏CRUSH‏ آن ‏PG‏ را به مجموعه‌ای از ‏OSD‏ها در دامنه‌های خرابی متفاوت می‌برد
۱-۵مسیر تصمیم‌گیری در برنامه‌ریزی: نوع بار، دامنه‌ی خرابی، شبکه و ظرفیت
۱-۶توپولوژی آزمایشگاه: سه ‏node‏ برای شروع، ‏node‏ چهارم برای گسترش، و یک ماشین کلاینت
۱-۷مراحل ‏bootstrap‏ و افزودن میزبان‌ها با ‏cephadm‏
۱-۸سه لایه‌ی مسیر یک شیء. در لایه‌ی منطقی، نام شیء با تابع ‏hash‏ به نام ‏rjenkins‏ به یک عدد ۳۲ بیتی تبدیل می‌شود (‏hobject_t‏ این ‏hash‏ را همراه ‏namespace‏ و ‏snap‏ نگه می‌دارد) و ‏ceph_stable_mod‏ آن را به یک ‏PG‏ از ‏pool‏ نگاشت می‌کند. در لایه‌ی نگاشت، ‏CRUSH‏ با ‏seed‏ جای‌گذاری (‏pps‏) و ‏rule‏ آن ‏pool‏ فهرست ‏OSD‏ها را می‌سازد، سپس استثناهای ثبت‌شده در ‏OSDMap‏ یعنی ‏upmap‏ و ‏pg_temp/primary_temp‏ روی آن اعمال می‌شوند و ‏up set‏ و ‏acting set‏ به دست می‌آیند. نخستین عضو ‏acting‏، ‏primary‏ است. در لایه‌ی فیزیکی، هر ‏PG‏ روی هر ‏OSD‏ یک ‏collection‏ در ‏BlueStore‏ است (مثلاً ۳.۱‏a_head‏). فراداده‌ی هر شیء یک ‏onode‏ در ‏RocksDB‏ است و داده از طریق زنجیره‌ی ‏extent‏، ‏blob‏ و ‏pextent‏ به بازه‌های خام روی دستگاه بلوکی اشاره می‌کند
۱-۹پیشوندهای کلید در ‏RocksDB‏ داخل ‏BlueStore‏ که در ‏BlueStore.cc‏ تعریف شده‌اند. ‏S‏ برای ‏superblock‏، ‏T‏ برای آمار، ‏C‏ برای ‏collection‏ها، ‏O‏ برای ‏onode‏ها، ‏M‏ برای ‏omap‏، ‏L‏ برای تراکنش‌های ‏deferred‏، ‏B‏ برای ‏freelist‏ و ‏X‏ برای ‏shared blob‏ها است. ردیف پایین فیلدهای اصلی ‏pg_log_entry_t‏ را نشان می‌دهد که در ‏omap‏ شیء ویژه‌ی ‏pgmeta‏ هر ‏PG‏ ذخیره می‌شود. این فیلدها عبارت‌اند از عملیات، شناسه‌ی شیء، نسخه و نسخه‌ی قبلی، ‏reqid‏ برای تشخیص درخواست تکراری پس از ‏reconnect‏ کلاینت، و ‏mod_desc‏ که در ‏EC‏ اجازه‌ی ‏rollback‏ می‌دهد
۱-۱۰نتیجه‌ی محاسبه‌ی دقیق روی نگاشت واقعی ‏CRUSH‏ برای ۱۲۰ ‏OSD‏ روی ۱۰ میزبان و ‏pool‏ سه‌نسخه‌ای (‏crushtool‏ و ‏osdmaptool‏، ‏‏Ceph 18.2.7‏). چپ: احتمال اینکه سه خرابی هم‌زمان دست‌کم یک ‏PG‏ را کاملاً از بین ببرند، برحسب ‏pg_num‏، همراه با کران بالای ‏pg_num/C‏(۱۲۰,۳). راست: تعداد ‏OSD‏های هم‌‏PG‏ با یک ‏OSD‏ (میانگین و بازه) و تعداد ‏PG‏هایی که پس از ‏out‏ شدن ‏osd.0‏ روی پرکارترین ‏OSD‏ مقصد می‌نشینند
۱-۱۱زمان بازگرداندن افزونگی پس از خرابی یک دیسک پر ۱۶ ترابایتی. قرمز ‏RAID‏ با یک دیسک یدک است و سبز ‏Ceph‏، که در آن تعداد ‏PG‏های دیسک خراب و تعداد ‏PG‏های پرکارترین مقصد از نگاشت واقعی ‏CRUSH‏ (osdmaptool --‏mark-out 0‏) آمده است. محور عمودی لگاریتمی است و فقط نرخ ۰.۱۵ ‏TB/h‏ برای هر دیسک فرضی است
۲-۱ساختار یک ‏image‏ در ‏RADOS‏: شیء ‏header‏ برای فراداده و اشیای داده‌ی ۴ مگابایتی که فقط پس از نوشتن ساخته می‌شوند
۲-۲راه‌های دسترسی به ‏RBD: librbd‏ در فضای کاربر (‏QEMU‏، ‏rbd-nbd‏، دروازه‌ی ‏NVMe-oF‏) و ‏krbd‏ در هسته
۲-۳زنجیره‌ی ‏image‏ والد، ‏snapshot‏ و ‏clone‏؛ ‏flatten‏ وابستگی ‏clone‏ به والد را قطع می‌کند
۲-۴دو حالت ‏mirroring‏: در حالت ‏journal‏ همه‌ی نوشتن‌ها ثبت و بازپخش می‌شوند؛ در حالت ‏snapshot‏ تفاضل ‏snapshot‏های دوره‌ای منتقل می‌شود
۲-۵توپولوژی ‏mirroring: rbd-mirror‏ در ‏site-b‏ تغییرات را از ‏site-a‏ می‌خواند؛ برای ‏failback‏ به ‏rbd-mirror‏ در ‏site-a‏ هم نیاز است
۲-۶چیدمان یک تصویر فرمت ۲ در ‏RADOS‏. ردیف بالا فهرست‌های سطح ‏pool‏ را نشان می‌دهد: در ‏omap‏ شیء ‏rbd_directory‏ کلیدهای ‏name_‏ و ‏id_‏ نام و شناسه را به هم نگاشت می‌کنند. ردیف میانی اشیای فراداده‌ی هر تصویر است: ‏rbd_id.<name>‏ شناسه را نگه می‌دارد، و ‏rbd_header.<id>‏ اندازه، ‏order‏، ‏features‏، ‏object_prefix‏، ‏snapshot‏ها و والد را در ‏omap‏ ذخیره می‌کند و ‏watcher‏ها روی همین شیء ثبت می‌شوند. ‏object map‏ برای هر شیء داده دو بیت حالت دارد. ردیف پایین اشیای داده است که نامشان از ‏object_prefix‏ و یک عدد ‏hex‏ شانزده‌رقمی ساخته می‌شود؛ فقط اشیایی ساخته می‌شوند که واقعاً نوشته شده باشند (‏thin provisioning‏)
۲-۷انتقال ‏exclusive lock‏ بین دو کلاینت. ‏B‏ ابتدا مالک فعلی را می‌خواند و اگر قفل گرفته شده باشد، از طریق ‏watch/notify‏ روی ‏header‏ درخواست قفل می‌فرستد. اگر ‏A‏ سالم باشد، طبق ‏policy‏ (پیش‌فرض: آزادسازی خودکار) کش را ‏flush‏ و قفل را رها می‌کند. اگر ‏A‏ دیگر ‏watch‏ نداشته باشد، ‏B‏ قفل را می‌شکند و با ‏rbd_blocklist_on_break_lock=true‏ (پیش‌فرض) آدرس ‏A‏ را ‏blocklist‏ می‌کند تا نوشتن‌های دیررسیده‌ی ‏A‏ پذیرفته نشوند. سپس ‏B‏ قفل را می‌گیرد و در ‏PostAcquire‏، ‏object map‏ و ‏journal‏ را باز می‌کند
۲-۸زنجیره‌ی شیءهای یک ‏image‏ با قالب ۲. ردیف اول ساختن ‏id‏ از ‏instance id‏ و یک عدد تصادفی است. ردیف دوم نگاشت نام به ‏id‏ در ‏rbd_id‏ و ‏rbd_directory‏، و سرآیند ‏image‏ است. ردیف سوم ‏object map‏، شیءهای داده‌ای که با اولین نوشتن ساخته می‌شوند، و ورودی ‏trash‏ هنگام حذف است. ‏id‏ نمونه نمادین است
۲-۹پنج درخواست ‏discard‏ روی سه شیء چهارمگابایتی. نوار خاکستری بالای هر ردیف بازه‌ی درخواستی است. قرمز ‏remove‏، نارنجی ‏truncate‏، آبی ‏zero‏ و هاشور «رد شده» است. گرد شدن به ۶۴ ‏KiB‏ باعث می‌شود ‏discard‏های کوچک‌تر از یک واحد کامل حذف شوند
۲-۱۰مسیر تصمیم ‏discard‏ در ‏librbd‏: تبدیل به ‏extent‏های شیء، هرس با واحد ۶۴ ‏KiB‏ (مگر اینکه ‏skip_partial_discard‏ خاموش باشد یا ‏cache‏ کثیف وجود داشته باشد) و انتخاب ‏remove‏، ‏truncate‏ یا ‏zero‏. برای ‏clone‏ با ‏parent‏، حذف کامل به ‏truncate‏ تبدیل می‌شود تا داده‌ی ‏parent‏ دوباره پیدا نشود
۲-۱۱‏USED‏ اندازه‌گیری‌شده روی کلاستر آزمایشی ‏Ceph 18.2.7‏ برای چهار بار کاری. خاکستری بایت‌های واقعاً نوشته‌شده است، آبی ‏rbd du --exact‏ و نارنجی ‏rbd du‏ پیش‌فرض (شمارش کل شیء از ‏object map‏). محور عمودی لگاریتمی است. در بار تصادفی ‏D‏، ‏‏--exact‏ هم حدود دویست برابر داده‌ی نوشته‌شده را نشان می‌دهد، چون هر شیء را از بایت ۰ تا بالاترین بایت نوشته‌شده می‌شمارد
۲-۱۲ترتیب گام‌های ‏switchover‏، ‏failover‏ اجباری و ‏failback‏
۳-۱نگاشت سرویس‌های ‏OpenStack‏ به اجزای ‏Ceph‏: هر سرویس ‏pool‏ یا رابط اختصاصی خودش را دارد
۳-۲توزیع کاربران و ‏keyring‏ها: سرویس‌های کنترلی هر کدام کاربر خودشان را دارند و ‏nova-compute‏ از طریق ‏secret‏ در ‏libvirt‏ به کلید ‏client.cinder‏ دسترسی پیدا می‌کند
۳-۳‏copy-on-write‏ سرتاسری: هم ‏volume‏ در ‏Cinder‏ و هم دیسک موقت ‏Nova clone‏ همان ‏snapshot‏ در ‏Glance‏‌اند
۳-۴مسیر بوت از ‏volume: Cinder volume‏ را با ‏clone‏ می‌سازد و ‏nova-compute‏ از طریق ‏libvirt‏ و ‏librbd‏ آن را به ‏QEMU‏ می‌دهد
۳-۵الگوی رایج ‏OpenStack‏ روی ‏RBD‏. ردیف بالا نشان می‌دهد که ‏Glance‏ تصویر را با یک ‏snapshot‏ ذخیره می‌کند. در ‏clone v1‏ این ‏snapshot‏ باید ‏protect‏ شود؛ در ‏clone v2‏ (پیش‌فرض ‏rbd_default_clone_format=auto‏، در صورتی که ‏min_compat_client‏ حداقل ‏mimic‏ باشد) این کار لازم نیست و ‏snapshot‏ والد حتی با وجود فرزند قابل حذف است و به ‏trash‏ می‌رود. ردیف میانی صدها ‏volume‏ را نشان می‌دهد که بدون کپی داده از یک ‏snapshot‏ ساخته شده‌اند. ردیف پایین پیامدهای عملیاتی است: خواندن ناحیه‌های نوشته‌نشده از والد سرویس داده می‌شود، اولین نوشتن روی هر شیء یک ‏copyup‏ به اندازه‌ی کل شیء ایجاد می‌کند، و ‏flatten‏ وابستگی را قطع ولی کل فضا را مصرف می‌کند
۳-۶مسیر نوشتن روی یک تصویر ‏clone‏. اگر شیء فرزند وجود داشته باشد، نوشتن مستقیم است. در غیر این صورت، ‏CopyupRequest‏ کل محدوده‌ی شیء را از ‏snapshot‏ والد می‌خواند، در صورت وجود ‏deep-flatten‏ و ‏snapshot‏های فرزند داده را به آن‌ها هم کپی می‌کند (‏deep_copy‏)، ‏object map‏ را به‌روز می‌کند و در نهایت یک عملیات اتمیک واحد روی ‏OSD‏ می‌فرستد. این عملیات شامل متد ‏cls‏ به نام ‏rbd.copyup‏ یا ‏sparse_copyup‏ است که داده‌ی والد را فقط در صورت نبود شیء می‌نویسد، و به دنبال آن خود ‏write‏ کاربر می‌آید. اتمیک بودن این ترکیب تضمین می‌کند که هیچ خواننده‌ای شیء نیمه‌کپی‌شده نبیند
۳-۷نگاشت یک نوشتن ۱ مگابایتی از ‏offset‏ ‏۱۰.۵ ‏MiB‏ به شیءهای ‏RADOS‏ در دو چیدمان. بالا: ‏stripe_count‏ = ۱ (پیش‌فرض)، که کل نوشتن در شیء شماره‌ی ۲ و از ‏offset‏ ‏۰‏x280000‏ قرار می‌گیرد. پایین: ‏stripe_unit‏ = ۶۴‏K‏ و ‏stripe_count‏ = ۴، که همان نوشتن به ۱۶ قطعه‌ی ۶۴‏K‏ تقسیم و به نوبت میان چهار شیء پخش می‌شود. شماره‌ی قطعه‌های بافر روی هر شیء نوشته شده است. همه‌ی قطعه‌ها در «مجموعه‌ی شیء» صفر هستند، چون ۴۲ تا ۴۵ کمتر از ۶۴ ‏stripe‏ در هر شیء است
۳-۸اندازه‌ی ‏object map‏ برحسب اندازه‌ی ‏image‏، برای سه اندازه‌ی شیء، روی محورهای لگاریتمی. هر شیء دو بیت می‌گیرد، پس اندازه‌ی ‏map‏ با اندازه‌ی ‏image‏ نسبت خطی دارد. خط‌چین‌های عمودی بزرگ‌ترین ‏image‏ سازگار با ‏object map‏ برای هر اندازه‌ی شیء هستند، یعنی جایی که تعداد شیءها به ۲۵۶ میلیون می‌رسد. نقطه‌ی سیاه ‏volume‏ یک ترابایتی معمولی با شیء ۴ ‏MiB‏ است که فقط ۶۴ ‏KiB map‏ دارد
۳-۹ماشین حالت یک خانه‌ی ‏object map‏. خانه‌ها در آغاز ‏NONEXISTENT‏ هستند. پیش از نخستین نوشتن به ‏EXISTS‏ می‌روند، چون ‏librbd map‏ را قبل از داده به‌روز می‌کند تا پس از خرابی کلاینت، شیءِ موجودی «ناموجود» علامت نخورد. هنگام ساخت ‏snapshot‏ همه‌ی خانه‌های ‏EXISTS‏ در ‏map‏ فعلی (‏HEAD‏) به ‏EXISTS_CLEAN‏ تبدیل می‌شوند. هر نوشتن بعدی خانه را دوباره ‏EXISTS‏ می‌کند. حذف دو مرحله دارد: ابتدا ‏PENDING‏، سپس پس از حذف موفق ‏NONEXISTENT‏
۳-۱۰جدول حالت محاسبه‌ی ‏diff‏ برای شش شیء نمونه از ‏snap1‏ تا ‏HEAD‏. هر خانه وضعیت شیء در آن ‏object map‏ را نشان می‌دهد (نارنجی ‏EXISTS‏، آبی ‏EXISTS_CLEAN‏، خاکستری ‏NONEXISTENT‏) و زیر آن حالت ‏diff‏ پس از پردازش همان ‏map‏ آمده است. ستون آخر نتیجه است: قرمز یعنی داده‌ی تغییرکرده که باید منتقل شود، بنفش یعنی ناحیه‌ای که باید صفر گزارش شود، سبز و سفید یعنی بدون تغییر. ‏o2‏ نشان می‌دهد چرا ‏HOLE‏ در ‏snapshot‏ میانی تغییر نمی‌کند: نتیجه فقط به وضعیت در ‏map‏ آخر بستگی دارد
۳-۱۱سمت چپ: تعداد ‏IO‏ پذیرفته‌شده در هر ثانیه برای یک کلاینت اشباع، در سه پیکربندی. بدون ‏burst‏ نرخ ثابت ۱۰۰ است. با ‏burst‏ ‏۳۰۰ و ‏burst_seconds‏ ‏۲، نرخ حدود سه ثانیه ۳۰۰ می‌ماند و بعد به ۱۰۰ برمی‌گردد. اگر کلاینت پیش از آن سه ثانیه بی‌کار بوده باشد، ثانیه‌ی نخست ۵۸۵ ‏IO‏ می‌گیرد. سمت راست: توکن‌های افزوده‌شده در هر ‏tick‏ برای سه مقدار ‏avg‏. پخش صحیح ‏avg‏ میان ‏tick‏ها، جمع هر ثانیه را دقیق نگه می‌دارد. این عددها از بازپیاده‌سازی پایتونی ‏TokenBucketThrottle‏ با فرض تقاضای پیوسته به دست آمده‌اند، نه از اندازه‌گیری روی کلاستر
۴-۱معماری ‏RGW‏: نمونه‌های بدون حالت پشت ‏load balancer‏ و ‏pool‏های جداگانه برای ‏index‏، داده و فراداده
۴-۲یک شیء ‏S3‏ بیست‌مگابایتی در ‏RADOS‏: شیء ‏head‏ با فراداده و ‏manifest‏، اشیای ‏tail‏ برای بقیه‌ی داده، و یک ورودی در ‏shard‏ مربوط به ‏index‏
۴-۳دو سازوکار چندمستاجری: ‏tenant‏ برای جداسازی فضای نام ‏bucket‏ها و ‏account‏ (از ‏Squid‏) برای مدیریت سلف‌سرویس کاربران و سیاست‌ها به سبک ‏IAM‏
۴-۴جریان احراز هویت ‏Swift‏ با ‏Keystone: RGW‏ توکن را نزد ‏Keystone‏ اعتبارسنجی می‌کند و برای هر پروژه خودکار کاربر می‌سازد
۴-۵مسیر ‏PUT‏ یک شیء. ‏radosgw‏ شماره‌ی شارد را از ‏hash‏ نام شیء محاسبه می‌کند. ابتدا متد ‏cls‏ به نام ‏bucket_prepare_op‏ یک ورودی با حالت ‏PENDING_MODIFY‏ و یک ‏tag‏ یکتا در شارد ثبت می‌کند. سپس ‏head‏ (شامل حداکثر ۴‏MiB‏ اول داده و ‏xattr‏های ‏manifest‏، ‏ACL‏ و ‏etag‏) و ‏stripe‏های ‏tail‏ نوشته می‌شوند. در پایان ‏bucket_complete_op‏ ورودی را با اندازه، ‏etag‏ و ‏mtime‏ به حالت ‏COMPLETE‏ می‌برد و یک رکورد ‏bilog‏ برای همگام‌سازی ‏multisite‏ می‌سازد. اگر ‏RGW‏ بین این دو مرحله از کار بیفتد، ورودی ‏pending‏ باقی می‌ماند و بعداً با ‏dir_suggest_changes‏ (هنگام ‏list‏) یا ‏radosgw-admin bucket check‏ اصلاح می‌شود
۴-۶چیدمان فیزیکی. در ‏pool index‏، هر ‏bucket‏ به ‏N‏ شیء .‏dir.<marker>.<shard>‏ تقسیم می‌شود و ورودی‌ها در ‏omap‏ آن‌ها نگهداری می‌شوند. در ‏pool‏ داده، ‏head‏ با نام <‏marker>_<key>‏ و ‏tail‏ها با پیشوند __‏shadow_‏ ذخیره می‌شوند. در ‏multipart‏ هر ‏part‏ یک شیء __‏multipart_‏ جداست و ‏CompleteMultipartUpload‏ فقط ‏manifest‏ را می‌سازد و داده را کپی نمی‌کند. ‏tail‏ اشیای حذف‌شده یا بازنویسی‌شده به‌صورت ناهمگام در صف ‏GC‏ (کلاس ‏rgw_gc‏) پاک می‌شوند
۴-۷سمت چپ: پخش ۱۰۰۰۰۰ کلید پشت‌سرهم میان ۱۱ ‏shard‏ ایندکس. خط‌چین میانگین است و همه‌ی ‏shard‏ها در حدود یک درصد آن هستند. سمت راست: سه کلید نمونه گام‌به‌گام از ‏hash‏ تا شماره‌ی ‏shard‏، هم برای ۱۱ و هم برای ۲۳ ‏shard‏. مرحله‌ی ‏XOR‏ بایت پایین ‏hash‏ را به بایت بالا می‌برد، چون باقی‌مانده‌ی تقسیم بر عدد اول کوچک بیشتر به بیت‌های پایین حساس است
۴-۸تعداد ‏shard‏ ایندکس برحسب تعداد شیء در ‏bucket‏، روی محورهای لگاریتمی. آبی مسیر یک ‏zone‏ با ضریب ۲ است و قرمز مسیر ‏multisite‏ با ضریب ۸. هر پله یک ‏reshard‏ است و عدد کنار آن تعداد ‏shard‏ تازه (عدد اول) است. خط نقطه‌چین سقف ۱۰۰۰۰۰ شیء در هر ‏shard‏ است. هر بار که ‏bucket‏ از آن عبور کند، ‏reshard‏ صف می‌شود. سقف ۱۹۹۹ در هر دو مسیر به تعداد شیء متفاوتی می‌رسد
۴-۹تعداد کل ورودی‌های ایندکسی که برای ساختن یک صفحه‌ی فهرست مرتب درخواست می‌شود، برحسب تعداد ‏shard‏، برای ‏max-keys‏ برابر ۱۰۰۰ و ۱۰۰. خط نقطه‌چین هم‌رنگ تعداد واقعی کلیدهای صفحه است. با ‏shard‏ کم، اضافه‌خوانی کم است. از جایی که ‏n‏ / ‏s‏ زیر ۸ می‌رود، حداقل ۸ غالب می‌شود، دو منحنی بر هم منطبق می‌شوند و هزینه با تعداد ‏shard‏ خطی بالا می‌رود. عددها فقط تلاش اول را نشان می‌دهند، پیش از گسترش ضریب
۴-۱۰بالا: یک ‏PUT‏ ساده‌ی ۱۰ مگابایتی. ‏head‏ (زرد) چهار مگابایت اول و فراداده را دارد و دو ‏stripe‏ در ‏namespace‏ ‏‏shadow‏ (آبی) با پیشوند تصادفی ۳۲ نویسه‌ای بقیه را دارند. پایین: آپلود ‏multipart‏ با دو قطعه‌ی ۱۰ مگابایتی. ‏head‏ بدون داده است و فقط ‏manifest‏ را دارد. هر قطعه یک شیء در ‏namespace‏ ‏‏multipart‏ (قرمز) برای ۴ مگابایت اول خود و دو ‏stripe‏ در ‏namespace‏ ‏‏shadow‏ دارد. نام‌ها از ‏get_implicit_location‏ گرفته شده‌اند. ‏marker‏ و ‏upload id‏ نمادین هستند
۴-۱۱‏placement‏ و ‏storage class‏: داده‌ی تازه در کلاس ‏STANDARD‏ و پس از مدتی با ‏lifecycle‏ به کلاس سرد ‏EC‏ یا ذخیره‌سازی ابری بیرونی منتقل می‌شود
۵-۱سلسله‌مراتب ‏multisite: realm‏ شامل ‏zonegroup‏ها و هر ‏zonegroup‏ شامل ‏zone‏هایی است که هر کدام در یک کلاستر ‏Ceph‏ جداگانه قرار دارند
۵-۲از ‏federated‏ در ‏Hammer‏ به ‏multisite‏ نسخه‌ی ۲: همگام‌سازی از یک ‏agent‏ بیرونی به درون خود ‏radosgw‏ منتقل شد و همه‌ی ‏zone‏ها نوشتنی شدند
۵-۳گام‌های راه‌اندازی: ساخت ‏realm‏، ‏zonegroup‏ و ‏master zone‏ در کلاستر اصلی، سپس ‏pull‏ کردن ‏realm‏ و ساخت ‏zone‏ ثانوی در کلاستر دوم
۵-۴سازوکار همگام‌سازی: ‏zone‏ مقصد ‏datalog‏ و ‏bilog‏ مبدأ را می‌خواند و اشیای تغییرکرده را با ‏GET‏ از ‏endpoint‏ مبدأ می‌کشد؛ فراداده از ‏metadata log‏ در ‏master zone‏ می‌آید
۵-۵جریان کامل ‏multisite‏. در ‏zone‏ مبدأ، هر ‏PUT‏ یا ‏DELETE‏ یک ورودی ‏bilog‏ در شارد ‏index‏ می‌سازد و یک ورودی ‏datalog‏ اعلام می‌کند که «آن شارد ‏bucket‏ تغییر کرده است». پنجره‌ی ۳۰ ثانیه‌ای ‏datalog‏ تغییرات پیاپی روی یک شارد را ادغام می‌کند تا لاگ منفجر نشود. ساخت ‏bucket‏ یا کاربر در ‏mdlog‏ ثبت می‌شود که فقط ‏master zone‏ آن را تولید می‌کند. در ‏zone‏ مقصد سه لایه ‏coroutine‏ کار می‌کنند: ‏meta sync‏، ‏data sync‏ برای هر شارد ‏datalog‏، و ‏bucket sync‏ برای هر شارد ‏bucket‏ که ‏bilog‏ را می‌خواند. لایه‌ی پایانی شیء را با ‏fetch_remote_obj‏ از مبدأ می‌کشد، محلی می‌نویسد و ‏marker‏ را در شیء وضعیت ذخیره می‌کند
۵-۶ماشین حالت همگام‌سازی داده. ابتدا اشیای وضعیت ساخته می‌شوند (‏Init‏)، سپس همه‌ی ‏bucket‏های مبدأ در ‏full-sync map‏ فهرست می‌شوند (‏BuildingFullSyncMaps‏)، و پس از آن هر شارد ‏datalog‏ مستقل از بقیه ابتدا ‏full sync‏ و بعد ‏incremental sync‏ را با ‏poll‏ بیست‌ثانیه‌ای اجرا می‌کند. خطاها در ‏error repo‏ ثبت و بعداً دوباره تلاش می‌شوند. اگر چند ‏RGW‏ در ‏zone‏ مقصد باشند، هر شارد با یک ‏lease‏ دودقیقه‌ای فقط به یکی از آن‌ها سپرده می‌شود. ‏trim‏ لاگ‌های مبدأ هر ۲۰ دقیقه و فقط تا کمترین ‏marker‏ی انجام می‌شود که همه‌ی ‏peer‏ها به آن رسیده‌اند
۵-۷بالا: سهم ‏shard‏های ‏datalog‏ از ۱۱۰۰۰ ‏bucket shard‏ (۱۰۰۰ bucket با نام شماره‌دار و ۱۱ ‏shard‏ برای هر کدام). فقط ۸۸ ‏shard‏ از ۱۲۸ ‏shard‏ کار دارند، در دسته‌هایی ۱۱تایی که از ۸، ۲۴، …، ۱۲۰ شروع می‌شوند. پایین: همین تعداد ‏bucket‏ با نام تصادفی که همه‌ی ۱۲۸ ‏shard‏ را می‌پوشاند. خط‌چین میانگین ایده‌آل، یعنی حدود ۸۶ ‏bucket shard‏ برای هر ‏shard‏ ‏‏datalog‏ است. شبیه‌سازی ما، بر پایه‌ی بازپیاده‌سازی ‏choose_oid‏
۵-۸بالا: یک ‏bucket shard‏ پرکار با ۱۲۰۰۰ نوشتن در ۱۲۰ ثانیه فقط شش ورودی ‏datalog‏ تولید می‌کند. اولی در لحظه‌ی نخستین نوشتن ثبت می‌شود و بقیه در تیک‌های ۲۲ ثانیه‌ای ‏renew‏. پایین: با نوشتن پراکنده، ‏renew‏ فقط وقتی انجام می‌شود که در پنجره‌ی قبل نوشتنی مانده باشد. نوشتن ثانیه‌ی ۱۰۰ پس از انقضای پنجره بلافاصله ورودی می‌سازد
۵-۹درخت تصمیم ‏add_entry‏ در ‏main‏. ردیف اول مسیر ایمن را نشان می‌دهد: اگر ‏watch‏ روی ‏sem-set‏ قطع باشد، ورودی بی‌درنگ نوشته می‌شود. ردیف دوم ادغام در پنجره و انتظار برای ‏push‏ در جریان است. ردیف سوم ‏push‏ واقعی و چرخه‌ی ‏renew‏ است که پنجره را تمدید می‌کند و شمارنده‌ها را کم می‌کند
۵-۱۰چپ: ‏shard‏ ‏‏mdlog‏ برای ۱۰۰۰ کلید ‏bucket‏ با نام شماره‌دار که فقط چهار ‏shard‏ را اشغال می‌کنند. راست: ۱۰۰۰ نام تصادفی که هر ۶۴ ‏shard‏ را می‌پوشانند، با ۷ تا ۳۱ کلید در هر ‏shard‏. محاسبه با بازپیاده‌سازی ‏rgw_shard_name‏
۵-۱۱چرخه‌ی ‏trim‏ ‏‏datalog‏. هر ۲۰ دقیقه وضعیت همگام‌سازی از همه‌ی ‏peer‏ها گرفته می‌شود. اگر یکی پاسخ ندهد، آن دور ‏trim‏ انجام نمی‌شود. برای هر ‏shard‏ کمترین ‏marker‏ پایدار حساب می‌شود (در ‏full sync‏، ‏‏next_step_marker‏) و فقط وقتی ‏trim‏ انجام می‌شود که از ‏trim‏ قبلی جلوتر باشد. ردیف پایین یک مثال نمادین است
۵-۱۲تصمیم‌گیری هنگام از دست رفتن ‏master zone‏: در خرابی کوتاه صبر کنید؛ در خرابی طولانی ‏zone‏ دیگری را ‏master‏ کنید
۶-۱معماری ‏CephFS‏: کلاینت فراداده را از ‏MDS‏ و داده را مستقیم از ‏OSD‏ها می‌گیرد؛ ‏MDS journal‏ و ‏inode‏ها را در ‏pool‏ فراداده نگه می‌دارد
۶-۲فراخوانی ‏capability‏: وقتی کلاینت دوم فایل را باز می‌کند، ‏MDS‏ از کلاینت اول می‌خواهد داده‌ی ‏buffer‏‌شده را ‏flush‏ کند و مجوزش را آزاد کند
۶-۳چند ‏MDS‏ فعال: زیردرخت‌ها با ‏pin‏ صریح یا ‏pin‏ توزیع‌شده بین ‏rank‏ها تقسیم می‌شوند
۶-۴چهار راه دسترسی به ‏CephFS‏: کلاینت هسته، ‏ceph-fuse‏، ‏NFS‏ از طریق ‏Ganesha‏ و ‏SMB‏ از طریق ‏Samba‏
۶-۵رمزگشایی رشته‌ی ‏cap‏. ردیف اول پیشوندها را نشان می‌دهد: ‏p‏ برای ‏pin‏، ‏A‏ برای ‏Auth‏ (مالکیت و مجوز)، ‏L‏ برای ‏Link‏، ‏X‏ برای ‏Xattr‏ و ‏F‏ برای ‏File‏ (محتوا و اندازه). ردیف دوم بیت‌های عمومی است که روی هر پیشوند اعمال می‌شوند: ‏s‏ برای خواندن مشترک، ‏x‏ برای انحصاری، ‏c‏ برای کش، ‏r‏ و ‏w‏ برای خواندن و نوشتن، ‏b‏ برای بافر نوشتن، ‏a‏ برای گسترش ‏EOF‏ و ‏l‏ برای ‏lazyio‏. ردیف سوم سه حالت را نشان می‌دهد: خواننده‌ی عادی، تنها نویسنده با کش و بافر کامل، و دو نویسنده‌ی هم‌زمان روی یک فایل که ‏MDS‏ از آن‌ها ‏c‏ و ‏b‏ را پس می‌گیرد. در حالت آخر همه‌ی ‏I/O‏ به‌صورت هم‌گام به ‏OSD‏ می‌رود؛ این دلیل اصلی کندی برنامه‌هایی است که هم‌زمان از چند گره روی یک فایل می‌نویسند
۶-۶چرخه‌ی عمر یک عملیات فراداده. در ردیف اول، درخواست کلاینت در ‏MDS‏ قفل‌های لازم (‏SimpleLock‏، ‏ScatterLock‏ و...) را می‌گیرد، کش را به‌روز می‌کند و رویداد را در ‏MDLog‏ ثبت می‌کند. در ردیف دوم، رویدادها در ‏segment‏های ۱۰۲۴ رویدادی جمع می‌شوند و ‏MDS‏ سعی می‌کند حداکثر ۱۲۸ ‏segment‏ نگه دارد. با ‏expire‏ شدن ‏segment‏، ‏dirfrag‏ها در ‏omap‏ نوشته می‌شوند. اگر ‏trim‏ عقب بماند، هشدار ‏MDS_TRIM_BEHIND‏ ظاهر می‌شود. ردیف سوم توالی حالت‌ها پس از شکست یک ‏rank‏ است: ‏replay‏، ‏resolve‏ (فقط در چند ‏rank‏ فعال)، ‏reconnect‏ (کلاینت‌ها ‏cap‏های خود را اعلام می‌کنند)، ‏rejoin‏، ‏clientreplay‏ و در نهایت ‏active. standby-replay‏ با دنبال کردن پیوسته‌ی ‏journal‏، زمان ‏replay‏ را کوتاه می‌کند
۶-۷چیدمان یک فایل ۱۰ مگابایتی. چپ: ‏layout‏ پیش‌فرض، با هر ۴ مگابایت پشت‌سرهم در یک شیء. راست: ‏su‏ برابر ۱ ‏MiB‏ و ‏sc‏ برابر ۴، که مگابایت‌های پیاپی را به‌صورت چرخشی بین چهار شیء پخش می‌کند. نام شیء ‏ino‏ به ‏hex‏ و شماره‌ی شیء به ‏hex‏ هشت‌رقمی است. پیکان جای بایت ۹ ‏MiB‏ را در هر دو حالت نشان می‌دهد
۶-۸فضای ‏hash‏ نام‌ها در یک پوشه. ردیف بالا ‏frag‏ ریشه است، ردیف میانی هشت ‏frag‏ پس از تقسیم سه‌بیتی با _‏enc‏ و تعداد نام‌های هر کدام، و ردیف پایین تقسیم دوباره‌ی ‏frag‏ ‏۰۱۰ به هشت ‏frag‏ شش‌بیتی. خط‌های رنگی جای ‏hash‏ سه نام نمونه را نشان می‌دهند. محور افقی ۲۴ بیت پایین ‏hash‏ است
۶-۹تعداد ‏dirfrag‏ (آبی، پله‌ای) و اندازه‌ی بزرگ‌ترین ‏frag‏ (قرمز) هنگام ساخت ۱۰۰۰۰۰ ورودی در یک پوشه، با فرض تقسیم بی‌درنگ. خط‌چین آستانه‌ی ۱۰۰۰۰ و نقطه‌چین آستانه‌ی تقسیم سریع ۱۵۰۰۰ است. پس از تقسیم اول، اندازه‌ی بزرگ‌ترین ‏frag‏ به حدود ۱۲۵۰ می‌افتد و دوباره تا ۱۰۰۰۰ بالا می‌رود. شبیه‌سازی ما با ‏rjenkins‏ و منطق ‏frag_t‏
۶-۱۰منحنی شمارنده‌ی ‏IWR‏ یک ‏dirfrag‏ زیر بار نوشتن پایدار، با نیمه‌عمر ۵ ثانیه. هر منحنی به مقدار پایدار ‏r‏ × ۷.۲۱ میل می‌کند. خط‌چین آستانه‌ی ‏mds_bal_split_wr‏ است و نقطه‌ها لحظه‌ی رد شدن آن را نشان می‌دهند. منحنی ۱۰۰۰ نوشتن بر ثانیه هرگز به آستانه نمی‌رسد
۶-۱۱درخت تصمیم تقسیم و ادغام ‏dirfrag‏. ردیف اول دو محرک تقسیم است: اندازه و داغی. ردیف دوم صف‌بندی عادی با تأخیر ۵ ثانیه یا مسیر سریع بالای ۱.۵ برابر آستانه است. ردیف سوم تقسیم به ۸ فرزند، ادغام ‏frag‏های زیر ۵۰ ورودی و سقف سخت ۱۰۰۰۰۰ را نشان می‌دهد
۶-۱۲سلسله‌مراتب ‏volume‏، ‏subvolumegroup‏ و ‏subvolume‏؛ درایور ‏ceph-csi‏ و ‏Manila‏ هر ‏PVC‏ یا ‏share‏ را به یک ‏subvolume‏ نگاشت می‌کنند
۷-۱لایه‌های مدیریت ‏daemon‏ در ‏cephadm‏: از دستور ‏ceph orch‏ تا کانتینر روی میزبان
۷-۲منابع پیکربندی و ترتیب اولویت آن‌ها؛ مقدار مؤثر هر ‏daemon‏ را با ‏ceph config show‏ ببینید
۷-۳دو راه افزایش ظرفیت: دیسک بیشتر در همان گره‌ها یا گره‌های بیشتر
۷-۴روال امن تعویض دیسک: اول بازیابی، بعد بررسی ‏safe-to-destroy‏، سپس حذف با حفظ ‏id‏ و تعویض فیزیکی
۷-۵ترتیب ارتقا در ‏cephadm‏: اول ‏MGR‏ و ‏MON‏، سپس ‏OSD‏ و ‏MDS‏، در پایان دروازه‌ها
۷-۶توالی پیام‌های ‏Paxos‏ در ‏MON‏: پس از انتخابات، رهبر با ‏OP_COLLECT‏ شماره‌ی پیشنهاد جدید را اعلام و با ‏OP_LAST‏ مقادیر ناتمام را جمع می‌کند؛ سپس برای هر تغییر، ‏OP_BEGIN‏ با نسخه‌ی بعدی ارسال، ‏OP_ACCEPT‏ از همه‌ی اعضای ‏quorum‏ دریافت، ‏OP_COMMIT‏ اعلام و با ‏OP_LEASE‏ اجازه‌ی خواندن تا مدت ‏mon_lease‏ به ‏peon‏ها داده می‌شود
۷-۷مسیر یک فرمان در یک سرویس ‏Paxos‏ مانند ‏OSDMonitor‏: فرمان‌های فقط‌خواندنی در ‏preprocess_query‏ از نقشه‌ی فعلی پاسخ داده می‌شوند؛ فرمان‌های تغییردهنده در ‏prepare_update‏ روی ‏pending incremental‏ اعمال، با تأخیر ‏paxos_propose_interval‏ دسته‌بندی و با ‏propose_pending‏ به ‏Paxos‏ پیشنهاد می‌شوند؛ پس از ‏commit‏، نقشه بارگذاری، ‏incremental‏ به مشترکان منتشر و به فرمان‌دهنده پاسخ داده می‌شود
۷-۸زمان‌بندی یک ‏OSD‏ خاموش با پیش‌فرض‌ها. پس از ۲۰ ثانیه بی‌پاسخی، همتایان روی دو میزبان گزارش می‌دهند و ‏MON‏ آن را ‏down‏ می‌کند. ‏PG‏ها ‏degraded‏ می‌شوند، اما داده جابه‌جا نمی‌شود. پس از ۶۰۰ ثانیه (به‌علاوه‌ی جمله‌ی ‏laggy) OSD‏ ‏‏out‏ می‌شود و ‏backfill‏ شروع می‌شود. خط بنفش استثناها را نشان می‌دهد: پرچم ‏noout‏ و ‏down‏ بودن کل یک ‏rack‏ (mon_osd_down_out_subtree_limit) که شمارنده را صفر می‌کند
۷-۹چپ: رشد ‏laggy_probability‏ و ‏laggy_interval‏ با هر ‏down‏ اشتباه (‏OSD‏ زنده که ۴۰ ثانیه بعد برمی‌گردد)، با وزن ۰.۳. راست: ‏grace‏ مؤثر شکست بعدی، یعنی ۲۰ ثانیه‌ی پایه به‌علاوه‌ی ‏p‏ × ‏I‏. جمله‌ی همتایان در این مثال صفر فرض شده است. اگر ‏down‏ بعدی بیش از ۴۸ ساعت بعد باشد، سابقه صفر می‌شود
۷-۱۰چپ: درصد استفاده‌ی دوازده ‏OSD‏، با یک ‏OSD‏ چهار ترابایتی که ۸۰٪ پر است (قرمز). راست: برای هر ‏OSD‏، داده‌ی خامی که می‌توان نوشت تا همان ‏OSD‏ به ‏full_ratio‏ برسد. کمینه‌ی آن‌ها (خط قرمز) پایه‌ی ‏MAX AVAIL‏ است و بر ‏size‏ یا (‏k‏ + ‏m‏) / ‏k‏ تقسیم می‌شود
۷-۱۱نردبان نسبت‌های پری با پیش‌فرض‌ها: هشدار ‏NEARFULL‏ از ۸۵٪، توقف ‏backfill‏ به سمت ‏OSD‏ از ۹۰٪، پرچم ‏FULL‏ و توقف نوشتن کلاینت‌ها از ۹۵٪، و ‏failsafe‏ خود ‏OSD‏ از ۹۷٪. سه نسبت اول در ‏OSDMap‏ نگه‌داری می‌شوند و با ‏set-*-ratio‏ تغییر می‌کنند. ‏failsafe‏ یک گزینه‌ی پیکربندی ‏OSD‏ است
۸-۱دو مرحله‌ی جاشناسی: نام شیء با ‏hash‏ به یک ‏PG‏ نگاشت می‌شود و ‏CRUSH‏ آن ‏PG‏ را به مجموعه‌ای از ‏OSD‏ها نگاشت می‌کند
۸-۲یک سلسله‌مراتب ‏CRUSH‏ نمونه: ‏root‏، ‏datacenter‏، ‏rack‏، ‏host‏ و ‏OSD‏ها با کلاس دستگاه
۸-۳نقشه‌های کلاستر در ‏MON‏ها نگهداری و با ‏epoch‏ نسخه‌گذاری می‌شوند؛ کلاینت‌ها و ‏OSD‏ها نقشه‌ی جدید را از ‏MON‏ یا از یکدیگر می‌گیرند
۸-۴سه مرحله‌ی نگاشت شیء به ‏OSD‏: در کلاینت، نام شیء با ‏ceph_str_hash‏ درهم‌سازی و با ‏ceph_stable_mod‏ به شماره‌ی ‏PG‏ تبدیل می‌شود؛ سپس شماره‌ی ‏PG‏ با شناسه‌ی ‏pool‏ ترکیب و به‌عنوان ورودی ‏x‏ به ‏crush_do_rule‏ داده می‌شود؛ در پایان ‏OSDMap OSD‏های ناموجود را حذف و ‏upmap‏ و ‏pg_temp‏ را اعمال می‌کند تا ‏acting set‏ و ‏primary‏ به دست آید
۸-۵الگوریتم ‏straw2‏ در سه مرحله: برای هر آیتم ‏bucket‏ یک عدد شبه‌تصادفی شانزده‌بیتی از ‏hash‏ ورودی، شناسه‌ی آیتم و شماره‌ی تلاش ساخته و لگاریتم طبیعی آن با جدول محاسبه و بر وزن تقسیم می‌شود؛ آیتمی با بیشترین مقدار انتخاب می‌شود که از نظر ریاضی معادل انتخاب کمینه از متغیرهای نمایی با نرخ برابر وزن است و احتمال انتخاب هر آیتم را دقیقاً متناسب با وزن آن می‌کند؛ پیامد آن حداقل جابه‌جایی داده هنگام تغییر وزن است
۸-۶جریان احراز هویت ‏cephx‏: کلاینت با اثبات دانستن کلید مشترک از ‏MON‏ کلید جلسه و بلیت احراز می‌گیرد، سپس برای سرویس ‏OSD‏ بلیت سرویس رمزشده با کلید چرخشی آن سرویس دریافت می‌کند؛ ‏OSD‏ که کلیدهای چرخشی را از ‏MON‏ گرفته است، بلیت را رمزگشایی و انقضا و ‏cap‏ها را بررسی می‌کند و پاسخ می‌دهد تا احراز دوطرفه کامل شود
۸-۷ساختار قاب در پروتکل ‏msgr2: preamble‏ با اندازه‌ی ثابت شامل برچسب نوع قاب، تعداد ‏segment‏ها تا چهار و طول و هم‌ترازی هر ‏segment‏ و ‏CRC‏ جداگانه؛ سپس حداکثر چهار ‏segment‏ شامل سرآیند، ‏front‏، ‏middle‏ و ‏data‏؛ و در پایان ‏epilogue‏ که در حالت ‏crc‏ شامل ‏CRC‏ هر ‏segment‏ و در حالت ‏secure‏ رمز و احراز صحت ‏AES-GCM‏ کل قاب است
۸-۸افزودن میزبان یازدهم (۱۲ ‏OSD‏) به کلاستر ۱۰ × ۱۲ با ‏straw2‏، ‏‏pool‏ سه‌نسخه‌ای و ۸۱۹۲ ‏PG‏، اندازه‌گیری‌شده با ‏osdmaptool‏. خاکستری سهم ایده‌آل است، سبز ‏replica‏هایی که به میزبان جدید رفتند و نارنجی جابه‌جایی بین ‏OSD‏های قدیمی که از عوض شدن ‏r‏ جایگاه در ‏chooseleaf‏ ناشی می‌شود
۸-۹تغییر وزن ‏CRUSH‏ یک ‏OSD‏ (osd.۵ روی ‏host0‏) از ۱.۰ به پنج مقدار و دسته‌بندی جای‌گذاری‌های جابه‌جاشده: آبی خود ‏osd.5‏، زرد دیگر ‏OSD‏های ‏host0‏ که با تغییر وزن میزبان سهم تازه می‌گیرند یا از دست می‌دهند، قرمز میزبان‌های نامربوط. چپ ‏straw2‏ و راست ‏straw‏ قدیمی، با ۸۱۹۲ ‏PG‏ و اندازه‌گیری ‏osdmaptool‏
۸-۱۰جریان ‏cephx‏: کلاینت هویتش را به ‏MON‏ ثابت می‌کند، ‏ticket‏ می‌گیرد و با آن مستقیماً با ‏OSD‏ کار می‌کند
۸-۱۱نوشتن در ‏pool‏ تکثیری: کلاینت فقط با ‏primary‏ صحبت می‌کند؛ ‏primary‏ به‌طور موازی به ثانویه‌ها تکثیر می‌کند و پس از ‏commit‏ همه‌ی نسخه‌ها ‏ack‏ می‌دهد
۸-۱۲حالت‌های اصلی ‏PG‏ و گذارهای رایج بین آن‌ها
۹-۱دو نسل ابزار مدیریت گرافیکی ‏Ceph‏: ابزارهای مستقل (‏Calamari‏، ‏VSM‏، ‏openATTIC‏) و ‏Dashboard‏ درون ‏MGR‏
۹-۲اجزای ‏Dashboard‏: ماژول ‏MGR‏، ‏Grafana‏ و ‏Prometheus‏ برای نمودارها، و در ‏Tentacle‏ دروازه‌ی ‏nginx‏ و ‏oauth2-proxy‏ به‌عنوان نقطه‌ی ورود واحد
۹-۳کنترل دسترسی مبتنی بر نقش در ‏Dashboard‏: هر کاربر یک یا چند نقش دارد و هر نقش مجموعه‌ای از مجوزها روی ‏scope‏های مشخص است
۹-۴کاربرد ‏Dashboard‏ در چرخه‌ی عمر کلاستر: استقرار، پیکربندی سرویس‌ها و کار روزانه‌ی پایش و نگهداری
۹-۵معماری ‏ceph-mgr‏. ردیف بالا بخش ‏C++‏ است. ‏MgrStandby‏ به ‏MON beacon‏ می‌فرستد و وقتی ‏MON‏ آن را ‏active‏ اعلام کند، ‏Mgr‏ نقشه‌ها را مشترک می‌شود و ‏ClusterState‏ را نگه می‌دارد. ‏DaemonServer‏ گزارش‌های دوره‌ای (‏MMgrReport‏) و ‏perf counter‏ها را از همه‌ی ‏daemon‏ها دریافت می‌کند. ردیف میانی میزبانی پایتون است: ‏PyModuleRegistry‏ ماژول‌ها را کشف و بارگذاری می‌کند، ‏ActivePyModules‏ هر ماژول را در یک ‏sub-interpreter‏ جدا با ‏thread‏ مخصوص ‏serve‏() اجرا می‌کند، و ‏BaseMgrModule‏ پل ‏C++‏ به پایتون برای ‏get‏، ‏mon_command‏ و ‏set_store‏ است. ردیف پایین ساختار یک ماژول را نشان می‌دهد: کلاس ‏Module‏ که از ‏MgrModule‏ ارث می‌برد، با گزینه‌ها، نوع اعلان‌های موردنیاز، فرمان‌های ‏CLI‏ از طریق ‏decorator‏ و حلقه‌ی ‏serve‏
۹-۶اعتبار پیچیدگی چهار رمز نمونه. هر خانه یک نویسه است و رنگ آن دسته‌ی نویسه و پهنایش اعتبار آن را نشان می‌دهد. خط‌چین حداقل ۱۰ است. رمز سوم با وجود اعتبار کافی به خاطر سه حرف پیاپی رد می‌شود. رمز چهارم نشان می‌دهد هر حرف غیر ‏ASCII‏ پنج اعتبار دارد. علامت سؤال جای حرف فارسی است
۹-۷دو مرحله‌ی رسیدگی به یک درخواست ‏REST‏ در ‏Dashboard‏. ردیف بالا احراز هویت با ‏JWT‏ است: امضا، زمان انقضا و نبود شناسه‌ی توکن در فهرست توکن‌های باطل‌شده. ردیف پایین مجوز است: ‏scope‏ کنترلر و مجوز متد ‏HTTP‏ باید هر دو در یکی از نقش‌های کاربر وجود داشته باشند. ترکیب مجوزهای دو نقش متفاوت برای یک درخواست کافی نیست
۹-۸خط زمانی یازده تلاش ورود با ‏ACCOUNT_LOCKOUT_ATTEMPTS‏ = ۱۰. هر تلاش ناموفق شمارنده را یکی بالا می‌برد. شرط قفل پیش از سنجش رمز بررسی می‌شود. بنابراین تلاش یازدهم، با هر رمزی، حساب را غیرفعال می‌کند و فقط مدیر با ‏ac-user-enable‏ می‌تواند آن را باز کند
۹-۹زمان از نوشتن یک شیء تا وقتی که ‏pg dump‏ آن را نشان می‌دهد، برای چهار مقدار ‏mgr_stats_period‏. چپ: توزیع تجمعی ۱۵ آزمایش. راست: جعبه‌نمودار همراه با خط‌های ‏y‏ = ‏p‏ و ‏y‏ = ۲‏p‏. اندازه‌گیری روی ‏Ceph 18.2.7‏
۹-۱۰اندازه‌ی پاسخ ‏JSON‏ (چپ) و میانه‌ی زمان (راست) برای ‏pg dump‏ کامل، ‏pg dump pgs_brief‏ و ‏status‏ در کلاسترهایی با ۳۲ تا ۱۰۲۴ ‏PG‏. اندازه‌گیری روی ‏Ceph 18.2.7‏
۱۰-۱سنجش لایه‌به‌لایه: هر لایه را فقط پس از شناختن سقف لایه‌ی زیرین بسنجید
۱۰-۲سه روش اجرای ‏fio‏ روی ‏Ceph‏: موتور ‏rbd‏ مستقیم با ‏librbd‏، دستگاه بلوکی نگاشت‌شده و فایل روی ‏CephFS‏
۱۰-۳دو مسیر ارتباط با ‏daemon: ceph tell‏ از طریق شبکه و ‏ceph daemon‏ از طریق ‏admin socket‏ محلی
۱۰-۴سه سازوکار سنجش در کد ‏Ceph‏. در ردیف بالا، ‏osd bench‏ در ‏OSD::run_osd_bench_test‏ ابتدا محدودیت‌ها را بررسی می‌کند. اندازه‌ی بلوک نباید از ۶۴‏MiB‏ بیشتر باشد. برای بلوک‌های کوچک، کل حجم به ۱۰۰ ‏IOPS‏ ضرب در ۳۰ ثانیه محدود است و برای بلوک‌های بزرگ به ۱۰۰‏MiB/s‏ ضرب در ۳۰ ثانیه، تا آزمون ‏OSD‏ را در محیط عملیاتی از کار نیندازد. سپس کلاس ‏OSDBenchTest‏ (تعریف‌شده در ‏OSD.h‏) تراکنش‌های نوشتن را مستقیم روی ‏ObjectStore‏ در ‏collection‏ متادیتا اجرا می‌کند؛ نه شبکه در کار است، نه ‏PG‏ و نه تکثیر. در ردیف میانی، ‏rados bench‏ از کلاس ‏ObjBencher‏ در ‏common/obj_bencher.cc‏ استفاده می‌کند: اشیای ‏benchmark_data_<host>_<pid>_objectN‏ را با ۱۶ عملیات هم‌زمان پیش‌فرض می‌نویسد، متادیتای اجرا را در ‏benchmark_last_metadata‏ ذخیره می‌کند و ‏status_printer‏ هر ثانیه پهنای باند و تأخیر لحظه‌ای را چاپ می‌کند. در ردیف پایین، ‏OSD‏ هنگام بوت (اگر ‏benchmark‏ غیرفعال نشده باشد) یک آزمون کوتاه ۴‏K‏ اجرا می‌کند، نتیجه را با آستانه‌های معقول نوع دستگاه مقایسه می‌کند و در صورت معتبر بودن، ‏osd_mclock_max_capacity_iops_hdd‏ یا _‏ssd‏ را در ‏config MON‏ ثبت می‌کند
۱۰-۵سقف ‏count‏ پذیرفتنی برای ‏osd bench‏ برحسب اندازه‌ی بلوک، هر دو محور لگاریتمی. زیر یک مگابایت سقف با فرض ۱۰۰ ‏IOPS‏ در ۳۰ ثانیه خطی رشد می‌کند. از یک مگابایت به بعد با فرض ۱۰۰ مگابایت بر ثانیه ثابت می‌ماند. ستاره پارامترهای آزمون بوت ‏mClock‏ است که دقیقاً روی سقف قرار دارد. بالاتر از ۶۴ مگابایت هر اندازه‌ی بلوکی رد می‌شود
۱۰-۶بازه‌ی پذیرش نتیجه‌ی آزمون بوت برای ‏HDD‏ و ‏SSD‏ روی محور لگاریتمی. ناحیه‌ی سبز بازه‌ی میان آستانه‌ی پایین و بالاست و خط آبی مقدار پیش‌فرض. نقطه‌های سبز ذخیره می‌شوند. نقطه‌های قرمز، مثل ‏HDD‏ با کش نوشتن یا ‏NVMe‏ بسیار سریع، رد می‌شوند و مقدار قبلی می‌ماند
۱۰-۷رزرو (ستون پررنگ) و سقف (ستون کم‌رنگ) سه کلاس سرویس در سه پروفایل داخلی ‏mClock‏، برحسب ‏op‏ کوچک در ثانیه برای ‏HDD‏ با پیش‌فرض‌ها. وزن هر کلاس سهم آن از ظرفیت باقی‌مانده پس از رزروهاست. سقف «‏max‏» یعنی محدودیتی جز ظرفیت دستگاه ندارد. اعداد از ضرب نسبت‌های پروفایل در ۱۵۰ مگابایت بر ثانیه و تقسیم بر هزینه‌ی هر ‏IO‏ به دست آمده‌اند
۱۰-۸چپ: ‏IOPS‏ و تأخیر میانگین نوشتن ۴ ‏KiB‏ برحسب تعداد عملیات در پرواز (‏rados bench‏ -‏t‏) روی کلاستر آزمایشی. راست: حاصل‌ضرب ‏IOPS‏ اندازه‌گیری‌شده در تأخیر میانگین که در همه‌ی نقطه‌ها ‏T‏ را بازمی‌سازد (قانون ‏Little‏ برای سامانه‌ی حلقه‌بسته)
۱۰-۹پهنای باند (نارنجی) و ‏IOPS‏ (آبی) نوشتن برحسب اندازه‌ی شیء با ۱۶ عملیات در پرواز روی همان کلاستر آزمایشی، همراه با تأخیر میانگین هر نقطه. زیر حدود ۶۴ ‏KiB‏ سقف ‏IOPS‏ حاکم است و بالای حدود ۲۵۶ ‏KiB‏ سقف پهنای باند
۱۰-۱۰کاربرد نجات با ‏ceph-objectstore-tool: OSD‏ متوقف، بررسی محتوا، ‏export‏ یک ‏PG‏ و ‏import‏ آن روی ‏OSD‏ دیگر
۱۱-۱توپولوژی آزمایشگاه پیشنهادی: سه گره ‏Ceph‏ و یک کلاینت روی یک میزبان ‏KVM‏
۱۱-۲ابزارهای استقرار دیروز و امروز
۱۱-۳چرخه‌ی تطبیق در ‏cephadm: spec‏ وضعیت مطلوب را تعیین می‌کند و ‏orchestrator‏ اختلاف با وضعیت واقعی را برطرف می‌کند
۱۱-۴یک دور از ‏CephadmServe.serve‏() به ترتیبی که در کد فراخوانی می‌شود. ردیف اول تازه‌سازی کش میزبان‌ها و ‏daemon‏ها است (‏gather-facts‏، فهرست ‏daemon‏ها، ‏inventory‏ دستگاه‌ها با ‏ceph-volume‏)، و پس از آن بررسی ‏daemon‏های سرگردان و اجرای اقدام‌های ناهمگام درخواستی. ردیف دوم هم‌گرایی است: _‏apply_all_services‏ برای هر ‏spec‏، _‏apply_service‏ را صدا می‌زند و ‏HostAssignment.place‏() در ‏schedule.py‏ بر اساس ‏placement‏ (count، ‏label‏، ‏host_pattern‏، ‏hosts‏) تعیین می‌کند کدام ‏daemon‏ باید اضافه یا حذف شود. ردیف سوم کارهای نگهداری است: بررسی سلامت ‏daemon‏ها و گواهی‌ها، پاک کردن سرویس‌های حذف‌شده، تشخیص ‏OSD‏های جابه‌جاشده و تلاش دوباره برای عملیات ناموفق. در پایان حلقه می‌خوابد، ولی هر فرمان ‏orch‏ با _‏kick_serve_loop‏ آن را فوراً بیدار می‌کند
۱۱-۵ترتیب قطعی میزبان‌ها برای پنج سرویس نمونه در یک کلاستر شش‌میزبانه. هر سطر خروجی ‏get_candidates‏ است: فهرست مرتب میزبان‌ها که با یک مولد تصادفی با ‏seed‏ برابر ‏sha1‏ نام سرویس به هنگ ۲³² بُر خورده است. خانه‌های با کادر پررنگ میزبان‌هایی هستند که برای یک کلاستر خالی با ‏count‏ داده‌شده انتخاب می‌شوند. ‏crash‏ بدون ‏count‏ روی همه‌ی میزبان‌ها می‌رود. هر رنگ یک میزبان ثابت است تا دیده شود سرویس‌ها روی میزبان‌های متفاوتی پخش می‌شوند
۱۱-۶سه مرحله‌ی ‏HostAssignment.place‏. مرحله‌ی اول نامزدها را از ‏placement‏ می‌سازد و با ‏seed‏ نام سرویس بُر می‌زند. مرحله‌ی دوم شکاف‌ها را گسترش می‌دهد و هر ‏daemon‏ موجود را به نخستین شکاف سازگار نسبت می‌دهد. ‏daemon‏های فعال و رتبه‌دار زودتر جای خود را می‌گیرند تا جابه‌جا نشوند. مرحله‌ی سوم کمبود یا مازاد را جبران می‌کند: هنگام کاهش، ابتدا ‏daemon‏هایی حذف می‌شوند که کنار سرویس مرتبط نیستند (مثلاً ‏ingress‏ بدون ‏backend‏). هنگام افزایش، ابتدا میزبان‌های سرویس مرتبط و سپس بقیه به ترتیب ‏shuffle‏ انتخاب می‌شوند
۱۱-۷تقسیم خودکار ‏NVMe‏ میان ‏DB‏های ‏OSD‏ در دو میزبان نمونه. چپ: ۱۲ ‏HDD‏ و ۲ ‏NVMe‏، شش شکاف هم‌اندازه روی هر ‏NVMe‏. راست: ۱۰ ‏HDD‏ و ۳ ‏NVMe‏، چهار شکاف ۳۷۲.۵ گیگابایتی روی هر ‏NVMe. NVMe‏ سوم فقط دو شکاف پر دارد و دو شکاف هاشورخورده خالی می‌مانند، چون ‏batch‏ پس از رسیدن به تعداد ‏OSD‏ می‌ایستد. اندازه‌ی ۱۴۹۰ ‏GiB‏ برای ‏VG‏ فرضی است
۱۱-۸ترتیب انواع ‏daemon‏ در ارتقای ‏cephadm‏، از روی ‏CEPH_UPGRADE_ORDER‏ در کد ‏main‏. ردیف اول و دوم انواعی هستند که ‏image‏ خود ‏Ceph‏ را دارند. ‏mgr‏ پیش از ‏mon‏ می‌آید، چون خود ‏orchestrator‏ در ‏mgr‏ اجرا می‌شود و باید نسخه‌ی تازه را بشناسد. ‏OSD‏ها دسته‌ای و پس از بررسی ‏ok-to-stop‏ ارتقا می‌یابند. ردیف سوم ‏gateway‏ها و ‏ingress‏ هستند و ردیف آخر پشته‌ی پایش و ‏mgmt-gateway‏ که ‏image‏های جداگانه‌ی خود را دارند
۱۱-۹رشد مجموعه‌ی ‏OSD‏های قابل توقف در شبیه‌سازی ۴ میزبان و ۳ ‏OSD‏ در هر میزبان (‏replicated size 3‏، ‏min_size 2‏). الگوریتم از ‏osd.0‏ شروع می‌کند و ابتدا هم‌میزبان‌های آن را امتحان می‌کند (سبز: پذیرفته). در سطح ‏root‏، افزودن هر ‏OSD‏ از میزبان دیگر تعدادی ‏PG‏ را زیر ‏min_size‏ می‌برد (قرمز، با تعداد ‏PG‏های ناامن در زیر هر ‏OSD‏) و رد می‌شود. چون حداقل یک رد وجود داشت، الگوریتم به سطح بالاتر نمی‌رود و همان یک میزبان را برمی‌گرداند
۱۱-۱۰‏Rook: operator‏ منابع سفارشی (‏CRD‏) را به ‏daemon‏های ‏Ceph‏ تبدیل می‌کند و ‏ceph-csi volume‏ها را در اختیار ‏pod‏ها می‌گذارد
۱۱-۱۱چرخه‌ی تغییر پیشنهادی: هر تغییر ‏spec‏ از بازبینی، ‏dry-run‏ و پنجره‌ی تغییر می‌گذرد و پس از اعمال بررسی می‌شود
۱۲-۱مقایسه‌ی پشته‌ی ‏FileStore‏ و ‏BlueStore: BlueStore‏ فایل‌سیستم واسط و ‏journal‏ جدا را حذف می‌کند
۱۲-۲اجزای درونی ‏BlueStore: allocator‏ برای داده، ‏RocksDB‏ برای فراداده و ‏BlueFS‏ به‌عنوان فایل‌سیستم کوچک ‏RocksDB‏
۱۲-۳دو مسیر نوشتن در ‏BlueStore‏: نوشتن‌های کوچک ابتدا در ‏WAL‏ ثبت و بعداً اعمال می‌شوند؛ نوشتن‌های بزرگ مستقیماً در فضای تازه نوشته می‌شوند
۱۲-۴چیدمان رایج گره ترکیبی: هر ‏NVMe‏ چند ‏LV‏ برای ‏block.db‏ چند ‏OSD HDD‏ فراهم می‌کند
۱۲-۵مهاجرت ‏OSD‏ به ‏OSD‏ به چیدمان جدید: تخلیه یا جایگزینی، پاک‌سازی، ساخت دوباره و ‏backfill‏
۱۲-۶فضای تخصیص‌یافته برای یک ‏blob‏ ‏۶۴ ‏KiB‏ برحسب طول فشرده‌ی آن. چپ: با ‏min_alloc_size‏ برابر ۴‏K‏، پله‌ها تا ۵۶ ‏KiB‏ صرفه‌جویی واقعی می‌دهند و بالاتر از آن ‏blob‏ بدون فشرده‌سازی ذخیره می‌شود. راست: با ۶۴‏K‏، همیشه ۶۴ ‏KiB‏ تخصیص می‌یابد و فشرده‌سازی بی‌اثر است. خط‌چین قرمز آستانه‌ی پذیرش است
۱۲-۷مسیر فشرده‌سازی در نوشتن ‏BlueStore‏: فشرده‌سازی ‏blob‏، محاسبه‌ی ‏want‏، آزمون دومرحله‌ای (پیش و پس از افزودن سرآیند)، پر کردن تا ‏min_alloc‏ و ثبت در ‏statfs‏ که ستون‌های ‏USED COMPR‏ و ‏UNDER COMPR‏ در ‏ceph df‏ از آن ساخته می‌شوند
۱۲-۸نسبت فضای تخصیص‌یافته به اندازه‌ی منطقی شیء برای سه مقدار ‏min_alloc_size‏. برای اشیای بزرگ‌تر از چند ده کیلوبایت اختلاف ناچیز است، اما برای اشیای چندکیلوبایتی مصرف چند ده برابر می‌شود
۱۲-۹اندازه‌ی هدف سطح‌های ‏L1‏ تا ‏L4‏ در ‏RocksDB‏ با گزینه‌های پیش‌فرض ‏BlueStore‏ (ستون‌ها) و مجموع تجمعی آن‌ها (خط قرمز) روی محور لگاریتمی. هر پله‌ی تجمعی اندازه‌ای از ‏block.db‏ است که سطح بعدی را کامل جا می‌دهد. ‏L0‏ و ‏WAL‏ حدود ۱ ‏GiB‏ هر کدام اضافه می‌کنند
۱۲-۱۰پیشوندهای کلید ‏BlueStore‏ در ‏RocksDB‏ و ‏column family‏های پیش‌فرض. هر نوع فراداده با یک پیشوند تک‌حرفی از بقیه جدا می‌شود؛ تنظیم ‏bluestore_rocksdb_cfs‏ برخی از این پیشوندها را در ‏column family‏های جدا و ‏shard‏شده قرار می‌دهد
۱۲-۱۱ساختار فراداده‌ی یک شیء: ‏Onode‏ نگاشت منطقی را در ‏ExtentMap‏ نگه می‌دارد؛ هر ‏Extent‏ به یک ‏Blob‏ اشاره می‌کند که ‏checksum‏ و فشرده‌سازی و محل فیزیکی (‏pextent‏) را دارد؛ ‏Blob‏های مشترک بین ‏clone‏ها با ‏SharedBlob‏ مدیریت می‌شوند
۱۲-۱۲ماشین حالت ‏TransContext‏ در ‏BlueStore‏. ردیف اول: نوشتن داده با ‏aio‏؛ ردیف دوم: ‏commit‏ فراداده در ‏RocksDB‏ توسط ‏kv_sync_thread‏، که در آن ‏OSD‏ تأیید ‏commit‏ را می‌گیرد؛ ردیف سوم: اعمال نوشتن‌های ‏deferred‏ روی دستگاه و پاک کردن رکورد آن‌ها
۱۲-۱۳‏HybridAllocator‏: بازه‌های آزاد ابتدا در درخت ‏AVL‏ نگه داشته می‌شوند که جست‌وجوی سریع دارد؛ وقتی حافظه‌ی مجاز درخت پر شود، بازه‌های اضافی به ‏BitmapAllocator‏ سرریز می‌شوند که حافظه‌ی ثابت و کمی مصرف می‌کند
۱۳-۱کدگذاری ۴+۲: یک ‏stripe‏ شانزده‌کیلوبایتی به چهار ‏shard‏ داده‌ی چهارکیلوبایتی تقسیم و دو ‏shard parity‏ از آن‌ها محاسبه می‌شود؛ هر ‏shard‏ روی یک ‏OSD‏ در دامنه‌ی خرابی جدا قرار می‌گیرد
۱۳-۲چیدمان ‏stripe‏ها: داده‌ی شیء به ‏stripe‏هایی به طول ‏k‏×‏stripe_unit‏ تقسیم می‌شود؛ قطعه‌ی ‏i‏ هر ‏stripe‏ روی ‏shard i‏ قرار می‌گیرد و ‏parity‏ هر ‏stripe‏ جداگانه محاسبه می‌شود
۱۳-۳خواندن تخریب‌شده (‏degraded read‏): وقتی ‏shard 1‏ در دسترس نیست، ‏minimum_to_decode‏ مجموعه‌ای از ‏k shard‏ در دسترس را انتخاب و ‏decode‏ داده‌ی گم‌شده را بازسازی می‌کند
۱۳-۴مقایسه‌ی نوشتن کوچک در ‏EC‏ قدیمی و ‏FastEC‏: پیاده‌سازی قدیمی کل ‏stripe‏ را می‌خواند و دوباره می‌نویسد؛ ‏FastEC‏ فقط ‏shard‏ تغییرکرده و ‏parity‏ها را به‌روز می‌کند
۱۳-۵افزونه‌های ‏EC‏ در ‏Ceph‏: همه رابط مشترک ‏ErasureCodeInterface‏ را پیاده می‌کنند و به‌صورت کتابخانه‌ی پویا بارگذاری می‌شوند
۱۳-۶یک نوشتن در ‏pool‏ از نوع ‏EC. primary‏ (که همیشه ‏shard‏ صفر نیست، ولی در این شکل ‏shard 0‏ فرض شده) تراکنش را به ‏stripe‏ها می‌شکند و تصمیم می‌گیرد آیا ‏RMW‏ لازم است. در صورت نیاز، ‏chunk‏های قدیمی را می‌خواند، ‏parity‏ را محاسبه و برای هر ‏shard‏ یک ‏ECSubWrite‏ شامل ‏chunk‏ آن ‏shard‏ و ورودی ‏pg log‏ می‌فرستد. هر ورودی ‏log‏ یک ‏ObjectModDesc‏ دارد که توضیح می‌دهد این تغییر چگونه قابل بازگشت است. کلاینت فقط پس از ‏commit‏ همه‌ی ‏shard‏های ‏acting‏ پاسخ می‌گیرد
۱۳-۷خواندن و ‏recovery‏ در ‏EC‏. در خواندن، ‏OSD‏ با ‏minimum_to_decode‏ کمترین مجموعه‌ی ‏shard‏های لازم را تعیین می‌کند. با ‏fast_read=false‏ فقط همان‌ها خوانده می‌شوند. با ‏fast_read=true‏ همه‌ی ‏shard‏ها خوانده می‌شوند و با رسیدن اولین ‏k‏ پاسخ ‏decode‏ انجام می‌شود (تأخیر دم کمتر، ترافیک بیشتر). در ‏recovery‏، ‏primary‏ از ‏pg log‏ فهرست اشیای گم‌شده‌ی هر ‏shard‏ را می‌سازد، از ‏k shard‏ سالم (یا کمتر در ‏LRC‏ و ‏CLAY‏) می‌خواند، فقط ‏chunk‏ گم‌شده را ‏decode‏ و به ‏OSD‏ جدید ‏push‏ می‌کند. پایین شکل فرمول ‏min_size‏ پیش‌فرض را نشان می‌دهد
۱۳-۸کدگذاری ‏Reed-Solomon‏ روی یک ستون بایت. چپ: ماتریس ۶×۴ ساخته‌شده با ‏gf_gen_rs_matrix‏، شامل همانی برای داده و دو سطر ‏parity‏. راست: محاسبه‌ی ‏P0‏ و ‏P1‏ برای «‏ABCD‏» و بازگشایی پس از از دست رفتن ‏d1‏ و ‏d3‏. ضرب‌ها در ‏GF‏(۲⁸) با چندجمله‌ای ۰‏x11d‏ انجام شده‌اند
۱۳-۹تعداد الگوهای از دست رفتنِ حداکثر ‏m‏ ‏‏shard‏ که با ماتریس ‏Vandermonde‏ قابل بازگشایی نیستند. سبز یعنی ‏MDS‏ است و قرمز یعنی شکست دارد. بررسی کامل با اسکریپت ما بر پایه‌ی ‏gf_gen_rs_matrix‏ انجام شده است
۱۳-۱۰گام‌های به‌روزرسانی دلتای ‏parity‏ برای تغییر یک بایت در ‏d2: XOR‏ قدیم و جدید، ضرب در ضریب ستون همان ‏shard‏ و ‏XOR‏ در هر ‏parity‏. سطر آخر تطبیق با کدگذاری دوباره‌ی کامل را نشان می‌دهد. خط بنفش شمار ‏shard‏های خواندنی در دو مسیر را برای سه پروفایل مقایسه می‌کند
۱۳-۱۱داده‌ی خوانده‌شده برای بازسازی یک ‏chunk‏ ازدست‌رفته با ‏RS‏، ‏‏LRC‏ و ‏CLAY‏ در دو پیکربندی، همراه سربار خام هر طرح. ‏LRC‏ خواندن را با فضای بیشتر کم می‌کند و ‏CLAY‏ با همان فضای ‏RS‏
۱۳-۱۲مثال کامنت کد برای ‏pool‏ بهینه‌ی ۴+۲: ‏shard 0‏ از دست رفته است؛ ‏pg_temp‏ طوری مرتب می‌شود که ‏shard‏های ‏parity‏ (که همیشه به‌روزند) جلو بیایند و ‏osd.9 primary‏ شود
۱۴-۱جایگاه ‏librados‏: کتابخانه‌های بالادستی و برنامه‌ی شما از طریق ‏IoCtx‏ با ‏librados‏ کار می‌کنند؛ ‏Objecter‏ هدف هر عملیات را محاسبه می‌کند و ‏MonClient‏ نقشه‌ها و احراز هویت را مدیریت می‌کند
۱۴-۲ساختار یک عملیات مرکب: ‏guard‏ها، تغییرها و فراخوانی کلاس در یک تراکنش اتمی روی یک شیء اجرا می‌شوند
۱۴-۳کنترل هم‌زمانی خوش‌بینانه با ‏assert_version‏: کلاینت ‏B‏ که نسخه‌ی قدیمی را دیده، خطا می‌گیرد و پس از خواندن دوباره تلاش می‌کند
۱۴-۴‏watch/notify: watcher‏ها روی یک شیء ثبت‌نام می‌کنند؛ ‏notify‏ به همه می‌رسد و ‏notifier‏ پس از دریافت ‏ack‏ها یا ‏timeout‏ پاسخ تجمیعی می‌گیرد
۱۴-۵ترتیب فیلدها در ‏payload‏ پیام ‏MOSDOp‏ با ‏HEAD_VERSION=9‏ (COMPAT_VERSION=۳). ابتدا ‏pgid‏ و ‏hash‏ شیء، ‏epoch‏ نقشه و ‏flags‏، شناسه‌ی درخواست (‏reqid‏)، ‏client_inc‏ و ‏mtime‏، سپس ‏object_locator‏ و نام شیء، تعداد و ساختار هر ‏op‏، و در پایان اطلاعات ‏snapshot‏، ‏retry_attempt‏ و ‏features‏ می‌آیند. در ‏msgr2‏ همه‌ی این‌ها در ‏segment‏ از نوع ‏front‏ هستند و داده‌ی ورودی همه‌ی ‏op‏ها پشت سر هم در ‏segment data‏ قرار می‌گیرد
۱۴-۶سازوکار ‏backoff‏. وقتی ‏PG‏ در حال ‏peering‏ است یا شیء ‏unfound‏ است، ‏OSD‏ به‌جای نگه داشتن ‏op‏ در صف خودش (که حافظه و ‏thread‏ مصرف می‌کند)، یک ‏MOSDBackoff‏ با عمل ‏BLOCK‏ برای یک بازه‌ی ‏hobject‏ به کلاینت می‌فرستد. کلاینت تأیید می‌کند و ‏op‏های آن بازه را نزد خودش نگه می‌دارد. پس از فعال شدن ‏PG‏، ‏OSD‏ با ‏UNBLOCK‏ آزادسازی را اعلام می‌کند و کلاینت ‏op‏ها را دوباره می‌فرستد
۱۴-۷چپ: تعداد ‏op‏ هم‌زمان مجاز برحسب اندازه‌ی ‏op‏. تا ۱۰۰ ‏KiB‏ سقف ۱۰۲۴ حاکم است و بالاتر از آن ⌊۱۰۰ ‏MiB‏ / ‏s‏⌋. بالاتر از ۱۰۰ ‏MiB‏ فقط یک ‏op‏ در جریان است. راست: سقف ‏throughput‏ یک نمونه‌ی ‏Objecter‏ با قانون ‏Little‏، برای سه تأخیر نمونه. تأخیرها فرضی‌اند و فرمول از کد است
۱۴-۸زمان‌بندی زنده‌بودن ‏watch. ping‏ها هر ۵ ثانیه فرستاده می‌شوند. پس از قطع شبکه در ثانیه‌ی ۱۲، دیگر هیچ ‏ping‏ی نمی‌رسد. ‏OSD‏ حدود ثانیه‌ی ۴۰ ‏watch‏ را حذف می‌کند (۳۰ ثانیه پس از آخرین ‏ping‏). ‏Objecter‏ زودتر، پس از ۱۰ ثانیه، ‏session‏ را ‏laggy‏ می‌داند و نقشه‌ی تازه درخواست می‌کند
۱۴-۹کار هر ‏tick‏ در ‏Objecter‏: شمردن ‏op‏های ‏laggy‏، ‏ping‏ برای ‏watch‏های ثبت‌شده، و در صورت وجود ‏op‏ بی‌خانمان یا ‏session‏ مشکوک، درخواست ‏osdmap‏ تازه از ‏MON‏ و فرستادن ‏MPing‏. پاسخ ‏OSD‏ سیاست ‏lossy‏ دارد، پس ‏ping‏ لازم است تا ریست شدن ‏session‏ کشف شود
۱۴-۱۰‏timeout‏ مؤثر ‏notify‏ برحسب ‏timeout_ms‏ ورودی. صفر به ‏client_notify_timeout‏ (۱۰ ثانیه) می‌رسد. ۱ تا ۹۹۹ میلی‌ثانیه پس از تقسیم صحیح صفر می‌شود و ‏OSD‏ پیش‌فرض ۳۰ ثانیه را به کار می‌برد. از ۱۰۰۰ به بعد، ثانیه‌های کامل اعمال می‌شوند
۱۴-۱۱گام‌های _‏calc_target‏: وجود ‏pool‏، نگاشت شیء به ‏PG‏، محاسبه‌ی ‏up/acting‏ با ‏CRUSH‏ و ‏pg_temp‏ و ‏upmap‏، و تصمیم به ارسال دوباره در صورت تغییر ‏interval‏
۱۵-۱مقایسه‌ی دو روش افزایش شمارنده: در روش سمت کلاینت، دو کلاینت مقدار ۱۰ را می‌خوانند و هر دو ۱۱ می‌نویسند و یک به‌روزرسانی گم می‌شود؛ در روش کلاس ‏numops.add‏، ‏OSD‏ درخواست‌ها را به‌صورت سریال درون ‏PG‏ اجرا می‌کند و مقدار نهایی ۱۳ است
۱۵-۲نقشه‌ی مصرف‌کنندگان کلاس‌های داخلی: ‏librbd‏ برای سرآیند تصویر و ‏object map‏ به ‏cls_rbd‏، ‏RGW‏ برای شاخص ‏bucket‏ به ‏cls_rgw‏ و برای ‏GC‏ و صف به ‏cls_rgw_gc‏ و ‏cls_2pc_queue‏ و ‏cls_fifo‏ تکیه دارد؛ ‏cls_lock‏ و ‏cls_version‏ و ‏cls_refcount‏ عمومی‌اند
۱۵-۳سه مرحله‌ی اعتبارسنجی یک ‏CALL‏. ردیف اول هنگام رسیدن پیام اجرا می‌شود و کلاس و متد را می‌یابد. ردیف دوم پرچم‌های ثبت‌شده‌ی متد را به پرچم‌های ‏op‏ تبدیل می‌کند تا ‏OSDCap‏ بتواند مجوز را بسنجد و ‏PG‏ بداند ‏op‏ خواندنی است یا نوشتنی. ردیف سوم پس از اجرای متد شمارنده‌های خواندن و نوشتن را مقایسه می‌کند. متدی که برخلاف پرچمش رفتار کند، با ‏EIO‏ رد می‌شود
۱۵-۴خط زمانی شش درخواست قفل از دو کلاینت روی یک شیء. نقطه‌های سبز موفق و قرمز ناموفق‌اند. نوارهای سایه‌دار بازه‌ی نگه‌داشتن قفل هستند. تمدید ‏A‏ در ثانیه‌ی ۲۵ انقضا را به ۵۵ می‌برد. چون هیچ‌کس پیش از ثانیه‌ی ۶۰ به قفل دست نمی‌زند، ورودی ‏A‏ تا آن لحظه در ‏xattr‏ می‌ماند و تنها در درخواست ‏B‏ پاک می‌شود
۱۵-۵سه فراخوانی پیاپی ‏cls_cxx_map_get_vals‏ روی ۲۵۰۰ کلید با پیشوند ‏u_‏. هر فراخوانی از آخرین کلید فراخوانی قبلی ادامه می‌دهد (‏UPPER_BOUND‏، یعنی کلید بعد از ‏start_after‏). خط قرمز سقف ۱۰۲۴ کلید در هر درخواست است که روی ‏max_to_get‏ اعمال می‌شود. پرچم ‏more‏ فقط در صفحه‌ی آخر ‏false‏ است
۱۵-۶چپ: مقدار نهایی شمارنده پس از ۳۰۰ افزایش در هر رشته، با ‏cls_numops‏ (سبز) و با خواندن ‏omap‏، افزودن یک و نوشتن از سمت کلاینت (قرمز). خط سیاه مقدار مورد انتظار است. راست: میانه‌ی زمان هر افزایش روی کلاستر آزمایشی ‏Ceph 18.2.7‏
۱۵-۷چپ: میانه‌ی زمان شمردن خط‌ها در شیءهای ۶۴ ‏KiB‏ تا ۱۶ ‏MiB‏، با ‏cls_lua‏ درون ‏OSD‏ (بنفش) و با خواندن کل شیء در کلاینت (آبی) روی ‏loopback‏. خط‌چین‌ها همان زمان کلاینت را به‌علاوه‌ی زمان انتقال روی ۱۰ و ۱ گیگابیت نشان می‌دهند (محاسبه‌شده). راست: پهنای باند سربه‌سر که زیر آن اجرای ‏Lua‏ زودتر تمام می‌شود
۱۵-۸مسیر کامل یک فراخوانی کلاس در سه ردیف: کلاینت با ‏Objecter‏ درخواست ‏CEPH_OSD_OP_CALL‏ را به ‏primary‏ می‌فرستد؛ ‏PrimaryLogPG::do_osd_ops‏ نام کلاس و متد را ‏decode‏ و با ‏ClassHandler‏ کلاس را باز می‌کند؛ سپس متد اجرا و تعداد خواندن و نوشتن با پرچم‌های اعلام‌شده مقایسه می‌شود و تغییرات در تراکنش ‏PG‏ ثبت می‌شود
۱۵-۹دو مرحله‌ی بارگذاری کلاس در ‏ClassHandler‏: بالا، نخستین فراخوانی با بررسی ‏osd_class_load_list‏ و ‏dlopen‏ فایل ‏libcls_X.so‏ از ‏osd_class_dir‏ و اجرای __‏cls_init‏؛ پایین، ثبت متدها با نام و پرچم و تعیین وضعیت ‏allowed‏ بر اساس ‏osd_class_default_list‏ که روی تفسیر ‏cap‏های ‏class-read‏ و ‏class-write‏ اثر دارد
۱۶-۱معماری ‏cache tiering‏ در سه ردیف: کلاینت درخواست ‏pool‏ پایه را به دلیل ‏overlay‏ به ‏pool‏ کش می‌فرستد؛ ‏OSD‏ کش در ‏maybe_handle_cache_detail‏ بررسی می‌کند شیء موجود است یا با توجه به ‏HitSet‏ باید ‏promote‏ شود؛ عامل کش در پس‌زمینه اشیای ‏dirty‏ را ‏flush‏ و اشیای ‏clean‏ سرد را ‏evict‏ می‌کند
۱۶-۲ماشین حالت عامل کش: سه حالت ‏flush‏ (IDLE، ‏LOW‏، ‏HIGH‏) بر اساس نسبت اشیای ‏dirty‏ به هدف و سه حالت ‏evict‏ (IDLE، ‏SOME‏، ‏FULL‏) بر اساس پری؛ هیسترزیس با ‏osd_agent_slop‏ از نوسان سریع بین حالت‌ها جلوگیری می‌کند چون آستانه‌ی ورود کمی بالاتر و آستانه‌ی خروج کمی پایین‌تر از هدف است
۱۶-۳روال حذف ‏cache tier‏ حالت ‏writeback‏: تغییر به ‏proxy‏ برای توقف ‏promote‏، تخلیه با ‏cache-flush-evict-all‏ و تکرار تا خالی شدن ‏pool‏ کش، سپس حذف ‏overlay‏ و جدا کردن ‏tier‏
۱۶-۴شیء ‏S3‏ ده‌مگابایتی در ‏RADOS‏. شیء ‏HEAD‏ فراداده (‏manifest‏، ‏ACL‏، ‏etag‏ و سایر ‏xattr‏ها) و در ‏storage class‏ پیش‌فرض، اولین تکه‌ی داده (تا ‏rgw_max_chunk_size=4M‏) را به‌صورت ‏inline‏ نگه می‌دارد. بقیه‌ی داده در اشیای ‏tail‏ با اندازه‌ی ‏rgw_obj_stripe_size=4M‏ قرار دارد. پس از ‏transition‏ به یک ‏storage class‏ دیگر، ‏HEAD‏ در ‏pool‏ پیش‌فرض می‌ماند ولی ‏manifest‏ آن به ‏tail‏های تازه در ‏data_pool‏ کلاس جدید اشاره می‌کند و داده‌ی قدیمی به ‏garbage collection‏ سپرده می‌شود. پایین شکل ‏pool‏های یک ‏placement target‏ را نشان می‌دهد
۱۶-۵اجرای ‏lifecycle‏ در ‏RGW‏. فهرست ‏bucket‏های دارای قاعده در ‏rgw_lc_max_objs=32‏ شیء ‏RADOS‏ (shardهای ‏lc‏) نگه داشته می‌شود. فقط در پنجره‌ی ‏rgw_lifecycle_work_time‏ (پیش‌فرض ۰۰:۰۰ تا ۰۶:۰۰ به وقت محلی) کار انجام می‌شود. هر ‏shard‏ با قفلی با عمر ‏rgw_lc_lock_max_time=90‏ ثانیه برداشته می‌شود. ‏rgw_lc_max_worker=3 worker‏ موازی، هرکدام با ۱۲۸ ‏coroutine‏ (rgw_lc_max_wp_worker)، ‏bucket index‏ را پیمایش و ‏expire‏، ‏transition‏ یا ‏abort multipart‏ را اجرا می‌کنند
۱۶-۶اندازه‌ی فیلتر ‏Bloom‏ یک ‏HitSet‏ برحسب تعداد اشیای یکتای مجموعه‌ی قبلی، برای دو مقدار ‏hit_set_period‏. اندازه‌ی هدف به نسبت دوره‌ی تازه به مدت واقعی مجموعه‌ی قبلی مقیاس می‌شود و میان کف و سقف ‏OSD‏ بریده می‌شود. خط‌چین‌ها همین دو حد هستند. هر ‏PG‏ کش در هر دوره یک فیلتر می‌سازد و آن را به‌صورت شیء در ‏namespace‏ جداگانه‌ی همان ‏pool‏ بایگانی می‌کند
۱۶-۷شرط ‏evict‏ در حالت ‏SOME‏ برای هیستوگرام ده‌نمونه‌ای مثال. محور افقی ‏bin‏ توانی دما و ستون‌ها تعداد نمونه‌ها در هر ‏bin‏ هستند. سمت چپ تصمیم کد ‏main‏ است، با شرط ۱۰⁶ − ‏upper‏ ≥ ‏effort‏ برای نگه داشتن: فقط ‏bin‏ سرد صفر می‌ماند (سبز) و همه‌ی ‏bin‏های داغ بیرون می‌روند (قرمز). سمت راست تصمیم با اصلاح پیشنهادی ‏ceph-devel‏ در ۲۰۱۷ است، با شرط ‏lower‏ ≥ ‏effort‏ برای نگه داشتن: سردها بیرون می‌روند و داغ‌ها می‌مانند. خط‌چین در هر دو نیمه مرز تصمیم روی محور سهم تجمعی است: در کد ‏main‏ مرز ‏upper‏ = ۰.۶ (یعنی ۱ − ۰.۴) و در اصلاح پیشنهادی مرز ‏lower‏ = ۰.۴ است
۱۶-۸سمت چپ جدول درجه‌ی ‏HitSet‏های بایگانی‌شده برای دو نرخ کاهش است: با ‏decay 20‏ وزن قدیمی‌ترین مجموعه هنوز ۴۱٪ است و با ‏decay 50‏ فقط ۶٪. سمت راست دمای چهار شیء نمونه است: حضور در مجموعه‌ی جاری همیشه یک میلیون واحد می‌گیرد و با ‏search_last_n‏ = ۱ فقط نخستین مجموعه‌ی بایگانی که شیء در آن پیدا شود، سهمش را اضافه می‌کند
۱۶-۹‏evict_effort‏ برحسب نسبت پری برای دو حالت هیسترزیس. منحنی آبی وقتی است که عامل بی‌کار بوده و آستانه‌ی ورود ۰.۸۱۶ است. منحنی قرمز وقتی است که عامل در حال بیرون کردن است و آستانه به ۰.۷۸۴ پایین می‌آید. پله‌ها ناشی از کوانتش ۰.۱ هستند و بالای پری ۱۰۰٪ تلاش یکباره به ۱.۰ می‌پرد
۱۶-۱۰مسیر یک قانون ‏lifecycle‏ از ثبت تا اجرا. کلید ‏bucket‏ با ‏hash‏ ساده‌ی لینوکسی به یکی از ۳۲ شیء ‏lc.N‏ نگاشت می‌شود. در پنجره‌ی کاری (پیش‌فرض ۰۰:۰۰ تا ۰۶:۰۰ به وقت محلی سرور)، هر کارگر شاردها را به ترتیب تصادفی قفل می‌کند و ‏bucket‏ بعدی را برمی‌دارد. سپس اشیای ‏bucket‏ را با استخری از ‏coroutine‏ها بررسی می‌کند و برای هر نسخه انقضا یا انتقال را اعمال می‌کند
۱۶-۱۱خط زمانی انقضای شیئی با ‏Days‏ = ۳۰. شیء در میانه‌ی روز ۰ نوشته شده است. ‏obj_has_expired‏ زمان مبنا را به نیمه‌شب گرد می‌کند، پس اجرای روز ۳۰ فاصله‌ی ۲۹.۴ روز می‌بیند و شیء را نگه می‌دارد. نخستین اجرایی که حذف می‌کند، در پنجره‌ی روز ۳۱ است
۱۶-۱۲فضای ‏DB‏ که سیاست پیش‌فرض ‏use_some_extra‏ برای فایل‌های سطح‌های بالاتر ‏RocksDB‏ مجاز می‌داند، برحسب اندازه‌ی دستگاه ‏DB‏. منحنی دندانه‌ای است: پس از هر مرز سطح، فرمول دو برابر اندازه‌ی سطح جاری را رزرو می‌کند و فضای اضافه به صفر می‌افتد. نقطه‌های قرمز چهار اندازه‌ی مثال متن‌اند. خط‌چین سقف ۹۵ درصدی دستگاه است
۱۶-۱۳سه سطح لایه‌بندی امروزی در ‏Ceph‏: در سطح ‏pool‏ با ‏device class‏ و قانون‌های ‏CRUSH‏ جدا برای ‏nvme‏ و ‏hdd‏، درون ‏OSD‏ با قرار دادن ‏block.db‏ و ‏WAL‏ روی ‏NVMe‏ کنار داده‌ی ‏HDD‏، و در سطح شیء با ‏storage class‏ و قانون ‏lifecycle‏ در ‏RGW‏ که اشیا را پس از مدتی به ‏pool‏ ظرفیتی یا فضای ابری منتقل می‌کند
۱۷-۱روش عیب‌یابی لایه‌ای: از خلاصه‌ی ‏ceph‏ -‏s‏ و ‏ceph health detail‏ شروع کنید، لایه‌ی درگیر (‏MON‏، ‏OSD‏، ‏PG‏ یا کارایی) را تشخیص دهید، با فرمان‌های همان لایه شواهد جمع کنید و سپس با چرخه‌ی فرضیه، آزمون و اصلاح کوچک پیش بروید و همه‌چیز را در لاگ حادثه ثبت کنید
۱۷-۲ماشین حالت ‏Peering‏ در سه ردیف: مسیر اصلی ‏GetInfo‏، ‏GetLog‏، ‏GetMissing‏ و ‏WaitUpThru‏ زیر ‏Started/Primary/Peering‏؛ شاخه‌های شکست ‏Down‏، ‏Incomplete‏ و ‏WaitActingChange‏؛ و پس از فعال‌سازی، مسیر ‏Active‏ شامل رزرو و ‏Recovering‏، رزرو و ‏Backfilling‏، و در نهایت ‏Recovered‏ و ‏Clean‏
۱۷-۳اجرای ‏find_best_info‏ روی چهار ‏info‏ نمونه. ردیف قرمز به خاطر ‏last_epoch_started‏ قدیمی‌تر حذف می‌شود. از میان دو ردیف با جدیدترین ‏last_update‏، آنکه ‏log_tail‏ کوچک‌تر و در نتیجه ‏log‏ بلندتری دارد (سبز) برنده است. ترجیح ‏primary‏ فعلی فقط وقتی اعمال می‌شود که همه‌ی معیارهای قبلی برابر باشند. در ‏pool EC‏ جهت مقایسه‌ی ‏last_update‏ برعکس است، چون نسخه‌های جلوتر باید ‏rollback‏ شوند
۱۷-۴خط زمانی یک ‏op‏ که ۴۰۰ ثانیه گیر مانده است. از ثانیه‌ی ۳۰ به بعد در شمار ‏op‏های کند است. اما خط لاگ «‏slow request‏» فقط وقتی نوشته می‌شود که سن ‏op‏ از ۳۰ × ‏multiplier‏ بگذرد و پس از هر لاگ ضریب دو برابر می‌شود. پس بازه‌ی لاگ‌ها ۳۰، ۶۰ و ۱۲۰ ثانیه است و لاگ ‏OSD‏ در یک رخداد طولانی سیل‌آسا نمی‌شود
۱۷-۵توزیع زمان ‏scrub‏ بعدی با پیش‌فرض‌ها، از شبیه‌سازی همان فرمول‌های کد. سمت چپ ‏scrub‏ سطحی است: توزیع یکنواخت در یک تا یک‌ونیم روز. سمت راست ‏scrub‏ عمیق است: توزیع نرمال حول هفت روز با انحراف معیار ۱.۴ روز که در ±۲σ بریده شده است. دو ستون بلند لبه‌ها، نمونه‌های بریده‌شده هستند. ناحیه‌ی زرد بازه‌ی ۰.۸ تا ۱.۲ برابر است
۱۷-۶حالت ۳۲ ‏PG‏ در ‏pool‏ ‏‏t17‏ (size ۲) روی کلاستر آزمایشی سه‌‏OSD‏ی ‏BlueStore‏ با ‏Ceph 18.2.7‏، نمونه‌برداری‌شده هر حدود یک ثانیه. خط‌چین‌ها رخدادها را نشان می‌دهند: ‏out‏ و ‏in‏ کردن ‏osd.0‏، و توقف و راه‌اندازی دوباره‌ی ‏osd.1‏. سبز ‏active+clean‏ است، آبی ‏recovery‏، زرد ‏peering‏، خاکستری ‏stale‏ و قرمز ‏undersized+degraded‏
۱۷-۷چپ: درصد ‏PG‏های ‏degraded‏، ‏inactive‏ و بی‌نسخه برای از کار افتادن ۱، ۲ یا ۳ میزبان از ۱۰، با شمارش دقیق روی همه‌ی ترکیب‌ها از نگاشت ‏osdmaptool‏ (ستون میانگین است و خط‌ها کمینه تا بیشینه). راست: احتمال اینکه کلاینتی که ‏N‏ شیء را لمس می‌کند در وضعیت دو میزبان ازکارافتاده به ‏PG‏ ‏‏inactive‏ برخورد کند
۱۷-۸چرخه‌ی عمر یک عملیات در ‏OSD‏ در سه ردیف: ورود و قرار گرفتن در صف شارد و رسیدن به ‏PG‏؛ انتظارهای رایج که با رویدادهای ‏waiting for‏ ثبت می‌شوند؛ و اجرا، ارسال زیرعملیات به ‏replica‏ها، دریافت ‏ack‏ و ارسال پاسخ. آخرین رویداد ثبت‌شده‌ی یک عملیات کند نشان می‌دهد کجا گیر کرده است
۱۷-۹ماشین حالت ‏scrub‏ در سه بخش: ‏primary‏ پس از رزرو ‏replica‏ها (محدود به ‏osd_max_scrubs‏) اشیا را تکه‌به‌تکه انتخاب، منتظر نوشتن‌های در جریان می‌ماند، نقشه‌ی محلی می‌سازد و نقشه‌ی ‏replica‏ها را دریافت می‌کند؛ ‏replica‏ نقشه‌ی خود را می‌سازد و می‌فرستد؛ ‏scrub_backend‏ نسخه‌ی معتبر را انتخاب و ناسازگاری‌ها را در ‏ScrubStore‏ ثبت می‌کند
۱۸-۱چهار لایه‌ی دفاعی در برابر فاجعه: درون کلاستر با تکثیر و ‏recovery‏ خودکار، بین دو سایت با یک کلاستر کشیده (‏stretch mode‏) و ‏RPO‏ صفر، بین کلاسترهای مستقل با تکثیر ناهم‌زمان ‏rbd-mirror‏، ‏RGW multisite‏ و ‏cephfs-mirror‏، و در نهایت نسخه‌ی پشتیبان مستقل با ‏object lock‏ و پشتیبان پیکربندی
۱۸-۲یک دور همگام‌سازی ‏snapshot-based‏: سایت ‏A‏ طبق زمان‌بندی ‏snapshot‏ اولیه‌ی ‏S2‏ می‌سازد؛ ‏rbd-mirror‏ در سایت ‏B snapshot‏های دو طرف را اسکن می‌کند، ‏snapshot‏ ناقص ‏S2'‏ را در مقصد می‌سازد، تفاوت ‏S1‏ تا ‏S2‏ را با ‏deep-copy‏ منتقل می‌کند، وضعیت تصویر را اعمال و ‏snapshot‏ را کامل علامت می‌زند و در پایان پیوند ‏peer‏ را از ‏S1‏ برمی‌دارد تا قابل حذف شود
۱۸-۳دو سناریوی ‏failover‏: در حالت برنامه‌ریزی‌شده برنامه متوقف، تصویر در ‏A demote‏، پس از همگام شدن در ‏B promote‏ و برنامه در ‏B‏ راه‌اندازی می‌شود و هیچ داده‌ای از دست نمی‌رود؛ در حالت اضطراری ‏B‏ با ‏promote‏ --‏force‏ فعال می‌شود و پس از بازگشت ‏A‏، تصویر ‏A demote‏ و با ‏resync‏ از ‏B‏ دوباره همگام می‌شود
۱۸-۴چیدمان ‏journal‏ یک تصویر ‏RBD‏. سرآیند در ‏omap‏ شیء ‏journal.<id>‏ قرار دارد. داده در ‏set‏هایی از ‏splay_width‏ شیء است و هر ‏entry‏ بر اساس ‏entry_tid‏ % ‏splay_width‏ در یکی از اشیای ‏set‏ فعال ‏append‏ می‌شود. وقتی یکی از اشیا به اندازه‌ی ‏2order2^{\mathit{order}}‏ برسد، کل ‏set‏ بسته و ‏set‏ بعدی فعال می‌شود. ‏set‏هایی که همه‌ی ‏client‏ها از آن‌ها گذشته‌اند حذف می‌شوند
۱۸-۵سه حالت ‏stretch mode‏ و مقادیر ‏pg_pool_t‏ در هرکدام (منطق تابع‌های ‏trigger_degraded_stretch_mode‏، ‏trigger_recovery_stretch_mode‏ و ‏trigger_healthy_stretch_mode‏ در ‏OSDMonitor.cc‏ نسخه‌ی ‏Squid‏). مقادیر ‏min_size‏ برای ‏pool‏ پیش‌فرض ‏stretch‏ با ‏size=4‏ است
۱۸-۶امتیاز یک همتا در ‏ConnectionTracker‏. قرمز: اتصالی که از امتیاز کامل قطع می‌شود و پس از ۱۶.۶ ساعت به صفر می‌رسد. سبز: بازگشت از صفر. شیب هر دو با نیمه‌عمر ۴۳۲۰۰ ثانیه تعیین می‌شود. نقطه‌ها مقدارهای محاسبه‌شده با گام‌های ۲ ثانیه‌ای هستند
۱۸-۷امتیاز انتخاباتی پنج ‏MON‏ در سناریوی قطع پیوند ‏a‏ با ‏DC2‏. چپ: هنگام قطعی، ‏b‏ با امتیاز ۴ برنده است. راست: پس از وصل شدن، تاریخچه‌ی امتیاز ‏a‏ هنوز آسیب‌دیده است و ‏b‏، که در تساوی با ‏c‏ و ‏d rank‏ پایین‌تری دارد، رهبر می‌ماند. ‏e‏ به‌عنوان ‏tiebreaker‏ امتیاز −۱ دارد
۱۸-۸منطق رسیدگی به پیشنهاد در راهبرد ‏connectivity‏. ردیف اول نادیده گرفتن پیشنهاد ‏MON‏ ‏‏down‏ از سوی ‏tiebreaker‏ و محاسبه‌ی سه امتیاز است. ردیف دوم تصمیم بین پیشنهاد خود و ‏ACK‏ به دیگری است. ردیف سوم دو قاعده‌ی جانبی را نشان می‌دهد: امتیاز −۱ برای رهبران ممنوع و حاشیه‌ی ۰.۰۰۰۵ برای جلوگیری از رأی‌گیری نوسانی
۱۸-۹توزیع ۱۰۰ تصویر بین ‏daemon‏های ‏rbd-mirror‏ با ‏SimplePolicy‏ در پنج رویداد. ستون سوم اثر ‏migration_throttle‏ را نشان می‌دهد: تصویرهای تازه‌منتقل‌شده‌ی ‏B‏ تا ۳۰۰ ثانیه قابل جابه‌جایی نیستند و ‏C‏ فقط ۱۷ تصویر می‌گیرد. ستون چهارم فقط وقتی رخ می‌دهد که ‏rebalance_timeout‏ غیرصفر باشد. شبیه‌سازی ما بر پایه‌ی ‏SimplePolicy.cc‏
۱۸-۱۰توپولوژی ‏stretch mode‏: دو ‏datacenter‏ هر کدام با دو ‏MON‏ و ‏OSD‏هایی که دو نسخه از هر ‏PG‏ را نگه می‌دارند، و یک ‏MON‏ داور بدون داده در سایت سوم؛ هر نوشتن پیش از ‏ack‏ در هر دو سایت ثبت می‌شود و داور در صورت قطع لینک بین دو سایت، سایت برنده را مشخص می‌کند
۱۸-۱۱روال بازسازی ‏store‏ مانیتور از روی ‏OSD‏ها در دو مرحله: ابتدا روی هر میزبان ‏OSD‏ با ‏OSD‏های متوقف، ‏ceph-objectstore-tool‏ با عملیات ‏update-mon-db‏ نقشه‌ها را در یک پوشه‌ی ‏store‏ مشترک جمع می‌کند و این پوشه از میزبانی به میزبان دیگر منتقل می‌شود؛ سپس ‏ceph-monstore-tool‏ با کلید ‏admin‏ و شناسه‌ی ‏MON‏ها ‏store‏ را بازسازی می‌کند و ‏store.db‏ روی ‏MON‏ها جایگزین می‌شود
۱۹-۱روال ارتقا در سه مرحله: آمادگی شامل سالم بودن کلاستر، خواندن یادداشت‌های انتشار و اطمینان از دسترسی میزبان‌ها به ‏image‏؛ ترتیب ‏cephadm‏ از ‏mgr‏ و ‏mon‏ تا ‏OSD‏ها با بررسی ‏ok-to-stop‏ و سپس سایر ‏daemon‏ها؛ و پایان با یکسان بودن نسخه‌ها و بررسی ‏require_osd_release‏
۱۹-۲جایگزینی دیسک خراب با حفظ شناسه‌ی ‏OSD‏ در دو مرحله: خارج کردن با ‏orch osd rm‏ --‏replace‏ که ‏PG‏ها را تخلیه و ‏OSD‏ را ‏destroyed‏ علامت می‌زند، و جایگزینی که پس از تعویض فیزیکی و در صورت نیاز ‏zap‏، مشخصات سرویس ‏OSD‏ دیسک جدید را می‌گیرد و ‏OSD‏ با همان شناسه ساخته و ‏backfill‏ می‌شود
۱۹-۳مراحل محاسبه‌ی ‏pg_autoscaler‏: ابتدا بودجه‌ی ‏PG‏ هر ریشه‌ی ‏CRUSH‏ از تعداد ‏OSD‏ها ضرب در ‏mon_target_pg_per_osd‏، سپس سهم هر ‏pool‏ از مصرف واقعی یا نسبت هدف و تبدیل آن به ‏pg_target‏ با در نظر گرفتن ‏bias‏ و ‏size‏، و در نهایت گرد کردن به توان ۲ و تغییر فقط وقتی تفاوت از آستانه بیشتر باشد
۱۹-۴عملکرد ‏balancer‏ در حالت ‏upmap‏: ماژول ‏mgr‏ در هر بازه‌ی ‏sleep_interval‏ (پیش‌فرض ۶۰ ثانیه) و در صورت آرام بودن کلاستر ‏do_upmap‏ را اجرا می‌کند؛ ‏OSDMap::calc_pg_upmaps‏ تعداد ‏PG‏ هر ‏OSD‏ را با مقدار هدف وزن‌دار مقایسه و برای ‏OSD‏های با انحراف بیش از ‏max_deviation‏، ورودی‌های ‏pg_upmap_items‏ با رعایت قانون ‏CRUSH‏ می‌سازد؛ نتیجه ‏backfill‏ فقط برای ‏PG‏های منتخب است
۱۹-۵سمت چپ تابع پله‌ای ‏nearest_power_of_two‏ است: هدف هر ‏pool‏ به نزدیک‌ترین توان دو گرد می‌شود و مرز تصمیم (نقطه‌چین نارنجی) ۱.۵ برابر توان پایین‌تر است. نقطه‌ی قرمز مثال متن است. سمت راست قاعده‌ی آستانه با ‏threshold‏ = ۳ و ‏pg_num‏ فعلی ۳۲ است: فقط مقادیر سبز، یعنی بیش از ۹۶ یا کمتر از ۱۰.۷، تغییر واقعی ایجاد می‌کنند
۱۹-۶چهار انتخاب ممکن برای گرد کردن دو ‏pool‏ در مثال متن. ارتفاع هر ستون مجموع فاصله‌ی مقادیر گردشده از هدف است. گزینه‌ی خاکستری از بودجه‌ی ۴۲۴ نسخه‌ی ‏PG‏ اضافه بیشتر است و حذف می‌شود. گزینه‌ی سبز کمترین هزینه را دارد و انتخاب می‌شود. برنامه‌ریزی پویا به‌جای امتحان همه‌ی ۲ⁿ ترکیب، فقط مجموع‌های ممکن ‏PG‏ اضافه را نگه می‌دارد
۱۹-۷انحراف تعداد ‏PG‏ هر ‏OSD‏ از هدف وزن‌دارش در مثال متن، پیش از ‏upmap‏ و پس از هر ‏upmap‏. خط‌چین‌ها مرز ‏upmap_max_deviation‏ = ۵ هستند. هر ‏upmap‏ یک واحد از پرترین ‏OSD‏ کم و به کم‌بارترین اضافه می‌کند. امتیاز ‏bal_score‏ که مجموع وزن‌دار مربع انحراف نسبی است، با هر حرکت کمتر می‌شود. ‏OSD‏‌های وزن ۲ با انحراف ±۴ دست نخورده می‌مانند، چون درون مرزند
۱۹-۸توزیع تعداد ‏PG‏ روی ۱۲۰ ‏OSD‏ پیش از ‏upmap‏ (نارنجی، فقط ‏CRUSH‏) و پس از ‏upmap‏ (آبی)، برای ۱۰۲۴ و ۸۱۹۲ ‏PG‏، با ‏osdmaptool‏ --‏upmap‏ و انحراف مجاز ۱. عنوان هر نمودار تعداد ورودی‌های ‏pg-upmap-items‏ و سهم ‏replica‏های جابه‌جاشده را نشان می‌دهد
۱۹-۹چپ: پس از دو برابر شدن ‏pg_num‏ و ‏pgp_num‏، چند درصد ‏PG‏های فرزند روی ۰، ۱، ۲ یا ۳ ‏OSD‏ تازه می‌نشینند، و سهم کل داده‌ی جابه‌جاشده (حدود ۴۸.۷٪)، اندازه‌گیری‌شده با ‏osdmaptool‏ روی ۱۲۰ ‏OSD‏. راست: مدل کنترل‌گر ‏adjust_pgs‏ در ‏mgr‏ برای ۱۰۲۴ به ۲۰۴۸، با سقف فاصله‌ی ۱۲۸ بین ‏pg_num‏ و ‏pgp_num‏ و گام ‏pgp‏ برابر ‏pg_num‏ × ‏min‏(۰.۰۵ − ‏misplaced, 0.025‏)، با فرض پایان ‏backfill‏ پیش از هر دور
۲۰-۱معماری پشته‌ی پایش در سه ردیف: روی هر میزبان ‏ceph-exporter‏ شمارنده‌های عملکرد ‏daemon‏ها را از ‏admin socket‏ می‌خواند و ‏node-exporter‏ متریک‌های سیستم‌عامل را منتشر می‌کند؛ ‏mgr‏ فعال با ماژول ‏prometheus‏ متریک‌های سطح کلاستر و نقطه‌ی ‏service discovery‏ را ارائه می‌دهد؛ ‏Prometheus‏ همه را ‏scrape‏ و قواعد هشدار را ارزیابی می‌کند، ‏Alertmanager‏ هشدارها را مسیریابی و ‏Grafana‏ داشبوردها را نمایش می‌دهد
۲۰-۲مسیر یک شمارنده‌ی عملکرد از تولید تا مصرف: ‏OSD‏ شمارنده‌ای مثل ‏op_w_latency‏ را با ‏PerfCountersBuilder‏ ثبت می‌کند، ‏admin socket‏ آن را با فرمان‌های ‏counter dump‏ و ‏counter schema‏ عرضه می‌کند، ‏ceph-exporter‏ آن را با برچسب ‏ceph_daemon‏ به فرمت ‏Prometheus‏ تبدیل می‌کند و ‏Prometheus‏ آن را ذخیره و با ‏PromQL‏ در قواعد هشدار و پنل‌ها به کار می‌برد
۲۰-۳چرخه‌ی هشدار در سه مرحله: سیگنال‌ها از ‏ceph_health_detail‏ و متریک‌های وضعیت و تأخیر؛ قاعده در ‏Prometheus‏ با ‏expr‏ و مدت پایداری ‏for‏، برچسب ‏severity‏ و توضیح همراه لینک ‏runbook‏؛ و تحویل در ‏Alertmanager‏ با گروه‌بندی بر اساس ‏alertname‏ و ‏cluster‏، ‏inhibit‏ برای خاموش کردن هشدارهای فرعی و ارسال به ‏on-call‏
۲۰-۴پروتکل ‏admin socket‏. یک اتصال ‏UNIX‏ برای هر فرمان باز می‌شود. درخواست رشته‌ی ‏JSON‏ است که با ۰‏x00‏ یا ۰‏x0A‏ تمام می‌شود. پاسخ با یک طول ۴ بایتی ‏big-endian‏ و سپس ‏payload‏ می‌آید. اگر اولین بایت درخواست ۰‏x00‏ باشد، پروتکل قدیمی ۴ بایتی فرض می‌شود (کد ۱ یعنی ‏perfcounters_dump‏ و کد ۲ یعنی ‏perfcounters_schema‏)
۲۰-۵دو مسیر رسیدن یک شمارنده به ‏Prometheus‏. مسیر ‏mgr‏ از ‏MgrClient‏ و پیام ‏MMgrReport‏ (هر ‏mgr_stats_period=5‏ ثانیه) استفاده می‌کند و ماژول ‏prometheus‏ روی پورت ۹۲۸۳ آن را منتشر می‌کند. مسیر ‏ceph-exporter‏ روی هر میزبان مستقیماً از ‏admin socket‏ هر ‏daemon‏ می‌خواند (‏counter schema‏ و ‏counter dump‏ هر ‏exporter_stats_period=5‏ ثانیه) و روی پورت ۹۹۲۶ منتشر می‌کند. شمارنده‌های ‏LONGRUNAVG‏ به دو سری _‏sum‏ و _‏count‏ تبدیل می‌شوند
۲۰-۶سمت چپ: نمای هیستوگرام دوبعدی نوشتن برای یک بار مصنوعی با سه اندازه‌ی درخواست (۴ ‏KiB‏، ۶۴ ‏KiB‏ و ۴ ‏MiB‏) و توزیع لگ‌نرمال تأخیر. رنگ لگاریتم تعداد است. هر سطر یک اندازه‌ی درخواست و هر ستون یک بازه‌ی تأخیر است. سمت راست: محدوده‌ی ‏bucket‏های محور تأخیر روی محور لگاریتمی. هر ‏bucket‏ دو برابر قبلی است و سه مقدار نمونه (۱۵۰ µ‏s‏، ۱.۵ ‏ms‏ و ۳۰ ‏ms‏) روی آن نشان داده شده‌اند. عددهای سمت چپ از اندازه‌گیری نیامده‌اند و فقط شکل داده را نشان می‌دهند
۲۰-۷خط زمانی ‏cache‏ ماژول ‏prometheus‏ با ‏scrape_interval‏ ‏۱۵ ثانیه. ردیف بالا مدت هر ‏collect‏() است. جمع‌آوری‌های نارنجی بیش از ۱۵ ثانیه طول کشیده‌اند. دو ردیف پایین پاسخ ‏scrape‏های ‏Prometheus‏ را در دو سیاست نشان می‌دهند. در ‏fail‏ (که مقدار پیش‌فرض ‏log‏ هم به آن تبدیل می‌شود)، ‏scrape‏هایی که آخرین جمع‌آوری کامل‌شده‌شان کند بوده است ۵۰۳ می‌گیرند. در ‏return‏ همان ‏scrape‏ها داده‌ی کهنه با کد ۲۰۰ می‌گیرند. مدت‌ها فرضی هستند و برای نشان دادن منطق انتخاب شده‌اند
۲۰-۸بالا: نمونه‌های ‏ceph_osd_up‏ هر ۱۵ ثانیه برای یک ‏OSD‏ که در پنج دقیقه‌ی آخر هفت بار ‏down‏ و ‏up‏ می‌شود. ناحیه‌ی زرد پنجره‌ی ۵ دقیقه‌ای ارزیابی در ثانیه‌ی ۵۸۵ است. پایین: مقدار ‏rate‏(ceph_osd_up[۵m]) × ۶۰ در هر ارزیابی، با پیاده‌سازی تقریبی برون‌یابی ‏Prometheus‏. منحنی با هر ‏flap‏ یک پله بالا می‌رود و پس از هفتمین ‏flap‏ از خط‌چین آستانه‌ی ۱ می‌گذرد
۲۰-۹تعداد ‏PG‏ ده ‏OSD‏ در یک کلاس دستگاه، با میانگین ۱۰۵ (خط سیاه) و باند ±۳۰٪ (سبز). درصد بالای هر ستون انحراف آن ‏OSD‏ از میانگین است. فقط ‏osd.9‏ با ۳۴.۳٪ بیرون از باند است (قرمز) و اگر پنج دقیقه همین‌طور بماند هشدار ‏CephPGImbalance‏ فعال می‌شود. عددها مثال هستند
۲۰-۱۰نمونه‌های مصنوعی ‏ceph_pool_percent_used‏ در ۴۸ ساعت (نقطه‌های آبی)، با رشد خطی از ۰.۷۰ به ۰.۷۸ و کمی نویز. خط‌چین قرمز خط کمترین مربعات روی همین پنجره است که ۵ روز به جلو امتداد یافته. پیش‌بینی در آن نقطه ۰.۹۸۷ است که از خط ۰.۹۵ می‌گذرد، پس هشدار فعال می‌شود. خط عمودی نقطه‌ی ارزیابی را نشان می‌دهد
۲۰-۱۱‏mgmt-gateway‏ به‌عنوان ورودی واحد: مرورگر کاربر به ‏gateway‏ مبتنی بر ‏nginx‏ با ‏TLS‏ و نشانی مجازی وصل می‌شود که در صورت فعال بودن احراز هویت، کاربر را از ‏oauth2-proxy‏ و ‏IdP‏ بیرونی عبور می‌دهد؛ پشت ‏gateway‏، ‏Dashboard‏، ‏Grafana‏، ‏Prometheus‏ و ‏Alertmanager‏ قرار دارند و دیگر مستقیماً در معرض شبکه نیستند
۲۱-۱چرخه‌ی تنظیم کارایی: تعریف هدف قابل اندازه‌گیری، گرفتن خط پایه با ابزارهای سنجش، اعمال تنها یک تغییر، سنجش دوباره با همان بار و همان مدت، و ثبت تغییر در صورت بهبود یا بازگشت با ‏ceph config reset‏ در غیر این صورت
۲۱-۲چهار لایه‌ی کارایی از پایین به بالا: سخت‌افزار و شبکه، سیستم‌عامل، تنظیمات ‏OSD‏ شامل حافظه و شاردها و ‏mClock‏، و در بالاترین سطح طراحی ‏pool‏ و تنظیمات کلاینت؛ بیشترین اثر معمولاً از لایه‌های پایین‌تر می‌آید
۲۱-۳مسیر درخواست درون ‏OSD: Messenger‏ پیام را دریافت می‌کند و بر اساس ‏hash‏ شناسه‌ی ‏PG‏ یکی از شاردها انتخاب می‌شود؛ هر شارد صف ‏mClock‏ خود را دارد و ‏thread‏های همان شارد درخواست را با گرفتن قفل ‏PG‏ در ‏PrimaryLogPG::do_op‏ اجرا و تراکنش را به ‏BlueStore‏ می‌سپارند
۲۱-۴الگوریتم ‏dmClock‏ در سه بخش: هر درخواست بر اساس پارامترهای کلاس خود سه برچسب ‏R‏، ‏P‏ و ‏L‏ می‌گیرد؛ زمان‌بند ابتدا در مرحله‌ی رزرو درخواستی را اجرا می‌کند که برچسب ‏R‏ آن سررسید شده، و در غیر این صورت میان درخواست‌هایی که از سقف خود عبور نکرده‌اند کمترین برچسب ‏P‏ را انتخاب می‌کند؛ درخواست‌های ‏immediate‏ و پراولویت مانند ‏peering‏ پیش از صف ‏mClock‏ پردازش می‌شوند
۲۱-۵حلقه‌ی کنترل حافظه‌ی ‏OSD‏ با مقادیر پیش‌فرض. مرزهای ‏min‏ و ‏max‏ از ‏target‏، ‏fragmentation‏ و ‏base‏ محاسبه می‌شوند. ‏tune_memory‏ هر ثانیه بودجه را بر اساس فاصله‌ی حافظه‌ی ‏mapped‏ از ‏target‏ کم یا زیاد می‌کند. بودجه‌ی حاصل هر ۵ ثانیه بر اساس اولویت بین ‏cache‏های ‏RocksDB‏، ‏onode‏ و داده تقسیم می‌شود
۲۱-۶محاسبه‌ی هزینه‌ی تراکنش و ظرفیت هم‌زمانی ‏BlueStore‏ برای نوشتن‌های ۴‏K‏. روی ‏HDD‏ حدود ۵۰ تراکنش مستقیم یا ۱۰۰ تراکنش ‏deferred‏ در صف جا می‌شود و روی ‏SSD‏ بیش از ۵۵۰۰. صف کوتاه در ‏BlueStore‏ باعث می‌شود درخواست‌ها بیشتر در صف ‏mClock‏ منتظر بمانند، جایی که زمان‌بند هنوز می‌تواند بین کلاینت و بازیابی اولویت‌بندی کند
۲۱-۷سمت چپ: تعداد ‏PG‏های یک ‏OSD SSD‏ نمونه در هر یک از هشت شارد صف، با ۱۰۰ ‏PG‏ و ‏ps‏ تصادفی. خط‌چین میانگین ۱۲.۵ است. سمت راست: توزیع نسبت پرترین شارد به میانگین در ۲۰۰۰ ‏OSD‏ شبیه‌سازی‌شده. حتی وقتی بار هر ‏PG‏ برابر است، معمولاً یک شارد ۳۰ تا ۷۰ درصد بیش از میانگین ‏PG‏ دارد. این عددها شبیه‌سازی هستند و فرض می‌کنند ‏ps‏ هر ‏PG‏ تصادفی و مستقل است
۲۱-۸تقسیم حافظه‌ی یک میزبان ۲۵۶ گیگابایتی با ‏MemoryAutotuner‏. نوار بالا کل حافظه است. ۳۰ درصد هاشورخورده برای سیستم‌عامل و سربار ‏RSS‏ کنار گذاشته می‌شود. نوار پایین سهم ‏daemon‏های غیر ‏OSD‏ (با حداقل‌های جدول ‏min_size_by_type‏ یا مصرف فعلی، هر کدام بیشتر باشد) و سپس ۲۰ سهم برابر ‏OSD‏ها را نشان می‌دهد. مصرف ‏mon‏ و ‏RGW‏ در این مثال فرضی است
۲۱-۹پنج نوشتن نمونه روی یک ‏OSD HDD‏ و این‌که _‏do_write_data‏ هر کدام را چگونه تقسیم می‌کند. شبکه‌ی خاکستری بلوک‌های ۴ ‏KiB‏ است. زرد بخشی است که به مسیر ‏small‏ می‌رود. قرمز بخشی از مسیر ‏big‏ است که چون کمتر از ۶۴ ‏KiB‏ است ‏deferred‏ می‌شود. آبی نوشتن مستقیم است. سطر آخر حالت مرزی ۴ ‏KiB‏ هم‌تراز را نشان می‌دهد که با وجود قرار گرفتن در یک بلوک، به‌خاطر شرط ‏length‏ ≠ ‏min_alloc_size‏ به مسیر ‏big‏ می‌رود
۲۱-۱۰خروجی شبیه‌سازی ‏dmClock‏ برای سه کلاینت با رزرو، وزن و حد متفاوت. سمت چپ، ظرفیت ۱۰۰۰ درخواست در ثانیه: آبی سرویس در فاز ‏reservation‏ و نارنجی سرویس در فاز ‏weight‏ است. ‏client‏ فقط رزرو خود را می‌گیرد، ‏recovery‏ به حد ۲۰۰ می‌رسد و ‏scrub‏ بقیه را می‌گیرد. سمت راست، ظرفیت ۳۵۰ درخواست در ثانیه که از مجموع رزروها کمتر است: رزروها به نسبت ۳ به ۱ کوچک می‌شوند و ‏scrub‏ تقریباً صفر می‌گیرد. ‏cost‏ هر درخواست ۱ فرض شده است
۲۱-۱۱منطق دو مرحله‌ای ‏dmClock‏. هنگام رسیدن هر درخواست به سر صف کلاینت، سه برچسب ‏R‏، ‏P‏ و ‏L‏ از برچسب قبلی همان کلاینت و زمان رسیدن ساخته می‌شوند. در هر فراخوانی ‏do_next_request‏، ابتدا رزروهای سررسیدشده سرویس می‌گیرند. اگر رزروی سررسید نشده بود، از میان کلاینت‌هایی که از حدشان عبور نکرده‌اند، کمینه‌ی ‏P‏ انتخاب می‌شود و برچسب ‏R‏ آن کلاینت به عقب برمی‌گردد. ‏OSD‏ با ‏AtLimit::Wait‏ ساخته می‌شود، یعنی اگر همه به حد رسیده باشند، هیچ درخواستی بیرون نمی‌آید
۲۲-۱نقشه‌ی پوشه‌های اصلی ‏src/‏ در شاخه‌ی ‏main‏. ردیف اول کد پایه‌ی مشترک است: انواع پایه و ‏encoding‏ در ‏include‏، زیرساخت ‏config‏ و ‏log‏ و ‏perf counter‏ و ‏thread‏ در ‏common‏، لایه‌ی شبکه در ‏msg/async‏، احراز هویت ‏cephx‏ در ‏auth‏ و الگوریتم ‏CRUSH‏ در ‏crush‏. ردیف دوم کتابخانه‌های کلاینت است: ‏Objecter‏ و ‏Striper‏ در ‏osdc‏، ‏librados‏، ‏librbd‏، کلاینت ‏CephFS‏ در ‏client‏ و کلاس‌های سمت ‏OSD‏ در ‏cls‏ و ‏objclass‏. ردیف سوم ‏daemon‏ها است: ‏mon‏، ‏osd‏، ‏os‏ (ObjectStore و ‏BlueStore‏)، ‏mds‏ و ‏mgr‏. ردیف چهارم بقیه است: ‏rgw‏، ‏crimson‏ (OSD نسل بعد روی ‏Seastar‏)، افزونه‌های ‏erasure-code‏، ابزارهای خط فرمان در ‏tools‏ و کد پایتون در ‏pybind‏ و ‏python-common‏
۲۲-۲روابط کلاس‌های اصلی در مسیر داده. در سمت کلاینت، ‏RadosClient‏ مالک یک ‏Objecter‏ است. ‏Objecter‏ برای هر ‏OSD‏ یک ‏OSDSession‏ نگه می‌دارد و از طریق ‏AsyncMessenger‏ اتصال (‏AsyncConnection‏ با ‏ProtocolV2‏) برقرار می‌کند. در سمت ‏OSD‏، کلاس ‏OSD‏ که ‏Dispatcher‏ پیام‌ها است ‏N‏ شیء ‏OSDShard‏ دارد که هر کدام ‏scheduler‏ خودش را دارد (پیش‌فرض ‏mClockScheduler‏). هر ‏PG‏ بر اساس ‏hash‏ به یک شارد نگاشت می‌شود. ‏PrimaryLogPG‏، ‏PG‏ را گسترش می‌دهد و ‏PeeringState‏ و ‏PGLog‏ را دارد، و از طریق ‏PGBackend‏ (پیاده‌سازی‌های ‏ReplicatedBackend‏ یا ‏ECBackend‏) تکثیر را انجام می‌دهد. در لایه‌ی ذخیره‌سازی، هر تغییر یک ‏ObjectStore::Transaction‏ است که ‏BlueStore‏ آن را با ‏Collection‏، ‏Onode‏ و ‏TransContext‏ روی ‏RocksDB‏، ‏BlueFS‏ و ‏Allocator‏ اجرا می‌کند
۲۲-۳مدل ‏thread‏ در ‏OSD‏ کلاسیک. ردیف اول شبکه است: ‏ms_async_op_threads‏ (پیش‌فرض ۳) ‏Worker‏ دارد که هر کدام یک ‏EventCenter‏ مبتنی بر ‏epoll‏ اجرا می‌کند. پیام‌های داده در همان ‏thread‏ شبکه از طریق ‏ms_fast_dispatch‏ تحویل می‌شوند تا هزینه‌ی صف و ‏context switch‏ کم شود. ردیف دوم پردازش ‏op‏ است: ‏op‏ بر اساس ‏hash‏ شناسه‌ی ‏PG‏ در ‏scheduler‏ یکی از ‏OSDShard‏ها صف می‌شود و ‏ShardedThreadPool‏ برای هر شارد ‏threads_per_shard thread‏ دارد که ‏op‏ را برمی‌دارند، قفل ‏PG‏ را می‌گیرند و ‏do_request‏ را اجرا می‌کنند. ردیف سوم ابزارهای کمکی است: ‏Finisher‏ برای اجرای ‏callback‏ها در ‏thread‏ جدا، ‏SafeTimer‏ برای رویدادهای زمان‌دار مثل ‏tick‏ و ‏heartbeat‏، و دو ‏thread‏ اصلی ‏BlueStore‏ یعنی ‏kv_sync_thread‏ و ‏kv_finalize_thread‏
۲۲-۴نمودار توالی یک نوشتن ‏replicated‏ از ‏librados‏ تا ‏ack. Objecter‏ هدف را محاسبه می‌کند و ‏MOSDOp‏ را روی ‏msgr2‏ به ‏primary‏ می‌فرستد. در ‏primary‏، پیام در ‏ms_fast_dispatch‏ دریافت و در شارد مربوط به ‏PG‏ صف می‌شود. ‏thread‏ کاری آن را برمی‌دارد و از مسیر ‏do_request‏ و ‏do_op‏ به ‏execute_ctx‏ می‌رساند، و در آنجا ‏prepare_transaction‏ با ‏do_osd_ops‏ عملیات را به یک ‏ObjectStore::Transaction‏ تبدیل می‌کند. ‏issue_repop‏ تراکنش را از طریق ‏backend‏ به ‏replica‏ها می‌فرستد و هم‌زمان تراکنش محلی را به ‏BlueStore‏ می‌دهد. وقتی همه‌ی اعضای ‏acting commit‏ کردند، ‏eval_repop‏ پاسخ را به کلاینت می‌فرستد
۲۲-۵چرخه‌ی یک ‏TransContext‏ در ‏BlueStore. queue_transactions‏ برای هر تراکنش یک ‏TransContext‏ می‌سازد و _‏txc_add_transaction‏ هر عملیات را اجرا می‌کند؛ نوشتن‌ها از _‏write‏ به _‏do_write‏ می‌رسند. بسته به هم‌ترازی و اندازه، یکی از _‏do_write_small‏ یا _‏do_write_big‏ انتخاب می‌شود و _‏do_alloc_write‏ فضا را از ‏Allocator‏ می‌گیرد و ‏checksum‏ و فشرده‌سازی را اعمال می‌کند. داده یا مستقیم با ‏aio‏ نوشته می‌شود (‏AIO_WAIT‏ و سپس ‏IO_DONE‏)، یا اگر کوچک‌تر از ‏prefer_deferred_size‏ باشد (روی ‏HDD 64K‏ و روی ‏SSD‏ صفر یعنی هرگز)، همراه فراداده در ‏WAL‏ ذخیره می‌شود. سپس ‏kv_sync_thread‏ چند تراکنش را در یک ‏batch RocksDB‏ با یک ‏fsync commit‏ می‌کند و ‏on_commit‏ فراخوانی می‌شود. در مسیر ‏deferred‏، نوشتن واقعی روی دستگاه بعداً و در پس‌زمینه انجام می‌شود
۲۲-۶مسیر نوشتن ‏RBD‏ در ‏librbd/io‏. درخواست از ‏api::Io‏ وارد ‏ImageDispatcher‏ می‌شود که لایه‌هایی مثل ‏QoS‏، ‏exclusive lock‏، ‏journal‏ و کش را به ترتیب اجرا می‌کند. ‏AbstractImageWriteRequest::send_request‏ با ‏Striper‏ بازه‌ی تصویر را به اشیای ‏RADOS‏ تقسیم می‌کند. هر شیء از ‏ObjectDispatcher::send_dispatch‏ و ‏ObjectDispatch::write‏ عبور می‌کند و ‏AbstractObjectWriteRequest‏ به ترتیب ‏object map‏ را به‌روز می‌کند، شیء را می‌نویسد و اگر در ‏clone‏ شیء وجود نداشته باشد ‏copyup‏ اجرا می‌کند (فصل ۳)
۲۲-۷مسیر ‏PUT‏ در ‏RGW. frontend‏ مبتنی بر ‏beast‏ درخواست ‏HTTP‏ را به ‏process_request‏ می‌دهد و پس از احراز هویت، ‏rgw_process_authenticated‏ برای ‏op‏ به ترتیب ‏verify_permission‏، ‏pre_exec‏ و ‏execute‏ را اجرا می‌کند. ‏RGWPutObj::execute‏ داده را از طریق ‏Writer‏ در لایه‌ی انتزاعی ‏rgw::sal‏ به ‏AtomicObjectProcessor‏ در ‏driver rados‏ می‌دهد که با ‏RadosWriter::process head‏ و ‏stripe‏های ‏tail‏ را می‌نویسد. در پایان _‏do_write_meta‏ فراداده را می‌نویسد و ‏index bucket‏ را با ‏UpdateIndex::prepare‏ و ‏complete‏ (متدهای ‏cls_rgw‏، فصل ۴) به‌روز می‌کند
۲۲-۸مسیر ‏CephFS‏. در ردیف اول، ‏open‏ یک درخواست فراداده است: ‏Client::_open‏ یک ‏MClientRequest‏ می‌فرستد، ‏MDS‏ در ‏Server::handle_client_open‏ قفل‌های لازم را با ‏Locker::acquire_locks‏ می‌گیرد و همراه پاسخ ‏cap‏های مناسب را صادر می‌کند. در ردیف دوم، ‏write‏ مسیر داده است: ‏Client::_write‏ با ‏get_caps‏ حداقل ‏Fw‏ را لازم دارد و ‏Fb‏ را ترجیح می‌دهد. اگر ‏Fb‏ داشته باشد، داده در ‏ObjectCacher‏ بافر می‌شود و در غیر این صورت هم‌گام نوشته می‌شود. داده با ‏Striper‏ و ‏Objecter‏ مستقیم به ‏OSD‏ها می‌رود و ‏MDS‏ در مسیر داده قرار ندارد
۲۲-۹میانه (آبی تیره) و صدک ۹۰ (آبی روشن) زمان بین رویدادهای پیاپی ‏OpTracker‏ برای ۳۰۰ نوشتن ۴ ‏KiB‏ روی کلاستر آزمایشی ‏Ceph 18.2.7‏، روی محور ‏symlog‏. چپ: ‏osd_op‏ روی ‏primary‏، فقط ترتیب غالب. راست: ‏osd_repop‏ روی ‏replica‏
۲۲-۱۰خط زمانی میانه‌ی یک نوشتن ‏replicated‏ چهار کیلوبایتی. بالا ‏op‏ روی ‏primary‏ است و پایین ‏op‏ روی ‏replica‏ که به لحظه‌ی ارسال ‏subop‏ در ‏primary‏ منتقل شده است. ‏replica‏ کار خود را حدود ۱۱۷۰ میکروثانیه پس از شروع تمام می‌کند، ولی ‏primary‏ تأیید آن را حدود ۱۶۰۰ میکروثانیه می‌بیند. اختلاف هزینه‌ی مسیر بازگشت در ‏messenger‏ و صف است
۲۳-۱مسیر یک نوشتن از ‏BlueStore‏ تا دیسک. در فضای کاربر، ‏BlueStore‏ یک ‏bufferlist‏ هم‌تراز با بلوک می‌سازد و از طریق ‏IOContext‏ فهرستی از ‏aio_t‏ (offset، ‏iovec‏ و ‏write hint‏) جمع می‌کند. ‏KernelDevice::aio_submit‏ آن‌ها را دسته‌ای به صف ‏aio‏ می‌دهد. در مرز ‏syscall‏، ‏libaio‏ از ‏io_submit‏ و ‏io_uring‏ از ‏io_uring_prep_writev‏ و ‏io_uring_submit‏ استفاده می‌کند، روی ‏fd‏ی که با ‏O_RDWR‏ و ‏O_DIRECT‏ باز شده است (برای هر ‏write hint‏ یک ‏fd‏ جدا). در هسته، ‏bio‏ به ‏request‏ در ‏blk-mq‏ تبدیل و درایور ‏NVMe‏ یا ‏SCSI‏ داده را با ‏DMA‏ مستقیم از صفحه‌های حافظه‌ی کاربر منتقل می‌کند. تکمیل از طریق ‏io_getevents‏ یا ‏CQE‏ به _‏aio_thread‏ می‌رسد و او ‏aio_callback‏ را صدا می‌زند
۲۳-۲توالی یک دسته ‏I/O. thread‏ تراکنش فقط ‏aio_write‏ را در ‏IOContext‏ ثبت می‌کند و سپس ‏aio_submit‏ همه را دسته‌ای (تا عمق ۱۰۲۴) ارسال می‌کند. ‏thread‏ مستقل _‏aio_thread‏ در حلقه‌ای با مهلت ۲۵۰ میلی‌ثانیه حداکثر ۱۶ تکمیل را برمی‌دارد و شمارنده‌ی ‏num_running‏ هر ‏IOContext‏ را کم می‌کند. وقتی این شمارنده به صفر برسد، ‏aio_callback‏ با ‏priv‏ همان تراکنش فراخوانی می‌شود و ‏BlueStore‏ آن را از ‏AIO_WAIT‏ به ‏IO_DONE‏ و سپس ‏KV_QUEUED‏ می‌برد
۲۳-۳سه لایه‌ی ‏bufferlist‏. در بالا، ‏list‏ یک فهرست پیوندی از ‏ptr_node‏ها در _‏buffers‏ نگه می‌دارد، به‌همراه _‏carriage‏ (اشاره به آخرین ‏ptr‏ که ‏append‏ سریع روی آن انجام می‌شود)، _‏len‏ (کل بایت‌ها) و _‏num‏ (تعداد ‏ptr‏ها). در میانه، هر ‏ptr‏ فقط یک پنجره است: اشاره‌گر به یک ‏raw‏ همراه _‏off‏ و _‏len‏. در پایین، ‏raw‏ مالک حافظه‌ی واقعی است و ‏data‏، ‏len‏، شمارنده‌ی ارجاع اتمیک ‏nref‏، شناسه‌ی ‏mempool‏ و کش آخرین ‏crc‏ را نگه می‌دارد. چند ‏ptr‏ در چند ‏list‏ می‌توانند به یک ‏raw‏ اشاره کنند؛ کپی یک ‏bufferlist‏ فقط ‏nref‏ را افزایش می‌دهد
۲۳-۴زنجیره‌ی انتخاب پیاده‌سازی ‏crc32c‏ و محل‌های استفاده. ترتیب بررسی چنین است: روی ‏x86‏ ابتدا ‏AVX512‏ همراه ‏VPCLMULQDQ‏، سپس ‏SSE4.2‏ (با یا بدون ‏PCLMUL‏ برای پردازش موازی سه جریان)، روی ‏aarch64‏ دستورهای ‏CRC32‏ و روی ‏ppc‏، ‏s390x‏ و ‏riscv‏ نسخه‌های مخصوص. اگر هیچ‌کدام در دسترس نباشد، پیاده‌سازی جدولی نرم‌افزاری ‏sctp‏ استفاده می‌شود. این تابع در ‏BlueStore‏ برای هر ‏chunk‏ داده، در ‏msgr2‏ حالت ‏crc‏ برای هر ‏segment‏ و ‏preamble‏، و در ‏bufferlist‏ با کش ‏crc‏ روی ‏raw‏ به کار می‌رود
۲۳-۵ساختار ‏iocb‏ شصت و چهار بایتی که ‏aio_t‏ در ‏Ceph‏ برای هر ‏I/O‏ پر می‌کند. نمونه یک ‏pwritev‏ با سه ‏iovec‏ و در مجموع ۱۲ ‏KiB‏ در آفست ۰‏x5A000‏ است. ‏aio_data‏ اشاره‌گر خود ‏aio_t‏ است و در ‏io_event‏ برمی‌گردد. با این اشاره‌گر، ‏thread‏ برداشت بدون جست‌وجو می‌فهمد کدام ‏IOContext‏ را باید کامل کند. در فرمان‌های برداری، ‏aio_nbytes‏ تعداد ‏iovec‏هاست، نه طول بایتی
۲۳-۶انتظار پیش از هر تلاش دوباره (میله‌ها، مقیاس لگاریتمی) و انتظار تجمعی (خط قرمز) وقتی ‏io_submit‏ پیوسته ‏EAGAIN‏ برمی‌گرداند. پس از ۸ تلاش فقط ۳۲ میلی‌ثانیه گذشته است، اما ۸ تلاش آخر بیش از ۸ ثانیه طول می‌کشند. پس یک اشباع کوتاه تقریباً نامرئی است و فقط اشباع طولانی به ‏slow op‏ تبدیل می‌شود
۲۳-۷ورودی صف ارسال ‏io_uring‏ که ‏init_sqe‏ در ‏Ceph‏ برای همان ‏pwritev‏ می‌سازد. ‏opcode‏ برابر ‏IORING_OP_WRITEV‏ (۲) است. پرچم ‏IOSQE_FIXED_FILE‏ یعنی ‏fd‏ اندیسی در جدول فایل‌های ثبت‌شده است و هسته هزینه‌ی ‏fget/fput‏ را در هر ‏I/O‏ نمی‌پردازد. ‏user_data‏ همان اشاره‌گر ‏aio_t‏ است و در ‏CQE‏ شانزده بایتی برمی‌گردد
۲۳-۸مسیر کامل ‏io_uring‏ در ‏KernelDevice‏. ارسال زیر ‏sq_mutex‏ انجام می‌شود، چون چند ‏thread‏ (تراکنش، ‏deferred‏، ‏BlueFS‏) هم‌زمان ‏I/O‏ می‌فرستند. برداشت در ‏thread aio_thread‏ زیر ‏cq_mutex‏ است. وقتی ‏CQE‏ آماده نیست، به جای مسدود شدن در ‏io_uring_enter‏، روی ‏ring_fd‏ با ‏epoll‏ منتظر می‌ماند، و این همان الگوی زمان‌بندی ‏bdev_aio_poll_ms‏ در ‏libaio‏ را حفظ می‌کند
۲۳-۹اندازه‌ی ده بافر ‏raw_combined‏ که ‏refill_append_space‏ هنگام ‏append‏ کردن یک مگابایت داده در قطعه‌های کوچک می‌سازد. اندازه‌ها از ۴‏K‏ دوبرابر می‌شوند تا به سقف ۲۵۶‏K‏ برسند. ‏S‏ اندازه‌ی سرآیند ‏raw_combined‏ است که از هر تخصیص کم می‌شود. فضای بلااستفاده‌ی انتهای بافر آخر دلیل اختلاف ۱۲۷۶ ‏KiB‏ تخصیص و ۱۰۲۴ ‏KiB‏ داده است
۲۳-۱۰سه کاربرد ماتریس‌های جهش ‏crc32c‏ در ‏Ceph‏. ردیف بالا الگوریتم ‏ceph_crc32c_zeros‏ است که طول را به توان‌های دو می‌شکند. ردیف میانی مثال ۴۰۹۶ بایت صفر است که با یک ضرب ماتریس حل می‌شود. ردیف پایین تطبیق ‏crc‏ کش‌شده در ‏bufferlist‏ است: به دلیل خطی بودن ‏CRC‏، تفاوت دو ‏seed‏ معادل ‏crc‏ یک ناحیه‌ی صفر با ‏seed‏ برابر ‏XOR‏ آن دو است
۲۴-۱چیدمان ابتدای دستگاه‌های ‏BlueStore‏. در دستگاه ‏block‏ با ‏DB‏ هم‌دستگاه، بلوک ۰ ‏label‏، بلوک ۱ ‏superblock‏ مربوط به ‏BlueFS‏ و بقیه فضای مشترک داده و ‏BlueFS‏ است. ‏label‏ در موقعیت‌های ۱، ۱۰، ۱۰۰ و ۱۰۰۰ گیگابایت هم تکرار می‌شود (فقط اگر دستگاه به آن اندازه برسد). در دستگاه ‏block.db‏ جدا، ‏extent‏های ‏BlueFS‏ از ۸‏K‏ (SUPER_RESERVED) شروع می‌شوند
۲۴-۲ساختار ‏BlueFS. superblock‏ در بلوک ۱ قرار دارد و به ‏fnode‏ فایل لاگ (‏ino 1‏) اشاره می‌کند. لاگ دنباله‌ای از تراکنش‌هاست که ‏op‏هایشان (ساخت دایرکتوری، ‏link‏، به‌روزرسانی کامل یا افزایشی ‏fnode‏، حذف، پرش ‏seq‏) کل فراداده را می‌سازند. هر ‏fnode‏ فهرستی از ‏extent‏ها {‏bdev‏، ‏offset‏، ‏length}‏ دارد. وقتی لاگ بیش از ۱۶ مگابایت و ۵ برابر اندازه‌ی تخمینی شود، فشرده‌سازی آغاز می‌شود
۲۴-۳ساختار ‏onode‏. کلید ‏onode‏ به ترتیبی کدگذاری می‌شود که مرتب‌سازی ‏lexicographic‏ آن با ترتیب ‏ghobject_t‏ یکسان باشد: ‏shard‏ + ۰‏x80‏، سپس ‏pool‏ + ۲⁶³، ‏hash‏ بیت‌معکوس، ‏namespace‏ و نام ‏escape‏شده، ‏snap‏، ‏generation‏ و در پایان کاراکتر '‏o'‏. مقدار، ‏bluestore_onode_t‏ است. ‏lextent‏ها به ‏blob‏ها و ‏blob‏ها به ‏pextent‏های فیزیکی اشاره می‌کنند. ‏extent map‏های بزرگ به ‏shard‏هایی جدا با کلید «‏onode‏ + ‏u32 offset‏ + ‏x‏» شکسته می‌شوند
۲۴-۴‏allocator‏ها و ‏freelist. allocator‏ پیش‌فرض ‏hybrid‏ است: فضای خالی در یک ‏AVL‏ (درخت ‏offset‏ و درخت ‏size‏) نگه داشته می‌شود و اگر حافظه‌ی آن از ‏bluestore_hybrid_alloc_mem_cap‏ (۶۴M) بیشتر شود، قطعه‌های کوچک به یک ‏Bitmap‏ سرریز می‌کنند. ‏freelist‏ پایدار، در صورت استفاده، در پیشوند ‏b‏ قرار دارد: هر کلید ۱۲۸ واحد تخصیص را با ۱۲۸ بیت پوشش می‌دهد و تغییرات با ‏merge operator‏ از نوع ‏bitwise_xor‏ اعمال می‌شوند. در حالت ‏allocation from file‏ (پیش‌فرض)، ‏allocator‏ هنگام ‏shutdown‏ تمیز در یک فایل ‏BlueFS‏ ذخیره و پس از ‏crash‏ از روی ‏onode‏ها بازسازی می‌شود
۲۴-۵کش ‏BlueStore‏. کش ‏buffer‏ به‌طور پیش‌فرض الگوریتم ۲‏Q‏ را دارد: داده‌ی تازه به ‏warm_in‏ می‌رود. با بیرون رانده شدن، فقط سرآیند آن در ‏warm_out‏ می‌ماند. اگر همان بازه دوباره خوانده شود، به ‏hot‏ منتقل می‌شود. این کار از بیرون راندن داده‌های پرکاربرد توسط یک اسکن ترتیبی جلوگیری می‌کند. ‏autotune‏ هر ۵ ثانیه حافظه را بر اساس ‏osd_memory_target‏ میان ‏kv‏، ‏meta‏ و ‏data‏ تقسیم می‌کند
۲۴-۶سه نمای یک ناحیه‌ی ۳ مگابایتی از دستگاه. سبز بازه‌ی آزاد است و هاشور بخش تخصیص‌یافته. در نمای میانی، ‏near-fit‏ با ‏cursor‏ مخصوص اندازه‌ی ۶۴‏K‏ از ابتدای ناحیه جلو می‌رود، بازه‌ی ۳۲‏K‏ را رد می‌کند و ابتدای بازه‌ی ۱۲۸‏K‏ را برمی‌دارد. در نمای پایین، ‏best-fit‏ درخواست ۳۲‏K‏ را دقیقاً در کوچک‌ترین بازه‌ی کافی می‌گذارد تا بازه‌های بزرگ برای درخواست‌های بزرگ بمانند
۲۴-۷امتیاز تکه‌تکه شدن برای یک گیگابایت فضای آزاد که به تکه‌های هم‌اندازه تقسیم شده است. محور افقی لگاریتمی است. منحنی میان ۱ مگابایت و ۱۲۸ مگابایت سریع پایین می‌آید، چون ضریب ارزش هر دو برابر شدن طول در این بازه از ۱.۲ به ۱ کاهش می‌یابد. خط‌چین آبی ترکیب نمونه‌ی متن است که با وجود هزاران تکه‌ی کوچک امتیاز ۰.۲۶ دارد
۲۴-۸دو مسیر ‏hybrid allocator‏. ردیف بالا درج یک بازه‌ی آزاد است: تا سقف حافظه بازه در دو درخت ‏AVL‏ درج می‌شود و پس از آن کوچک‌ترین بازه‌ها به ‏bitmap‏ رانده می‌شوند. ردیف پایین تخصیص است: درخواست‌های کوچک‌تر از کوچک‌ترین بازه‌ی ‏AVL‏ اول از ‏bitmap‏ پاسخ می‌گیرند. این کار حافظه‌ی ‏OSD‏ را محدود نگه می‌دارد، بدون اینکه بازه‌های بزرگ گم شوند
۲۴-۹۵۸۸ بایت نخست ‏label‏ واقعی ‏osd.0‏ روی کلاستر آزمایشی ‏Ceph 18.2.7‏، به رنگ میدان‌ها: متن جادویی و ‏uuid‏ متنی (۶۰ بایت)، سرآیند ‏ENCODE_START‏ با نسخه و طول، ‏uuid‏ دودویی، ‏size‏، ‏btime‏، ‏description‏، ‏meta‏ چهارده‌جفتی و ‏crc32c‏ پایانی که با محاسبه‌ی مستقل وارسی شد
۲۴-۱۰نقشه‌ی واقعی فضای آزاد ‏osd.0‏ در سه لحظه. چپ: تعداد ‏extent‏های آزاد در هر بازه‌ی اندازه (محور لگاریتمی). راست: مجموع بایت آزاد در هر بازه. ده‌ها هزار حفره‌ی ۴ ‏KiB‏ تقریباً هیچ سهمی از بایت‌ها ندارند و به همین دلیل ‏fragmentation_rating‏ پایین می‌ماند
۲۵-۱مسیرهای نوشتن و خواندن در ‏RocksDB‏. در مسیر نوشتن، ‏WriteBatch‏ ابتدا در ‏WAL‏ (روی دستگاه ‏WAL‏ یا ‏DB‏ از طریق ‏BlueFS‏) و سپس در ‏memtable‏ فعال هر ‏column family‏ ثبت می‌شود. در پس‌زمینه، ‏memtable‏های پر تغییرناپذیر می‌شوند، پس از جمع شدن حداقل ۶ عدد با هم ادغام و به ‏L0 flush‏ می‌شوند و ‏compaction‏ آن‌ها را به سطوح پایین‌تر می‌برد. خواندن به ترتیب ‏memtable‏، ‏immutable‏ها، همه‌ی فایل‌های ‏L0‏ و سپس از هر سطح دیگر حداکثر یک فایل (با کمک ‏bloom filter‏) را بررسی می‌کند
۲۵-۲ساختار یک فایل ‏SST‏ از نوع ‏block-based‏. داده در بلوک‌های حدود ۴‏K‏ (rocksdb_block_size) با فشرده‌سازی پیشوند کلید و نقاط ‏restart‏ ذخیره می‌شود. پس از داده‌ها بلوک ‏filter‏ (bloom با ۲۰ بیت برای هر کلید در ‏Ceph‏)، ‏index‏ (آخرین کلید هر بلوک و ‏BlockHandle‏ آن)، ‏properties‏ و ‏metaindex‏ قرار می‌گیرند. ‏footer‏ در انتها ‏BlockHandle‏های ‏metaindex‏ و ‏index‏، ‏format_version‏ و عدد جادویی ۰‏x88e241b785f4cff7‏ را دارد. هر بلوک یک ‏trailer‏ پنج‌بایتی شامل نوع فشرده‌سازی و ‏checksum‏ دارد
۲۵-۳اندازه‌ی هدف سطوح با پیش‌فرض‌های ‏Ceph‏ و جای‌گذاری آن‌ها روی دستگاه‌ها. ‏L1‏ برابر ۱‏G‏ است و هر سطح ۸ برابر سطح قبلی، پس ‏L2‏ حدود ۸‏G‏، ‏L3‏ حدود ۶۴‏G‏ و ‏L4‏ حدود ۵۱۲‏G‏ است. سیاست ‏use_some_extra‏ سطوحی را که کامل جا می‌شوند روی ‏block.db‏ قرار می‌دهد، فضای اضافه را برای بخشی از سطح بعدی به کار می‌برد و فقط بقیه را به دستگاه کند می‌فرستد
۲۵-۴‏column family sharding‏ در ‏BlueStore‏. پیش‌فرض ‏main‏ این است: «‏m(3) p‏(۳,۰-۱۲) ‏O‏(۳,۰-۱۳)=‏block_cache={type=binned_lru} L=min_write_buffer_number_to_merge=32 P=min_write_buffer_number_to_merge=32‏». هر پیشوند فهرست‌شده ‏CF‏(های) جداگانه‌ی خودش را دارد. برای انتخاب ‏shard‏، بازه‌ی بایت مشخص‌شده از کلید با ‏ceph_str_hash_rjenkins‏ هش و باقیمانده‌ی آن بر تعداد ‏shard‏ محاسبه می‌شود. پیشوندهای فهرست‌نشده در ‏CF‏ پیش‌فرض می‌مانند
۲۵-۵‏skiplist‏ با ده کلید و ارتفاع‌های تصادفی، و مسیر ‏Seek‏(۳۸) با رنگ قرمز. جست‌وجو از بالاترین سطح ‏head‏ شروع می‌شود، تا وقتی کلید بعدی کوچک‌تر است جلو می‌رود و وقتی بزرگ‌تر یا تهی است یک سطح پایین می‌رود. اگر در هر سطحی کلید برابر پیدا شود، همان‌جا برمی‌گردد. در ‏InlineSkipList‏ اشاره‌گرهای سطوح بالا پیش از خود گره در همان تخصیص ‏arena‏ قرار دارند، پس یک گره با ارتفاع ‏h‏ دقیقاً ‏h‏ اشاره‌گر هشت بایتی و سپس کلید و مقدار را در یک قطعه‌ی پیوسته دارد
۲۵-۶توالی ‏group commit‏ در ‏WriteThread‏. نخستین نویسنده با ‏CAS‏ روی ‏newest_writer_‏ رهبر می‌شود. بقیه منتظر می‌مانند. رهبر تا سقف اندازه و تا جایی که پرچم‌ها سازگارند، ‏batch‏ها را در یک رکورد ‏WAL‏ ادغام می‌کند. یک‌بار ‏Sync‏ می‌کند، هر ‏batch‏ را با شماره‌ی توالی پیوسته در ‏memtable‏ درج می‌کند و سپس همه را با ‏STATE_COMPLETED‏ بیدار می‌کند
۲۵-۷اینکه ‏log::Writer::AddRecord‏ یک ‏batch‏ هفتادهزار بایتی را چگونه در چهار بلوک ۳۲ ‏KiB‏ می‌نویسد. هر قطعه هدر ۷ بایتی دارد: ‏crc32c‏ چهاربایتی، طول دوبایتی و نوع یک‌بایتی. بلوک‌ها مستقل‌اند: اگر خواننده در بازیابی به بلوک خراب برسد، می‌تواند از ابتدای بلوک بعدی همگام شود. اگر کمتر از ۷ بایت به پایان بلوک مانده باشد، با صفر پر می‌شود
۲۵-۸چیدمان بایت‌به‌بایت یک بلوک داده‌ی ‏SST‏ با دو ورودی نمونه. ورودی اول ‏restart‏ است و کلید کامل ۱۸ بایتی را نگه می‌دارد (۱۰ بایت کلید کاربر و ۸ بایت ‏trailer‏ شامل ‏seq‏ و ‏type‏). ورودی دوم فقط ۹ بایت غیرمشترک دارد. انتهای بلوک آرایه‌ی آفست ‏restart‏ها و یک ‏u32‏ پانویس است که بیت ۳۱ آن نوع ایندکس درون‌بلوکی را نشان می‌دهد و بقیه‌ی بیت‌ها تعداد ‏restart‏ها را
۲۵-۹نرخ مثبت کاذب برحسب بیت به ازای هر کلید برای ‏Bloom‏ استاندارد و ‏Bloom‏ محلی در ‏cache line‏، با تعداد ‏probe‏ که هر پیاده‌سازی انتخاب می‌کند. نقطه‌ی قرمز تنظیم ‏Ceph‏ است (۲۰ بیت، ۱۱ ‏probe‏، حدود ۰.۰۱۷٪). در ۱۰ بیت تفاوت دو منحنی کم است و در ۲۰ بیت حدود ۲.۵ برابر. اما ‏Bloom‏ محلی همیشه یک خط ۶۴ بایتی را لمس می‌کند، و این برای ‏Get‏‌های ‏onode‏ که بیشترشان در ‏L0‏ تا ‏L2‏ دنبال کلید نبوده می‌گردند، تعیین‌کننده است
۲۵-۱۰اندازه‌ی واقعی و هدف هر سطح با ‏level_compaction_dynamic_level_bytes=true‏. هدف‌ها از سطح آخر به بالا با تقسیم بر ۸ به دست می‌آیند. ‏L1‏ و ‏L2‏ زیر سطح پایه‌اند و خالی می‌مانند. امتیاز برای سطح آخر حساب نمی‌شود، چون ‏MaxInputLevel‏ برابر ‏num_levels‏ − ۲ است
۲۶-۱دست‌دهی کامل ‏msgr2‏. پس از ‏banner‏، هر طرف ‏HELLO‏ (نوع ‏entity‏ و آدرسی که طرف مقابل را با آن می‌بیند) می‌فرستد. سپس احراز هویت ‏cephx‏ با ‏AUTH_REQUEST‏، رفت و برگشت‌های ‏MORE‏ و ‏AUTH_DONE‏ (که ‏global_id‏ و حالت اتصال ‏crc‏ یا ‏secure‏ را تعیین می‌کند) انجام می‌شود. از این نقطه ‏frame‏ها در حالت انتخاب‌شده‌اند. ‏AUTH_SIGNATURE‏ (HMAC-SHA۲۵۶ روی بایت‌های پیش از احراز) از دستکاری مرحله‌ی رمزنشده جلوگیری می‌کند. ‏CLIENT_IDENT‏ و ‏SERVER_IDENT‏ جلسه را با ‏cookie‏ و ‏global_seq‏ برقرار می‌کنند
۲۶-۲ساختار ‏frame‏ در ‏msgr2.1. preamble_block_t‏ دقیقاً ۳۲ بایت است و طوری طراحی شده که مضرب بلوک ‏AES‏ (۱۶) باشد. در حالت ‏crc‏، ‏segment‏ اول بلافاصله پس از ‏preamble‏ و همراه ‏crc‏ خودش می‌آید و اگر ‏segment‏ دیگری باشد، ‏epilogue‏ شامل ‏late_status‏ و ‏crc‏ بقیه‌ی ‏segment‏هاست. در حالت ‏secure‏، ‏preamble‏ همراه ۴۸ بایت اول ‏segment‏ صفر یکجا رمز می‌شود و یک ‏tag‏ مربوط به ‏GCM‏ می‌گیرد. پیام‌ها چهار ‏segment‏ دارند: ‏header2‏، ‏front‏، ‏middle‏ و ‏data‏ (با هم‌ترازی صفحه برای کپی صفر)
۲۶-۳همان قاب ‏KEEPALIVE2‏ بالا، این بار به شکل شبکه‌ی بایت: ‏preamble‏ سی‌ودوبایتی با ‏tag‏، تعداد ‏segment‏ و جدول چهار ‏segment‏ (سه‌تای آخر صفر)، سپس ‏crc‏ خود ‏preamble‏، سپس ‏segment‏ هشت‌بایتی ‏utime_t‏ و در پایان ‏crc‏ آن ‏segment‏. جمع ۴۴ بایت است و چون فقط یک ‏segment‏ هست، ‏epilogue‏ ندارد. این کوچک‌ترین قاب پرتکرار روی یک اتصال بی‌کار است و مثلاً ‏MonClient‏ هر ‏mon_client_ping_interval‏ ثانیه یکی از آن را به ‏mon‏ می‌فرستد (‏send_keepalive‏)
۲۶-۴ماشین وضعیت ‏ProtocolV2‏. سمت ‏connect‏ از ‏START_CONNECT‏ با ‏banner‏، ‏HELLO‏، ‏auth‏ و فشرده‌سازی به ‏SESSION_CONNECTING‏ (جلسه‌ی تازه) یا ‏SESSION_RECONNECTING‏ (cookie قبلی) و در نهایت ‏READY‏ می‌رسد. سمت ‏accept‏ مسیر مشابهی دارد. در ‏READY‏ دریافت هر پیام از سه ‏throttler‏ (تعداد پیام، بایت‌های ‏policy‏ و صف ‏dispatch‏) می‌گذرد. خطا در اتصال ‏lossless‏ به ‏STANDBY‏ یا بازاتصال با ‏backoff‏ (۰.۲ تا ۱۵ ثانیه)، و در اتصال ‏lossy‏ به ‏CLOSED‏ منجر می‌شود
۲۶-۵مدل ‏threading‏ در ‏AsyncMessenger. NetworkStack‏ (پیش‌فرض ‏Posix‏) تعداد ‏ms_async_op_threads=3 Worker‏ می‌سازد و هر ‏Worker‏ یک ‏EventCenter‏ دارد. هر اتصال هنگام ساخت به یکی از ‏worker‏ها سپرده می‌شود و تا پایان عمرش همان‌جا می‌ماند. حلقه‌ی هر ‏worker‏ ابتدا زمان‌سنج‌های سررسیده را اجرا می‌کند، سپس در ‏epoll_wait‏ (edge-triggered) منتظر رویدادهای ‏fd‏ می‌ماند و در پایان رویدادهای خارجی‌ای را اجرا می‌کند که ‏thread‏های دیگر صف کرده‌اند. ‏thread‏های دیگر ‏worker‏ را با نوشتن در یک ‏eventfd‏ (یا ‏pipe‏ در سیستم‌های بدون آن) بیدار می‌کنند
۲۶-۶مقایسه‌ی بایت‌های روی سیم برای یک پیام با داده‌ی ۴ ‏KiB‏ در دو حالت ‏crc‏ و ‏secure‏. در ‏crc‏، ‏segment 0 crc‏ چهاربایتی خودش را دارد و ‏epilogue‏ سیزده‌بایتی ‏crc‏ سه ‏segment‏ دیگر را. در ‏secure‏، ‏preamble‏ جای ۴۸ بایت ‏inline‏ و تگ ‏GCM‏ شانزده‌بایتی را دارد، پس هدر پیام بدون قاب جداگانه رمز می‌شود. ‏front‏ به مضرب ۱۶ گرد می‌شود و ‏epilogue‏ یک بلوک ۱۶ بایتی به‌اضافه‌ی تگ است. نوار داده برای خوانایی در ۱۰۰۰ بایت بریده شده است
۲۶-۷چیدمان ۴۰ بایت ابتدایی ‏connection_secret‏ که ‏cephx‏ در پایان احراز هویت به هر دو طرف می‌دهد: ۱۶ بایت کلید ‏AES-128‏ مشترک، سپس ‏nonce‏ دوازده‌بایتی دریافت و ‏nonce‏ دوازده‌بایتی ارسال. هر ‏nonce‏ یک بخش ثابت چهاربایتی و یک شمارنده‌ی هشت‌بایتی دارد. سمت سرور با پرچم ‏crossed‏ نقش ‏rx‏ و ‏tx‏ را جابه‌جا می‌کند تا هر جهت ‏nonce‏ مستقل داشته باشد
۲۶-۸ساختار ‏process_events‏ در ‏EventCenter‏. مهلت انتظار از نزدیک‌ترین رویداد زمان‌دار به دست می‌آید و کف ۱ میلی‌ثانیه دارد. اگر رویداد خارجی منتظر باشد، صفر می‌شود. پس از ‏epoll_wait‏، اول ‏callback‏های ‏fd‏، سپس تایمرها و در پایان رویدادهای خارجی اجرا می‌شوند. ردیف پایین مسیر بیدار کردن از ‏thread‏ دیگر را نشان می‌دهد: صف زیر قفل، شمارنده‌ی اتمی و یک ‏write‏ هشت‌بایتی روی ‏eventfd‏
۲۶-۹ردیابی ‏get_worker‏ برای ده اتصال و یک بستن. ستون‌های سمت راست شمارنده‌ی ‏references‏ هر ‏Worker‏ را پس از هر رویداد نشان می‌دهند. انتخاب همیشه کمترین شمارنده است و در تساوی، نخستین ‏Worker‏ برنده می‌شود. بستن اتصال پنجم یک جای خالی در ‏Worker 1‏ باز می‌کند که بلافاصله اتصال بعدی آن را پر می‌کند
۲۶-۱۰توالی بازاتصال یک اتصال ‏lossless‏. کلاینت پس از قطع، پیام‌های بی‌‏ack‏ را به سر صف برمی‌گرداند و پس از احراز هویت دوباره ‏SESSION_RECONNECT‏ می‌فرستد. ‏OSD‏ آخرین ‏seq‏ دریافتی را در ‏RECONNECT_OK‏ اعلام می‌کند و کلاینت هر چیزی تا آن شماره را دور می‌ریزد. اتصال‌های ‏lossy‏ (مثل کلاینت به ‏OSD‏ در ‏Objecter‏) این مسیر را ندارند و به جای آن ‏reset‏ می‌شوند، و ‏Objecter‏ خودش ‏op‏ها را دوباره می‌فرستد
۲۷-۱سه لایه‌ی محاسبه. در بالا، حساب ‏GF‏(۲⁸): جمع برابر ‏XOR‏ است و ضرب با شیفت و ‏XOR‏ با ۰‏x1D‏ هنگام سرریز، یا با جدول ‏log/exp‏ انجام می‌شود. در وسط، ماتریس تولید سیستماتیک ‏ISA-L‏ برای ‏k=4‏ و ‏m=2‏ به روش ‏Vandermonde‏: چهار سطر اول همانی‌اند (داده دست‌نخورده می‌ماند)، سطر ‏P0‏ همه ۱ است (همان ‏XOR‏ ساده) و سطر ‏P1‏ توان‌های ۲ است. در پایین، پیاده‌سازی ‏SIMD: ec_init_tables‏ برای هر ضریب دو جدول ۱۶ خانه‌ای (‏nibble‏ پایین و بالا) می‌سازد و دستور ‏PSHUFB‏ با این جدول‌ها ۳۲ یا ۶۴ بایت را در یک دستور ضرب می‌کند
۲۷-۲تقسیم یک شیء ۶۴‏K‏ در ‏pool‏ با ‏k=4‏، ‏m=2‏ و ‏stripe_unit=4K. stripe_width‏ برابر ۱۶‏K‏ است، پس شیء چهار ‏stripe‏ دارد. در هر ‏stripe‏، چهار ناحیه‌ی ۴‏K‏ پشت‌سرهم به ‏shard‏های داده ۰ تا ۳ می‌روند و دو ‏parity‏ محاسبه و در ‏shard‏های ۴ و ۵ نوشته می‌شوند. هر ‏OSD‏ یک «شیء ‏shard‏» با ‏shard_id‏ خودش و اندازه‌ی ۱۶‏K‏ نگه می‌دارد
۲۷-۳نوشتن جزئی ۴‏K‏ در مقایسه‌ی دو ‏backend‏. در ‏EC‏ قدیمی، ‏primary‏ کل ‏stripe‏ را از ‏k shard‏ می‌خواند، داده‌ی جدید را ادغام، دوباره ‏encode‏ و همه‌ی ‏shard‏ها را بازنویسی می‌کند. در ‏EC‏ بهینه (‏Tentacle‏ با ‏allow_ec_optimizations‏)، فقط همان ۴‏K‏ قدیمی و ‏parity‏ها خوانده می‌شوند، ‏delta‏ برابر قدیم ⊕ جدید محاسبه و هر ‏parity‏ با ضریب مربوط در ‏delta‏ به‌روز می‌شود. خواندن کوچک هم به‌جای ‏decode‏ از ‏k shard‏، مستقیم از یک ‏shard‏ داده انجام می‌شود
۲۷-۴ترافیک بازسازی یک ‏shard‏ به اندازه‌ی ‏S. RS‏ با ‏k=4‏ به ۴‏S‏ نیاز دارد. ‏LRC‏ با ‏l=3‏ فقط از گروه محلی (۳ ‏chunk‏) می‌خواند. ‏CLAY‏ با ‏d=5‏ از ۵ ‏helper‏، هرکدام کسر ۱/(‏d‏−‏k+1‏) = ۱/۲ از ‏chunk‏ را می‌خواند، یعنی ۲.۵‏S. SHEC parity‏ها را روی زیرمجموعه‌های هم‌پوشان داده می‌سازد تا خرابی تکی با خواندن کمتر از ‏k‏ ترمیم شود
۲۷-۵جدول ۳۲ بایتی که ‏gf_vect_mul_init_base‏ برای ضریب ‏c=0x02‏ می‌سازد. ۱۶ خانه‌ی اول حاصل‌ضرب ‏c‏ در نیم‌بایت پایین (۰ تا ‏F‏) هستند و ۱۶ خانه‌ی دوم حاصل‌ضرب ‏c‏ در نیم‌بایت بالا (۰‏x00‏ تا ۰‏xF0‏). ضرب هر بایت با دو جست‌وجو و یک ‏XOR‏ انجام می‌شود، چون ضرب در ‏GF‏(۲⁸) نسبت به ‏XOR‏ خطی است: ‏c‏·(‏hi‏ ⊕ ‏lo‏) = ‏c‏·‏hi‏ ⊕ ‏c‏·‏lo‏. خانه‌های زرد همان دو خانه‌ای‌اند که برای بایت ۰‏xB7‏ خوانده می‌شوند
۲۷-۶مسیر ضرب ۳۲ بایت داده در ثابت ‏c=2‏ با ‏AVX2‏. داده به دو نیم‌بایت شکسته می‌شود. هر نیم‌بایت با ‏VPSHUFB‏ از جدول ۱۶ خانه‌ای مربوطش جست‌وجو می‌شود و دو نتیجه ‏XOR‏ می‌شوند. ردیف پایین نشان می‌دهد این ضرب برای هر ستون داده تکرار می‌شود و در ‏parity‏ انباشته می‌شود. نسخه‌های ‏gf_Nvect_dot_prod‏ چند ‏parity‏ را در یک گذر روی داده می‌سازند تا داده فقط یک‌بار از حافظه خوانده شود
۲۷-۷دو ماتریس کدگذاری ‏ISA-L‏ برای ‏k=4‏، ‏m=2‏ و برچسب ‏parity‏ حاصل برای بایت نمونه‌ی ‏D‏ = [۰‏x37‏، ۰‏x5C‏، ۰‏xA1‏، ۰‏x0F]‏. چهار سطر بالا همانی‌اند و داده را بی‌تغییر به ‏shard‏های ۰ تا ۳ می‌برند. دو سطر پایین ضرایب ‏parity‏ هستند. در ‏Vandermonde‏، سطر ‏P‏ تماماً ۱ است، پس ‏P‏ همان ‏XOR‏ است و مسیر سریع ‏xor_gen‏ برای ‏m=1‏ به‌کار می‌رود. در ‏Cauchy‏ همه‌ی ضرایب ‏parity‏ غیر بدیهی‌اند
۲۷-۸نوشتن ۴‏K‏ درون ‏shard D1‏ در ‏pool‏ با ‏k=4‏، ‏m=2‏ و مسیر ‏parity delta. primary‏ فقط همان بازه را از ‏D1‏ و دو ‏parity‏ می‌خواند. Δ را با ‏encode_delta‏ حساب می‌کند و با ‏apply_delta‏ ضریب ستون ۱ ضرب در Δ را به هر ‏parity XOR‏ می‌کند. سپس سه نوشتن را در یک تراکنش توزیع‌شده می‌فرستد. ‏shard‏های ‏D0‏، ‏D2‏ و ‏D3‏ درگیر نمی‌شوند
۲۷-۹اینکه ‏parse_kml‏ سه پارامتر ‏k=4‏، ‏m=2‏ و ‏l=3‏ را چگونه به رشته‌های ‏mapping‏ و ‏layers‏ تبدیل می‌کند. در ‏mapping‏، حرف ‏D‏ جای داده و خط زیر جای ‏chunk‏ کدشده است. در هر لایه، ‏c‏ یعنی ‏chunk‏ی که آن لایه تولید می‌کند. لایه‌ی سراسری ‏parity‏های ۲ و ۶ را با ‏RS‏ می‌سازد و هر لایه‌ی محلی یک ‏parity XOR‏ مانند برای سه ‏chunk‏ گروه خود. با ‏crush-locality=rack‏، قانون ‏CRUSH‏ دو ‏rack‏ و در هر ‏rack‏ چهار ‏host‏ انتخاب می‌کند تا ترمیم محلی از ‏rack‏ بیرون نرود
۲۷-۱۰شبکه‌ی ‏sub-chunk‏ در ‏Clay‏ با ‏k=4‏، ‏m=2‏ و ‏d=5‏ برای سه گره گم‌شده‌ی متفاوت. سطرها گره‌ها با مختصات (‏x, y‏) در شبکه‌ی ‏q‏×‏t‏ هستند و ستون‌ها شاخص ‏sub-chunk‏. سطر قرمز گره گم‌شده است و خانه‌های آبی ‏sub-chunk‏هایی‌اند که هر ‏helper‏ می‌فرستد. الگو از ‏get_repair_subchunks‏ می‌آید: هرچه ‏y‏ بزرگ‌تر باشد، بازه‌ها کوتاه‌تر و پراکنده‌ترند. پس خواندن روی دیسک تکه‌تکه‌تر می‌شود، و این هزینه‌ی پنهان ‏Clay‏ روی ‏HDD‏ است
۲۸-۱خط لوله‌ی کامل نگاشت. نام شیء با ‏rjenkins‏ به ۰‏xD1895615 hash‏ می‌شود. با ‏pg_num=128‏، ‏ceph_stable_mod‏ نتیجه‌ی ۰‏x15‏ را می‌دهد، یعنی ‏PG 3.15‏. با پرچم ‏HASHPSPOOL‏، شماره‌ی ‏pool‏ هم در ‏hash‏ دخیل می‌شود تا ‏PG‏های ‏pool‏های مختلف روی هم نیفتند، و ‏x‏ = ۰‏x36D15C32‏ ورودی ‏CRUSH‏ است. ‏crush_do_rule‏ فهرست خام ‏OSD‏ها را برمی‌گرداند، سپس ‏OSD‏های ناموجود حذف و ‏upmap‏ها اعمال می‌شوند و در پایان ‏pg_temp‏ مجموعه‌ی ‏acting‏ را تعیین می‌کند
۲۸-۲اجرای واقعی ‏straw2‏ برای ‏x=0x36D15C32‏ در یک ‏bucket‏ ریشه با سه ‏host‏: -۳ و -۴ هرکدام با وزن ۱۰ و -۵ با وزن ۲۰. در ‏r=0‏، ‏host‏ -۴ با بیشترین ‏draw‏ (−۸.۹‏M‏) برنده است. در ‏r=1‏، ‏host‏ -۵ برنده است. برای ‏replica‏ سوم، ‏r=2‏ تا ‏r=5‏ به ‏host‏های تکراری می‌رسند و هر بار ‏ftotal‏ افزایش می‌یابد، تا این‌که ‏r=6‏ به ‏host‏ -۳ می‌رسد. سقف تلاش ‏choose_total_tries=50‏ است
۲۸-۳اجرای ‏replicated_rule. TAKE‏ ریشه را در بردار کاری ‏w‏ می‌گذارد. ‏CHOOSELEAF_FIRSTN‏ با ‏numrep=0‏ (یعنی برابر ‏size‏) برای هر ‏replica‏ یک ‏host‏ انتخاب می‌کند و سپس به‌صورت بازگشتی یک ‏OSD‏ درون آن. ‏EMIT‏ نتیجه را به خروجی می‌افزاید. در هر انتخاب، ‏r‏ = ‏rep‏ + ‏ftotal‏ است. اگر نتیجه تکراری باشد یا ‏OSD‏ با ‏is_out‏ رد شود، ‏ftotal‏ افزایش می‌یابد و تلاش تکرار می‌شود. ‏is_out‏ برای وزن ‏reweight‏ بین ۰ و ۱، با ‏hash‏ دیگری از (‏x‏، ‏osd‏) تصمیم احتمالی می‌گیرد
۲۸-۴سه ابزار اصلاح توزیع و ترتیب اعمال آن‌ها. ‏reweight‏ با ‏is_out‏ احتمالی کار می‌کند و جابه‌جایی گسترده ایجاد می‌کند. ‏choose_args‏ (weight-set) وزن‌های جایگزینی برای ‏straw2‏ فراهم می‌کند که حتی می‌تواند برای هر ‏position‏ (replica اول، دوم و ...) متفاوت باشد و ‏balancer‏ در حالت ‏crush-compat‏ از آن استفاده می‌کند. ‏upmap‏ پس از ‏CRUSH‏ نتیجه را صریحاً جایگزین می‌کند. در ‏OSDMap‏ ترتیب اعمال این است: ‏crush_do_rule‏ با ‏choose_args‏، حذف ‏OSD‏های ناموجود، ‏pg_upmap‏، ‏pg_upmap_items‏ و ‏pg_upmap_primary‏
۲۸-۵اجرای واقعی ‏bucket_perm_choose‏ برای ‏bucket‏ با شماره‌ی -۵ و چهار ‏item‏ با ‏x=0x1234ABCD‏. سه گام ‏Fisher‏–‏Yates‏ جایگشت [۳ ۲ ۰ ۱] را می‌سازند. پس ‏r=0‏ به ‏item 13‏، ‏r=1‏ به ۱۲، ‏r=2‏ به ۱۰ و ‏r=3‏ به ۱۱ می‌رسد. جایگشت در ‏crush_work‏ نگه داشته می‌شود تا تلاش‌های بعدی با همان ‏x‏ از نو محاسبه نشوند
۲۸-۶اجرای واقعی ‏bucket_list_choose‏ برای ‏bucket‏ با شماره‌ی -۶ با وزن‌های ۱، ۱ و ۲. برای ‏r=0‏ مقدار مقیاس‌شده‌ی ‏item 22‏ برابر ۳.۹۰ است که از وزن ۲ بیشتر است، پس رد می‌شود. ‏item 21‏ با ۰.۳۶ < ۱ انتخاب می‌شود. برای ‏r=1‏ و ‏r=2‏ هر دو ‏item‏ آخر رد می‌شوند و ‏item 20‏ انتخاب می‌شود
۲۸-۷اجرای واقعی ‏bucket_tree_choose‏ برای چهار ‏item‏ با وزن‌های ۱، ۲، ۳ و ۴. ریشه ‏n4‏ وزن ۱۰ دارد. ‏t=4.22‏ از وزن زیردرخت چپ (۳) بیشتر است، پس مسیر به راست (‏n6‏، وزن ۷) می‌رود. آنجا ‏t=0.78‏ < ۳، پس برگ ‏n5‏ یعنی ‏item 32‏ انتخاب می‌شود. برای ‏r=1‏ هم همین مسیر طی شد. توزیع روی ۴۰۰ هزار ورودی تقریباً ۱:۲:۳:۴ بود
۲۸-۸درصد ورودی‌هایی که با تغییر ‏bucket‏ مقصد دیگری گرفتند، روی ۲۰۰ هزار مقدار ‏x‏ با ‏hash‏ واقعی. ستون آبی: افزودن ‏item‏ پنجم به چهار ‏item‏ هم‌وزن (حداقل نظری ۲۰٪). ستون نارنجی: حذف ‏item‏ دوم و ساخت دوباره‌ی ‏bucket‏ (حداقل نظری ۲۵٪). فقط ‏straw2‏ در هر دو حالت به حداقل نظری می‌رسد
۲۸-۹ساختار ‏OSDMap::calc_pg_upmaps‏. پس از محاسبه‌ی ‏PG‏های هر ‏OSD‏ و انحراف هر ‏OSD‏ از هدف وزنی، در هر دور مجموعه‌های ‏overfull‏ و ‏underfull‏ ساخته می‌شوند. سپس از پرترین ‏OSD‏، نخست حذف ‏upmap‏های قبلی و بعد افزودن یک جفت تازه آزموده می‌شود. هر تغییر فقط وقتی پذیرفته می‌شود که امتیاز توازن بدتر نشود
۲۸-۱۰قانون ‏MSR‏ برای ‏EC 4+2‏ روی سه ‏host‏. خروجی شش‌خانه‌ای به سه ‏stride‏ دوتایی تقسیم می‌شود و ‏host‏های هر ‏stride‏ از ‏stride‏های دیگر جدا نگه داشته می‌شوند. وقتی ‏osd.7 out‏ است، شاخص ۳ در این دور خالی می‌ماند و در دور بعدی نزول با شماره‌ی تلاش تازه از ریشه تکرار می‌شود. جایگزین می‌تواند در همان ‏host‏ یا در ‏host‏ی باشد که در ‏stride‏ دیگری استفاده نشده است
۲۹-۱مسیر یک نوشتن در ‏krbd‏. برنامه روی /‏dev/rbd0‏ یا فایل‌سیستمی روی آن می‌نویسد. لایه‌ی ‏block‏ درخواست‌ها را از طریق ‏tag_set‏ مربوط به ‏blk-mq‏ (با عمق ‏queue_depth‏، پیش‌فرض ‏BLKDEV_DEFAULT_RQ‏) به ‏rbd_queue_rq‏ می‌دهد. ‏rbd.c‏ هر ‏request‏ را به یک ‏img_request‏ تبدیل و آن را بر اساس اندازه‌ی شیء به ‏obj_request‏ها تقسیم می‌کند. هر ‏obj_request‏ یک ‏ceph_osd_request‏ است که ‏osd_client‏ با ‏CRUSH‏ درون هسته به ‏primary OSD‏ می‌فرستد
۲۹-۲ماشین‌های وضعیت در ‏rbd.c. img_request‏ اگر ‏exclusive-lock‏ فعال باشد، ابتدا قفل را می‌گیرد، سپس همه‌ی ‏obj_request‏ها را اجرا می‌کند و منتظر می‌ماند. هر نوشتن شیء در صورت فعال بودن ‏object-map‏ ابتدا آن را به‌روز می‌کند، سپس روی ‏OSD‏ می‌نویسد. در ‏clone‏، اگر شیء هنوز وجود نداشته باشد، ‏copyup‏ داده‌ی ‏parent‏ را همراه نوشتن می‌فرستد و در پایان ‏object-map‏ نهایی می‌شود. خواندن اگر در ‏clone‏ با -‏ENOENT‏ روبه‌رو شود، به ‏parent‏ می‌رود
۲۹-۳هماهنگی ‏caps‏ در ‏CephFS‏ هسته‌ای. کلاینت ‏A‏ فایل را برای نوشتن باز می‌کند و چون تنها نویسنده است، ‏caps‏ کامل، از جمله ‏Fc‏ (کش خواندن) و ‏Fb‏ (بافر نوشتن)، می‌گیرد. پس نوشتن‌ها در ‏page cache‏ می‌مانند. وقتی کلاینت ‏B‏ همان فایل را باز می‌کند، ‏MDS‏ از ‏A‏ می‌خواهد ‏Fc‏ و ‏Fb‏ را پس بدهد. ‏A‏ صفحه‌های ‏dirty‏ را مستقیم به ‏OSD‏ها می‌نویسد و اندازه و ‏mtime‏ را به ‏MDS‏ گزارش می‌کند. سپس ‏B‏ فقط ‏Fr‏ می‌گیرد و هر خواندن را مستقیم و هم‌گام از ‏OSD‏ انجام می‌دهد
۲۹-۴مقایسه‌ی کلاینت‌های هسته و فضای کاربر. ‏krbd‏ و ‏ceph.ko‏ روی ‏libceph‏ ساخته شده‌اند که پیاده‌سازی مستقلی در ‏C‏ است و ویژگی‌هایش با نسخه‌ی هسته محدود می‌شود. ‏librbd‏ و ‏ceph-fuse‏ روی ‏librados‏ و ‏Objecter‏ و ‏AsyncMessenger‏ ساخته شده‌اند، همه‌ی ویژگی‌ها (‏journaling‏، ‏mirroring‏، ‏QoS‏، کش) را دارند و بدون ‏reboot‏ ارتقا می‌یابند
۲۹-۵محاسبه‌ی ‏striping‏ در هسته. آفست به شماره‌ی بلوک (‏su‏)، شماره‌ی ‏stripe‏ و مجموعه‌ی شیء تبدیل می‌شود. در مثال، یک خواندن ۳‏MiB‏ از آفست ۱۰.۵‏MiB‏ با ‏layout‏ برابر ۱‏MiB/4/4MiB‏ به چهار تکه در چهار شیء مختلف می‌شکند. پایین شکل قالب نام شیء در ‏RBD v2‏، ‏RBD v1‏ و ‏CephFS‏ را نشان می‌دهد
۲۹-۶انتخاب ‏OSD‏ در ‏libceph. calc_target‏ با ‏hash‏ نام شیء ‏seed‏ خام را می‌سازد، با ‏ceph_stable_mod‏ آن را به ‏pg_num‏ نگاشت می‌دهد، با ‏crush_hash32_2‏ و شناسه‌ی ‏pool‏ مقدار ‏pps‏ را می‌سازد و ‏CRUSH‏ و سپس ‏upmap‏ و ‏primary_temp‏ را اعمال می‌کند. با هر ‏osdmap‏ جدید، ‏scan_requests‏ درخواست‌های در جریان را دوباره ارزیابی و در صورت تغییر ‏target‏ دوباره ارسال می‌کند. درخواست بدون ‏OSD‏ در دسترس در ‏homeless_osd‏ منتظر می‌ماند
۲۹-۷نگاشت یک نوشتن ۲۵۶ کیلوبایتی روی دستگاه ‏krbd‏ با ‏striping‏ سفارشی. ردیف بالا تکه‌های درخواست را در فضای تصویر و شیء مقصد هرکدام را نشان می‌دهد. ردیف پایین همان تکه‌ها را درون چهار شیء نشان می‌دهد. دو تکه‌ی شیء ۰ که در فضای تصویر ۲۲۴‏K‏ از هم فاصله دارند، درون شیء کنار هم می‌افتند و ‏ceph_file_to_extents‏ آن‌ها را به یک ‏extent‏ تبدیل می‌کند. پس یک ‏obj_request‏ با دو ‏bio_vec‏ ساخته می‌شود
۲۹-۸شاخه‌های تصمیم ‏calc_target‏ در ‏libceph‏. ردیف بالا ورودی‌ها را از نقشه‌ی تازه می‌خواند: وجود ‏pool‏، شمارنده‌ی ‏force resend‏ و نگاشت تازه‌ی ‏PG‏. ردیف میانی سه نوع تغییر را می‌سنجد. ردیف پایین نتیجه است: هر تغییر معنادار درخواست را از ‏session‏ قبلی جدا و به ‏session OSD‏ تازه وصل می‌کند. تغییرهای بی‌ربط هیچ هزینه‌ای ندارند
۲۹-۹خط زمانی ‏handle_timeout‏ در ‏osd_client‏. خط‌چین‌های آبی دورهای پنج‌ثانیه‌ای هستند. درخواست ‏A‏ به‌موقع تمام شده است. درخواست ‏B‏ از دور دهم کند شمرده می‌شود و ‏keepalive‏ برای ‏OSD‏ آن فرستاده می‌شود. ‏session‏ بیکار ‏osd.3‏ تا گذشتن ‏lru_ttl‏ نگه داشته می‌شود و سپس در دور بعدی ‏handle_osds_timeout‏ بسته می‌شود
۲۹-۱۰دو شاخه‌ی گرفتن قفل در ‏krbd‏. اگر مالک فعلی ‏watcher‏ زنده روی ‏rbd_header‏ داشته باشد، ‏krbd‏ با ‏notify‏ از او درخواست رها کردن می‌کند و صبر می‌کند. اگر ‏watcher‏ی نباشد، مالک مرده فرض می‌شود: آدرسش در ‏mon blocklist‏ می‌شود تا نتواند نوشتن معلقی را تمام کند، و سپس قفل شکسته و گرفته می‌شود. ترتیب ‏blocklist‏ پیش از ‏break_lock‏ برای جلوگیری از نوشتن هم‌زمان دو میزبان حیاتی است
۲۹-۱۱چیدمان بیت‌های ‏cap‏ در یک عدد صحیح و مثال ۰‏x3F55‏. بیت ۰ ‏PIN‏ است. هر کدام از ‏AUTH‏، ‏LINK‏ و ‏XATTR‏ دو بیت (‏shared‏ و ‏exclusive‏) دارند. ‏FILE‏ از بیت ۸ به بعد هشت بیت دارد: ‏s‏، ‏x‏، ‏c‏، ‏r‏، ‏w‏، ‏b‏، ‏a‏ و ‏l‏. خانه‌های روشن بیت‌های فعال مثال‌اند. ‏ceph_cap_string‏ همین بیت‌ها را به رشته‌ای تبدیل می‌کند که در ‏debugfs‏ و لاگ‌های ‏MDS‏ دیده می‌شود
۳۰-۱مقایسه‌ی مدل اجرای ‏OSD‏ کلاسیک و ‏Crimson‏. در ‏OSD‏ کلاسیک هر درخواست از چند ‏thread pool‏ و چند قفل عبور می‌کند. در ‏Crimson‏ هر هسته یک ‏reactor‏ مستقل دارد که هم شبکه و هم ‏PG‏های خودش را اجرا می‌کند. ارتباط بین هسته‌ها فقط با پیام صریح (‏submit_to‏ و ‏invoke_on‏) است و نتیجه به‌صورت ‏future‏ به هسته‌ی مبدأ برمی‌گردد
۳۰-۲مسیر یک درخواست روی یک ‏reactor‏. هیچ مرحله‌ای منتظر نمی‌ماند. هر عمل ناهم‌گام ‏future‏ برمی‌گرداند و ‏continuation‏ پس از آماده شدن نتیجه در صف همان هسته قرار می‌گیرد. چون زمان‌بندی تعاونی است، هر ‏task‏ باید زود تمام شود. ‏Crimson‏ سهم زمانی هر ‏task‏ را با ‏crimson_reactor_task_quota_ms‏ (پیش‌فرض ۰.۵ میلی‌ثانیه در ‏main‏) تنظیم می‌کند
۳۰-۳نگاشت ‏PG‏ به هسته در ‏Crimson‏. اگر هسته‌ای ‏PG‏ را در نقشه‌ی محلی نیابد، از هسته‌ی ۰ می‌پرسد. هسته‌ی ۰ برای ‏PG‏ تازه کم‌بارترین هسته را با ‏min_element‏ روی شمار ‏PG‏های هر هسته انتخاب، شمارنده را افزایش و نقشه را به همه‌ی هسته‌ها پخش می‌کند. از آن پس همه‌ی کارهای آن ‏PG‏ فقط روی همان هسته اجرا می‌شود
۳۰-۴‏phase‏های یک ‏ClientRequest‏ در ‏Crimson‏. سطح اتصال (منتظر فعال شدن، دریافت نقشه و یافتن هسته‌ی ‏PG‏)، سطح شارد (ساختن یا انتظار برای ‏PG‏)، سطح ‏PG‏ (آماده بودن ‏PG‏، ‏gate‏ نسخه‌ی ‏OSDMap‏، فعال بودن ‏PG‏ و گرفتن ‏object context‏) و سطح شیء (پردازش، انتظار برای ‏scrub‏، انتظار برای تکرار در ‏replica‏ها و ارسال پاسخ). ‏phase‏های ‏exclusive‏ ترتیب را تضمین می‌کنند و ‏concurrent‏ها هم‌پوشانی را ممکن می‌کنند
۳۰-۵پشتیبان‌های ذخیره‌سازی ‏Crimson. SeaStore‏ و ‏CyanStore‏ بومی‌اند و مستقیماً روی ‏reactor‏ اجرا می‌شوند. ‏AlienStore‏، ‏BlueStore‏ یا ‏MemStore‏ کلاسیک را در ‏thread‏های «بیگانه» (خارج از ‏Seastar‏) اجرا می‌کند. درخواست از ‏reactor‏ به ‏thread pool‏ فرستاده می‌شود و نتیجه با ‏seastar::alien::submit_to‏ به هسته‌ی مبدأ برمی‌گردد
۳۰-۶لایه‌های ‏SeaStore‏. مدیرهای منطقی (‏onode‏ با درخت ‏FLTree‏، ‏omap‏ و ‏xattr‏ با ‏BTree‏، داده‌ی شیء و ‏collection‏ها) روی آدرس منطقی ‏laddr‏ کار می‌کنند. ‏TransactionManager‏ تغییرات را در یک ‏Transaction‏ جمع و به‌صورت یک ‏record‏ اتمیک ثبت می‌کند. ‏LBAManager‏ نگاشت ‏laddr‏ به ‏paddr‏ را در یک ‏B-tree‏ نگه می‌دارد و ‏BackrefManager‏ نگاشت معکوس را برای ‏GC. Journal‏، ‏ExtentPlacementManager‏، ‏Cache‏ و ‏AsyncCleaner‏ روی دستگاه‌های ‏segmented‏، ‏RBM‏ یا ‏ZBD‏ کار می‌کنند
۳۰-۷ساختار ۶۴ بیتی ‏paddr_t‏ در ‏seastore_types.h‏. هشت بیت بالایی ‏device_id‏ است (‏DEVICE_ID_BITS‏ = ‏digits‏(uint۸_t) = ۸). در دستگاه ‏segmented‏، ۲۴ بیت بعدی شماره‌ی ‏segment‏ درون دستگاه و ۳۲ بیت پایینی آفست درون ‏segment‏ است. در ‏RBM‏، ۵۶ بیت پایینی آفست مطلق است. هفت مقدار بالای ‏device_id‏ برای آدرس‌های نمادین مثل آفست نسبی درون ‏record‏ یا آدرس تأخیری رزرو شده‌اند
۳۰-۸ساختار سگمنت ‏journal‏ و ‏record group‏. هر ‏record group‏ با یک نوشتن روی دیسک ثبت می‌شود و شامل ‏record_group_header_t‏ (تعداد ‏record‏ها، طول فراداده و داده، ‏nonce‏ سگمنت، ‏committed_to‏ و ‏crc‏ داده)، سپس ‏header‏ هر ‏record‏ (نوع تراکنش، شمار ‏delta‏ و ‏extent‏، زمان تغییر)، ‏delta‏ها و ‏extent‏های ‏inline‏ است. ‏RecordSubmitter‏ تا ۱۶ ‏record‏ را در یک ‏batch‏ جمع و حداکثر ۵ نوشتن هم‌زمان روی ‏journal‏ نگه می‌دارد (پیش‌فرض‌های ‏main‏)
۳۰-۹ماشین حالت ‏extent‏ها در ‏Cache‏ مربوط به ‏SeaStore. extent‏ تازه در ‏INITIAL_WRITE_PENDING‏ ساخته و پس از ‏commit‏ تمیز (‏CLEAN‏) می‌شود. تغییر یک ‏extent‏ موجود نسخه‌ای در ‏MUTATION_PENDING‏ می‌سازد که پس از ‏commit‏ با ثبت ‏delta‏ در ‏journal‏، ‏DIRTY‏ می‌شود (محتوای حافظه با دیسک متفاوت و ‏pinned‏ در حافظه). ‏extent‏هایی که فراداده‌شان هنوز ثبت نشده ‏EXIST_CLEAN‏ و ‏EXIST_MUTATION_PENDING‏ هستند. ‏DIRTY‏ها با بازنویسی توسط ‏cleaner‏ یا ‏trim‏ مربوط به ‏journal‏ دوباره ‏CLEAN‏ می‌شوند
۳۱-۱مقایسه‌ی دو مسیر از ‏BlueStore‏ تا کنترلر ‏NVMe‏. در مسیر هسته، درخواست از ‏syscall‏، لایه‌ی ‏blk-mq‏ و درایور ‏nvme-pci‏ می‌گذرد و تکمیل با وقفه‌ی ‏MSI-X‏ گزارش می‌شود. در مسیر ‏SPDK‏، همه‌ی این کارها در فضای کاربر انجام می‌شود: درایور ‏SPDK‏ خود ‏SQE‏ را در صف می‌نویسد، ‏doorbell‏ را از راه ‏BAR‏ نگاشته‌شده‌ی دستگاه به‌روز می‌کند و تکمیل را با ‏polling‏ پیدا می‌کند. هر دو مسیر روی همان ساختار سخت‌افزاری (‏SQ‏ و ‏CQ‏ در حافظه‌ی میزبان) به هم می‌رسند
۳۱-۲ساختار ۶۴ بایتی فرمان ‏Read/Write‏ در صف ارسال، با مقادیر یک خواندن ۴ ‏KiB‏ از آفست ۱ ‏GiB. opcode=0x02‏ یعنی ‏Read‏ (۰x۰۱ Write است). ‏command_id‏ شناسه‌ای است که درایور انتخاب می‌کند و کنترلر در ‏CQE‏ برمی‌گرداند. ‏slba‏ آدرس منطقی آغاز بر حسب بلوک است و ‏length‏ تعداد بلوک‌ها به‌صورت صفرمبنا. ‏prp1‏ و ‏prp2‏ آدرس‌های فیزیکی (یا ‏IOVA‏ پشت ‏IOMMU‏) صفحه‌های داده‌اند
۳۱-۳ساختار ۱۶ بایتی ورودی صف تکمیل. ‏sq_head‏ به میزبان می‌گوید کنترلر تا کجای ‏SQ‏ را خوانده است. ‏sq_id‏ و ‏command_id‏ با هم فرمان را یکتا مشخص می‌کنند. بیت صفر فیلد ‏status‏ همان ‏Phase Tag‏ است: کنترلر در دور اول صف آن را ۱ می‌نویسد، در دور دوم ۰، و همین‌طور تا آخر
۳۱-۴توالی کامل یک فرمان: نوشتن ‏SQE‏ در خانه‌ی ‏sq_tail‏، به‌روزرسانی ‏SQ Tail Doorbell‏، واکشی و اجرای فرمان توسط کنترلر، نوشتن ‏CQE‏، وقفه (یا ‏polling‏)، پیمایش ‏CQ‏ و به‌روزرسانی ‏CQ Head Doorbell‏. آفست ‏doorbell‏ها از ثبات ‏CAP.DSTRD‏ به دست می‌آید
۳۱-۵مسیر کد ‏NVMEDevice‏ در سه مرحله. در باز کردن، رشته‌ی ‏transport ID‏ از فایل خوانده و با ‏spdk_nvme_transport_id_parse‏ تجزیه می‌شود. ‏NVMEManager‏ یک ‏thread‏ جدا برای محیط ‏DPDK‏ و ‏probe‏ می‌سازد. در نوشتن، داده به تکه‌های ۱۲۸ ‏KiB‏ تقسیم و در یک فهرست پیوندی ‏Task‏ در ‏IOContext‏ جمع می‌شود. ‏aio_submit‏ روی یک ‏qpair‏ مخصوص همان ‏thread‏ (thread_local) فرمان‌ها را می‌فرستد. تکمیل در همان ‏thread‏ با ‏polling‏ پیدا می‌شود و بافرهای ‏DMA‏ به مخزن برمی‌گردند
۳۱-۶اجزای درگاه ‏NVMe-oF‏. هدف ‏SPDK‏ (nvmf_tgt) فرمان‌های ‏NVMe/TCP‏ را از میزبان می‌گیرد و هر ‏namespace‏ را به یک ‏bdev‏ از نوع ‏rbd‏ می‌دهد. این ‏bdev‏ با ‏librbd‏ ناهم‌گام (‏rbd_aio_readv‏ و ‏rbd_aio_writev‏) کار می‌کند. سرویس ‏gRPC‏ درگاه پیکربندی را در یک شیء ‏omap‏ در ‏RADOS‏ نگه می‌دارد. ‏NVMeofGwMonitorClient‏ با ‏mon‏ در ارتباط است و وضعیت ‏ANA‏ هر گروه را از ‏NVMeofGwMap‏ می‌گیرد
۳۱-۷بایت‌های ‏NVMe/TCP‏. هر ‏PDU‏ با هدر مشترک ۸ بایتی (‏type‏، ‏flags‏، ‏hlen‏، ‏pdo‏، ‏plen‏) شروع می‌شود. ‏Command Capsule‏ همان ‏SQE‏ ۶۴ بایتی را حمل می‌کند و اگر داده از ‏in_capsule_data_size‏ کوچک‌تر باشد، داده هم درون کپسول می‌آید. برای داده‌ی بزرگ‌تر، هدف با یک ‏R2T‏ اجازه‌ی ارسال می‌دهد و میزبان داده را در ‏H2CData‏ می‌فرستد
۳۱-۸ماشین حالت هر درگاه برای هر گروه ‏ANA‏ در ‏gw_states_per_group_t‏ (NVMeofGwTypes.h): ‏IDLE‏، ‏STANDBY‏، ‏ACTIVE‏، ‏OWNER_WAIT_FAILBACK_PREPARED‏، ‏WAIT_FAILBACK_PREPARED‏ و ‏WAIT_BLOCKLIST_CMPL‏. حالت صادرشده به میزبان فقط دو مقدار دارد: ‏OPTIMIZED‏ برای ‏ACTIVE‏ و ‏WAIT_BLOCKLIST_CMPL‏، و ‏INACCESSIBLE‏ برای بقیه
۳۱-۹توالی ‏failover‏. پس از ‏mon_nvmeofgw_beacon_grace‏ (پیش‌فرض ۷ ثانیه) بدون ‏beacon‏، درگاه ‏A‏ ناموجود اعلام می‌شود. ‏find_failover_candidate‏ کم‌بارترین درگاه را پیدا می‌کند، ‏A blocklist‏ می‌شود و ‏B‏ در ‏WAIT_BLOCKLIST_CMPL‏ با تایمر ۳۰ ثانیه قرار می‌گیرد. با رسیدن ‏OSDMap‏ تازه به ‏B‏، گروه برای ‏B‏ فعال و ‏ANA‏ میزبان به‌روز می‌شود
۳۲-۱توزیع زمان ‏scrub‏ بعدی با پیش‌فرض‌های ‏main‏. چپ: ‏shallow‏ با ‏osd_scrub_min_interval=1‏ روز و ‏ratio=0.5‏، یکنواخت میان ۲۴ و ۳۶ ساعت پس از ‏scrub‏ قبلی. راست: ‏deep‏ با ‏osd_deep_scrub_interval=7‏ روز و ‏cv=0.2‏، نرمال با میانگین ۷ روز و انحراف ۱.۴ روز که به [۴.۲، ۹.۸] روز بریده می‌شود. بریدن، دو قله‌ی کوچک در لبه‌ها می‌سازد که هرکدام حدود ۲.۳٪ احتمال دارد. خط‌چین مرز هشدار ‏PG_NOT_DEEP_SCRUBBED‏ است
۳۲-۲ماشین حالت ‏ScrubMachine‏ در ‏scrub_machine.h‏. سمت ‏primary‏ از ‏NotActive‏ به ‏PrimaryActive‏ می‌رود، در ‏Session‏ رزرو ‏replica‏ها را می‌گیرد و در ‏ActiveScrubbing‏ چرخه‌ی ‏chunk‏ها را اجرا می‌کند: ‏PendingTimer‏ (خواب میان ‏chunk‏ها)، ‏NewChunk‏ (انتخاب بازه)، ‏WaitPushes‏ و ‏WaitLastUpdate‏ (صبر برای ‏push‏ها و نوشتن‌های در جریان)، ‏BuildMap‏، ‏WaitReplicas‏ و مقایسه. پایان ‏PG‏ با ‏WaitDigestUpdate‏ است. سمت ‏replica‏ حالت‌های ‏ReplicaIdle‏، ‏ReplicaWaitUpdates‏ و ‏ReplicaBuildingMap‏ را دارد
۳۲-۳توالی یک ‏chunk. primary‏ فهرست اشیای بازه را از ‏PGBackend‏ می‌گیرد، مرز را طوری تنظیم می‌کند که ‏head‏ و ‏clone‏هایش در یک ‏chunk‏ بمانند، بازه را قفل می‌کند و به ‏replica‏ها ‏MOSDRepScrub‏ می‌فرستد. هر طرف نقشه‌ی خود را می‌سازد (در ‏deep‏، خواندن داده در گام‌های ۴ ‏MiB‏). سپس ‏primary‏ نقشه‌ها را مقایسه می‌کند. نوشتن مشتری در بازه‌ی قفل‌شده ‏chunk‏ را ‏preempt‏ می‌کند
۳۲-۴منطق انتخاب نسخه‌ی معتبر در ‏ScrubBackend::select_auth_object‏. نامزدها با خود ‏primary‏ شروع می‌شوند. هر ‏shard‏ با خطای خواندن، خطای ‏stat‏، ناهمخوانی ‏hash‏ یا اندازه‌ی ‏EC‏، یا ‏object_info‏ و ‏snapset‏ خراب رد می‌شود. از میان ‏shard‏های قابل‌استفاده، نسخه‌ی بزرگ‌تر ‏object_info‏ برنده است. در تساوی، ‏shard‏ی که ‏digest‏ بیشتری ثبت کرده برنده است، و در تساوی کامل، اولی (‏primary‏)
۳۲-۵حالت‌های ‏recovery‏ و ‏backfill‏ درون ‏Active‏ در ‏PeeringState.h‏. هر دو مسیر ابتدا رزرو محلی (روی ‏primary‏) و سپس رزرو راه دور (روی هر هدف) می‌گیرند. اگر رزرو رد یا پس گرفته شود، ‏PG‏ به ‏NotRecovering‏ یا ‏NotBackfilling‏ می‌رود. اگر هدف پر باشد، حالت ‏toofull‏ می‌گیرد و پس از ‏osd_recovery_retry_interval‏ یا ‏osd_backfill_retry_interval‏ (هر دو ۳۰ ثانیه) دوباره تلاش می‌کند
۳۲-۶بازه‌های اولویت در ‏osd_types.h‏. هر پایه یک سقف دارد (‏get_max_prio_for_base‏) تا اولویت ‏pool‏ یک ‏PG‏ را به رده‌ی بالاتر نبرد: ‏backfill‏ عادی ۱۰۰ تا ۱۳۹، ‏backfill‏ با کمبود ‏replica 140‏ تا ۱۷۹، ‏recovery 180‏ تا ۲۱۹، و ‏PG‏ غیرفعال (کمتر از ‏min_size) 220‏ تا ۲۵۳. مقادیر ۲۵۴ و ۲۵۵ برای ‏force-backfill‏ و ‏force-recovery‏ است. اولویت‌های حذف ‏PG‏ (۱۷۹، ۲۱۹، ۲۵۵) هم در همین صف رقابت می‌کنند
۳۲-۷توالی رزرو ‏backfill. primary‏ ابتدا یک خانه از ‏local_reserver‏ خودش و سپس با ‏MBackfillReserve REQUEST‏ یک خانه از ‏remote_reserver‏ هدف می‌گیرد. هر دو به ‏osd_max_backfills‏ محدودند. درخواست با اولویت بالاتر روی هدف، رزرو قابل‌‏preempt‏ کم‌اولویت‌تر را پس می‌گیرد و ‏primary‏ پیام ‏REVOKE‏ دریافت می‌کند
۳۲-۸منطق ادغام در ‏recover_backfill. primary‏ یک بازه از اشیای خودش (‏backfill_info‏) و از هر هدف یک بازه (‏peer_backfill_info‏) را با اسکن ۶۴ تا ۵۱۲ شیئی (‏osd_backfill_scan_min/max‏) نگه می‌دارد. هر دو به ترتیب ‏hobject‏ مرتب‌اند. شیئی که فقط روی هدف است حذف می‌شود. شیئی که نسخه‌اش فرق دارد ‏push‏ می‌شود. شیئی که نسخه‌اش برابر است نگه داشته می‌شود. اشیای پس از ‏last_backfill‏ هدف به‌عنوان ‏missing push‏ می‌شوند
۳۲-۹جریان ‏ceph pg repair‏. فوریت هدف ‏scrub‏ به ‏must_repair‏ می‌رود و ‏deep scrub‏ کامل اجرا می‌شود. برای هر شیء ناسازگار، ‏ScrubBackend::repair_object‏ با ‏force_object_missing shard‏های خراب را به فهرست ‏missing‏ اضافه می‌کند و فقط ‏shard‏های سالم را منبع ‏missing_loc‏ قرار می‌دهد. سپس سازوکار عادی ‏recovery‏ نسخه‌ی معتبر را ‏push‏ می‌کند
۳۳-۱مهم‌ترین ‏thread‏های ‏ceph-osd‏ با نامی که در ‏top‏ -‏H‏ یا ‏perf‏ دیده می‌شود. ‏msgr-worker-N‏ (از ‏Stack.h‏) پیام‌ها را می‌خوانند و رمزگشایی می‌کنند. ‏tp_osd_tp‏ (thread pool مربوط به ‏OSD::osd_op_tp) op‏ها را از ‏ShardedOpWQ‏ اجرا می‌کند. ‏bstore_aio‏ تکمیل ‏I/O‏ را از هسته می‌گیرد. ‏bstore_kv_sync‏ تراکنش‌ها را دسته‌ای در ‏RocksDB‏ ثبت می‌کند. ‏bstore_kv_final‏ و ‏cfin callback‏ها را اجرا می‌کنند. ‏bstore_mempool‏ حافظه را تنظیم می‌کند، ‏rocksdb:low/high‏ فشرده‌سازی و ‏flush‏ انجام می‌دهند و ‏osd_srv_heartbt‏ ضربان را می‌فرستد
۳۳-۲نمونه‌ای از هم‌نامی (‏aliasing‏) در نگاشت ‏PG‏ به ‏shard. PG‏هایی که ‏ps‏ آن‌ها در پیمانه‌ی ۸ برابر است (۳، ۱۱، ۱۹، ۲۷) همگی در ‏shard 3‏ قرار می‌گیرند. اگر این ‏PG‏ها پربار باشند، دو ‏thread‏ مربوط به ‏shard 3‏ اشباع می‌شوند در حالی که ۱۴ ‏thread‏ دیگر بی‌کارند
۳۳-۳ساختار یک ‏shard_t‏ در ‏mempool.h‏. هر ‏shard‏ آرایه‌ای از ‏stats_t‏ برای همه‌ی ‏pool‏هاست و با ‏aligned‏(۱۲۸) شروع می‌شود تا دو ‏shard‏ هرگز یک ‏cache line‏ را به اشتراک نگذارند (توضیح کد، ۱۲۸ بایت را بزرگ‌ترین اندازه‌ی ‏cache line‏ در پردازنده‌های شناخته‌شده می‌داند). مقدار کل یک ‏pool‏ جمع همه‌ی ‏shard‏هاست
۳۳-۴سطوح ‏tcmalloc‏. تخصیص کوچک از ‏cache‏ همان ‏thread‏ و بدون قفل انجام می‌شود. کمبود از ‏transfer cache‏ و ‏central free list‏ پر می‌شود و آن‌ها از ‏page heap span‏ می‌گیرند. حافظه‌ی آزاد ‏page heap‏ با ‏madvise‏ به سیستم‌عامل پس داده می‌شود و در شمارنده‌ی ‏pageheap_unmapped_bytes‏ دیده می‌شود. ‏PriorityCache‏ مقدار «‏mapped‏» را ‏heap_size‏ منهای همین ‏unmapped‏ حساب می‌کند
۳۳-۵شبیه‌سازی هشت دور ‏tune_memory‏ با پیش‌فرض‌ها و مقادیر ورودی فرضی برای ‏mapped‏. از ۱۵۰۰ ‏MiB‏، چون ‏mapped=3000‏ کمتر از هدف است، بودجه به ۱۸۲۴.۷ بالا می‌رود. در دور بعد ‏mapped=4500‏ از هدف بیشتر است و بودجه به ۱۶۷۲.۴ کاهش می‌یابد. بودجه هرگز از ‏max=2713.6‏ بالاتر و از ‏min=128‏ پایین‌تر نمی‌رود
۳۳-۶نتیجه‌ی مثال ‏balance‏ پس از ‏PRI0‏ و دو دور ‏PRI1. kv‏ به ۵۰۰ ‏MiB‏، ‏meta‏ به ۷۲۳.۳، ‏data‏ به ۹۶.۴ و ‏kv_onode‏ به ۵۰ مگابایت رسیده‌اند. چون ‏cache‏های ‏meta‏ و ‏data‏ هنوز داده‌ی تازه‌ی بیشتری از بودجه دارند، ‏bin‏های قدیمی‌ترشان (اولویت‌های پایین‌تر) در ‏commit‏ بعدی بیرون رانده می‌شوند
۳۳-۷تصمیم وابستگی ‏NUMA‏ در ‏OSD::set_numa_affinity. OSD‏ گره ‏NUMA‏ دستگاه ذخیره‌سازی (‏get_numa_node‏) و گره رابط‌های شبکه‌ی ‏public‏ و ‏cluster‏ (get_iface_numa_node) را پیدا می‌کند. اگر هر سه یکسان باشند و ‏osd_numa_auto_affinity=true‏ باشد، همه‌ی ‏thread‏ها به ‏CPU‏های همان گره محدود می‌شوند. ‏osd_numa_node‏ اگر صریحاً تنظیم شود، بر تصمیم خودکار مقدم است
۳۳-۸ابزارها به ترتیب لایه. درون ‏Ceph: dump_mempools‏، ‏heap stats‏، ‏perf dump‏ و ‏dump_historic_ops‏. نمونه‌برداری ‏CPU‏ با ‏perf‏ و ‏flame graph‏. ردیابی رویداد و زمان ‏off-CPU‏ با ‏bpftrace‏. ردیابی تابع‌به‌تابع با ‏uftrace‏. و پشته‌ی همه‌ی ‏thread‏ها با ‏gdb‏ یا ‏eu-stack‏ برای حالت گیرکرده
۳۳-۹چپ: بیشینه و میانگین طول ‏pg log‏ در ۳۲ ‏PG‏ ‏‏pool‏ ‏‏m33‏ برحسب تعداد نوشتن، با خط‌چین بودجه‌ی هر ‏OSD‏ (۳۰۰۰۰ تقسیم بر تعداد ‏PG‏ها). راست: حجم ‏mempool‏ ‏‏osd_pglog‏ (خط پر) و ‏bluestore_cache_onode‏ (خط‌چین) برای هر ‏OSD‏. خط نقطه‌چین مرز بازنویسی ۱۰۰۰ شیء و ساختن شیء تازه است
۳۳-۱۰هزینه‌ی واقعی هر آیتم (بایت تقسیم بر تعداد آیتم) در ‏mempool‏های اصلی ‏osd.0‏ روی کلاستر آزمایشی ‏Ceph 18.2.7‏، همراه با تعداد آیتم‌ها و حجم کل هر ‏mempool‏
۳۴-۱کلید بیتی و بازه‌ی ‏PG‏ها. پنج بیت پایین ‏hash‏ شماره‌ی ‏PG‏ را در ‏pg_num=32‏ تعیین می‌کنند. پس از برعکس کردن بیت‌ها، همین پنج بیت به بالای کلید می‌روند و هر ‏PG‏ یک بازه‌ی پیوسته در ترتیب ‏hobject_t‏ می‌شود. با دوبرابر شدن ‏pg_num‏، هر بازه دقیقاً از وسط نصف می‌شود
۳۴-۲مسیر ‏op‏ درون ‏OSD‏ از ‏messenger‏ تا ‏commit. ms_fast_dispatch‏ پیام را بدون قفل سراسری به ‏shard‏ می‌سپارد؛ ‏thread‏ همان ‏shard‏ قفل ‏PG‏ را می‌گیرد و ‏do_op‏ دروازه‌ها را بررسی می‌کند. اگر شرطی برقرار نباشد، ‏op‏ در صف انتظار مربوط می‌ماند. در غیر این صورت ‏execute_ctx‏ تراکنش را می‌سازد، ‏issue_repop‏ آن را به ‏replica‏ها می‌فرستد و پاسخ پس از ‏commit‏ همه‌ی اعضای ‏acting‏ ارسال می‌شود
۳۴-۳توالی یک نوشتن ‏replicated‏ سه‌نسخه‌ای. ‏primary‏ تراکنش را محلی ثبت و هم‌زمان برای دو ‏replica‏ می‌فرستد. هر عضو پس از ثبت در ‏WAL‏ مربوط به ‏BlueStore‏ پاسخ ‏ONDISK‏ می‌دهد. پاسخ به کلاینت فقط پس از رسیدن همه‌ی پاسخ‌ها فرستاده می‌شود؛ ‏min_size‏ فقط تعیین می‌کند ‏PG‏ با چند عضو اصلاً نوشتن را بپذیرد
۳۴-۴چیدمان کلید یک ورودی ‏pg log‏ در ‏omap‏ شیء ‏pgmeta‏، بایت به بایت. ده رقم ‏epoch‏ با صفر ابتدایی، یک نقطه و بیست رقم ‏version‏. کلید ‏dup‏ همان قالب را با پیشوند چهاربایتی ‏dup_‏ دارد (یادداشت زیر شکل). طول ثابت باعث می‌شود ترتیب رشته‌ای با ترتیب عددی یکی باشد
۳۵-۱زمان‌خط ‏interval‏های یک ‏PG‏. هر تغییر در ‏up‏ یا ‏acting‏ یا ‏min_size‏ یک ‏interval‏ تازه می‌سازد. فقط ‏interval‏هایی که ‏primary‏ در آن ‏up_thru‏ گرفته یا ‏PG‏ در آن ‏clean‏ شده ‏maybe_went_rw‏ هستند و در ‏prior set‏ حساب می‌شوند. ‏interval‏ی که ‏acting‏ آن از ‏min_size‏ کوچک‌تر بوده نادیده گرفته می‌شود
۳۵-۲ادغام ‏log‏ با ورودی ‏divergent‏. عضو ‏A‏ تا ۱۴۶۲'۹۵ نوشته ولی ‏log‏ معتبر عضو ‏B‏ تا ۱۴۶۲'۹۳ است و سپس در ‏epoch‏ جدید ۱۴۷۰'۹۴ و ۱۴۷۰'۹۵ دارد. ورودی‌های ۱۴۶۲'۹۴ و ۱۴۶۲'۹۵ روی ‏A divergent‏ هستند، برگردانده یا حذف می‌شوند و شیء مربوط با نسخه‌ی ‏prior‏ در ‏missing‏ می‌نشیند
۳۵-۳‏backfill‏ به‌صورت ادغام دو پیمایش مرتب. بازه‌ی ‏primary‏ و بازه‌ی هدف هم‌زمان جلو می‌روند. شیء موجود فقط در هدف حذف می‌شود، شیء فقط در ‏primary‏ یا با نسخه‌ی متفاوت ‏push‏ می‌شود، و ‏last_backfill‏ پس از هر دور جلو می‌رود
۳۶-۱تحول ‏SnapSet‏ یک شیء در سه گام. هر نخستین نوشتن پس از یک یا چند ‏snapshot‏ تازه یک ‏clone‏ با نام بزرگ‌ترین ‏snap‏ جدید می‌سازد. ‏clone_snaps‏ نشان می‌دهد هر ‏clone‏ نماینده‌ی کدام ‏snap‏هاست و ‏clone_overlap‏ بخش‌هایی را که هنوز با نسخه‌ی جدیدتر مشترک‌اند
۳۶-۲جریان حذف ‏snapshot. mon‏ شماره را به ‏removed_snaps_queue‏ اضافه می‌کند، هر ‏PG‏ از روی ‏SnapMapper clone‏های آن ‏snap‏ را پیدا و ‏trim‏ می‌کند و در پایان ‏mon‏ آن را ‏purged‏ می‌شمارد
۳۷-۱چیدمان یک ‏image‏ با ‏data pool‏ جدا. ‏pool‏ فراداده‌ی ‏replicated‏ شامل ‏rbd_id‏، ‏rbd_directory‏، ‏header‏ و ‏object map‏هاست و ‏pool‏ داده‌ی ‏EC‏ اشیای ‏rbd_data‏ با پیشوند شناسه‌ی ‏pool‏ فراداده را دارد. کلاینت‌ها روی ‏header‏ ‏‏watch‏ می‌کنند و قفل ‏exclusive‏ روی ‏xattr‏ همان ‏header‏ است
۳۷-۲‏striping‏ با ‏stripe_unit=64K‏ و ‏stripe_count=4‏. هر سطر یک ‏stripe‏ است و چهار ستون چهار شیء یک ‏object set‏. بلوک‌های ۱۵ تا ۱۹ که نوشتن مثال را می‌سازند رنگی شده‌اند
۳۷-۳کدگذاری ‏object map‏ یک ‏image‏ ده‌گیگابایتی با اشیای ۴ ‏MiB‏ (۲۵۶۰ شیء)، بایت به بایت. ‏header‏ هجده‌بایتی شامل طول، ‏struct_v‏ و ‏compat‏ و تعداد عناصر است؛ سپس ۶۴۰ بایت داده که هر بایت چهار شیء را با دو بیت نگه می‌دارد و در پایان ‏footer‏ با ‏crc‏ها
۳۸-۱لایه‌های ‏dispatch‏ در ‏librbd‏. درخواست تصویر از نُه لایه‌ی ‏image‏ عبور و در ‏CORE‏ با ‏Striper‏ به ‏extent‏های شیء شکسته می‌شود؛ هر ‏extent‏ سپس از شش لایه‌ی شیء عبور می‌کند تا به‌صورت ‏op‏ به ‏RADOS‏ برسد. لایه‌های غیرفعال (مثلاً ‏journal‏ یا ‏crypto‏) در زنجیره نیستند
۳۸-۲‏copyup‏ برای نخستین نوشتن روی شیء ۱۲۸۰ یک ‏clone. librbd‏ از ‏object map‏ می‌فهمد شیء وجود ندارد، شیء والد را با خواندن پراکنده می‌آورد، ‏object map‏ را به ‏EXISTS‏ می‌برد و ‏copyup‏ و نوشتن کاربر را در یک ‏op‏ اتمی به شیء ‏clone‏ می‌فرستد
۳۸-۳گرفتن ‏exclusive-lock‏. اگر قفل گرفته شده باشد، کلاینت ‏B‏ بررسی می‌کند مالک هنوز ‏watch‏ زنده دارد یا نه. در حالت زنده، با ‏notify‏ درخواست رها کردن می‌فرستد؛ در غیر این صورت ابتدا مالک را ‏blocklist‏ و سپس قفل را می‌شکند
۳۹-۱یک رویداد ‏journal‏، بایت به بایت. پیش‌درآمد ثابت هشت‌بایتی برای بازیابی مرز رویدادها، نسخه، شناسه‌ی رویداد و ‏tag‏، طول داده، داده‌ی رمزگذاری‌شده‌ی ‏EventEntry‏ در ‏librbd‏ و ‏crc32c‏ کل بخش قبلی
۳۹-۲دو حالت ‏mirroring‏ در کنار هم. در حالت ‏journal‏ هر نوشتن اول در ‏journal‏ مبدأ ثبت و ‏rbd-mirror‏ رویدادها را پیوسته روی مقصد ‏replay‏ می‌کند. در حالت ‏snapshot‏ زمان‌بند ‏snapshot mirror‏ می‌سازد و ‏rbd-mirror‏ فقط اشیای تغییرکرده میان دو ‏snapshot‏ را با ‏fast-diff‏ کپی می‌کند
۴۰-۱کلیدهای ویژه در ‏omap‏ یک ‏shard‏ ایندکس. کلید ‏instance‏ از ۰‏x80‏، پیشوند ‏۱۰۰۰_، نام، بایت صفر، حرف ‏i‏ و شناسه‌ی نسخه ساخته می‌شود؛ ‏OLH‏ فقط پیشوند ‏۱۰۰۱_ و نام دارد؛ ‏bilog‏ شماره‌ی ‏‏index_ver‏ صفرپرشده‌ی یازده‌رقمی دارد تا ترتیب واژگانی همان ترتیب عددی باشد
۴۰-۲توالی به‌روزرسانی دومرحله‌ای: ‏prepare‏ برچسب عملیات را در ‏‏pending_map‏ می‌گذارد، داده نوشته می‌شود، ‏complete‏ برچسب را برمی‌دارد و ورودی، آمار و ‏bilog‏ را به‌روز می‌کند. اگر ‏complete‏ نرسد، فهرست‌گیری بعدی وضعیت ‏head‏ را بررسی و با ‏dir_suggest_changes‏ ورودی را اصلاح می‌کند
۴۰-۳چیدمان یک شیء ۱۰ ‏MiB‏ در دو حالت. در ‏STANDARD‏، ‏head‏ چهار مگابایت اول را دارد و دو ‏tail‏ بقیه را. در ‏COLD‏، ‏head‏ فقط ‏manifest‏ و ‏xattr‏ها را در ‏pool‏ ‏‏STANDARD‏ نگه می‌دارد و همه‌ی داده در سه ‏tail‏ در ‏pool‏ ‏‏COLD‏ است
۴۱-۱خط لوله‌ی یک درخواست در ‏RGW: Beast‏، انتخاب ‏handler‏ و ‏op‏، احراز هویت، خواندن مجوزها، بررسی‌ها، محدودیت نرخ، اجرا و تکمیل. خطای هر مرحله مستقیم به پاسخ ‏HTTP‏ می‌رود
۴۱-۲مشتق‌گیری کلید ‏SigV4‏: چهار ‏HMAC‏ زنجیره‌ای از کلید محرمانه با تاریخ، ‏region‏، سرویس و ‏aws4_request‏، و ‏HMAC‏ پنجم روی ‏string to sign‏ که خودش هش ‏canonical request‏ را دارد
۴۱-۳زمان‌بندی ‏gc‏ و ‏lifecycle‏. حذف شیء زنجیره‌ی ‏tail‏ را در صف یک شیء ‏gc.N‏ با زمان انقضای دوساعته می‌گذارد و نخ ‏gc‏ پس از قفل کردن ‏shard‏ آن را پاک می‌کند. نخ ‏lifecycle‏ در پنجره‌ی کاری روزانه ‏shard‏های ‏lc.N‏ را قفل می‌کند و برای هر ‏bucket‏ قواعد را اجرا می‌کند
۴۲-۱زنجیره‌ی سه لاگ برای یک ‏PUT‏ در ‏zone‏ مبدأ. ‏complete‏ در ایندکس یک ورودی ‏bilog‏ می‌نویسد، ‏RGW‏ کلید ‏bucket shard‏ را در ‏datalog‏ ثبت می‌کند و ‏zone‏ مقصد ‏datalog‏ را می‌خواند، ‏bilog‏ همان ‏shard‏ را از ‏marker‏ خود به بعد می‌گیرد و شیء را با ‏fetch_remote_obj‏ کپی می‌کند. تغییرهای فراداده جداگانه از ‏mdlog‏ ‏‏period‏ جاری خوانده می‌شوند
۴۲-۲انتقال میان نسل‌های ‏bilog‏ پس از ‏reshard‏: نسل ۰ با ۱۱ ‏shard‏ و نسل ۱ با ۲۳ ‏shard. zone‏ مقصد همه‌ی ‏shard‏های نسل ۰ را تا انتها همگام و در ‏shards_done_with_gen‏ علامت می‌زند و سپس ‏incremental_gen‏ را به ۱ می‌برد؛ لاگ نسل ۰ فقط پس از اتمام همه‌ی ‏peer‏ها ‏trim‏ می‌شود