DDIA·مطالعهSTUDY

نقشه راهRoadmap/ فصل ۲Chapter 2

تعریف نیازمندی‌های غیرعملکردیDefining nonfunctional requirements

در این فصل کیفیتِ سرویس را به سؤال‌های قابل‌سنجش تبدیل می‌کنیم: پاسخ چقدر طول می‌کشد، سیستم چه مقدار بار تحمل می‌کند و هنگام خرابی چه اتفاقی می‌افتد؟ هدف، حفظ کردن عددها نیست؛ باید بدانیم هر عدد چه تجربه‌ای برای کاربر می‌سازد. A focused distillation for learning and interviews: performance, reliability, scalability and maintainability — with the famous social-timeline case study, interactive Archify diagrams and a question pack.

۹9 نمودار تعاملیinteractive diagrams· محور: performance · reliability · scalability · maintainabilityTheme: performance · reliability · scalability · maintainability· زمان مطالعه: حدود ۶۰ دقیقهReading time: ~60 min

۰0 نیازمندی غیرعملکردی چیست؟What are nonfunctional requirements?

ایدهٔ کلیدی فصلChapter thesis

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

مثال فنی: نیاز قابل‌آزمون برای جست‌وجو

وضعیت اولیه: هدف فرضی API این است: با 1000 req/s، حداقل ۹۹٪ درخواست‌های موفق زیر 300 ms تمام شوند.

اجرای مرحله‌به‌مرحله: آزمون بار، نرخ ورودی، خطا و توزیع زمان پاسخ را با هم ثبت می‌کند؛ صرفِ پاسخ سریع به ۱۰ درخواست معیار نیست.

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

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

A functional requirement says what the software must do: which pages and buttons, and what each operation returns. A nonfunctional requirement says how those things happen: fast, reliable, secure, compliant, and maintainable. These may go unwritten and look “obvious”, but they matter as much as features: software that is unbearably slow or unstable effectively does not exist.

این فصل روی چهار مورد از این نیازمندی‌ها متمرکز است (بقیه مثل security خارج از دامنهٔ کتاب‌اند):

This chapter focuses on four of these requirements (the rest, like security, are outside the book’s scope):

  • Performance: تعریف و اندازه‌گیری سرعت سیستم
  • Reliability: ادامهٔ کار درست، حتی وقتی اشتباهی می‌افتد
  • Scalability: افزودن مؤثرِ ظرفیت محاسبه با رشد بار
  • Maintainability: آسان‌کردن نگهداری بلندمدت سیستم
  • Performance: defining and measuring system speed
  • Reliability: continuing to work correctly, even when something goes wrong
  • Scalability: effectively adding compute capacity as load grows
  • Maintainability: making long-term maintenance easy

اصطلاحات این فصل پایهٔ فصل‌های بعدی است؛ برای همین کتاب با یک بررسی یک نمونه عینی شروع می‌کند.

This chapter’s vocabulary underpins the later chapters; that is why the book starts with a concrete case study.

نقشهٔ چهار ستون — نیازمندی‌های غیرعملکردی این فصلFour pillars — this chapter’s nonfunctional requirements باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: نیاز قابل‌آزمون برای جست‌وجو

وضعیت اولیه: هدف فرضی API این است: با 1000 req/s، حداقل ۹۹٪ درخواست‌های موفق زیر 300 ms تمام شوند.

اجرای مرحله‌به‌مرحله: آزمون بار، نرخ ورودی، خطا و توزیع زمان پاسخ را با هم ثبت می‌کند؛ صرفِ پاسخ سریع به ۱۰ درخواست معیار نیست.

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

این نقشه را مثل چهار ستون کنار هم بخوانید. ستون اول (Performance): زمان پاسخ، توان عملیاتی، صدک‌ها و SLO/SLA — موضوع بخش ۲. ستون دوم (Reliability): فرق خرابی جزء و خرابی کل، نبود نقطهٔ شکست واحد، تزریق خرابی و مرور بی‌سرزنش — بخش ۳. ستون سوم (Scalability): توصیف بار، مقیاس عمودی یا افقی، بازاندیشی با هر ۱۰ برابر رشد — بخش ۴. ستون چهارم (Maintainability): قابلیت اجرا، سادگی و تکامل‌پذیری — بخش ۵. پانوشت پایین شکل هم مرز کتاب را می‌گوید: امنیت و انطباق بیرون از دامنه‌اند.

Read this map as four pillars side by side. First pillar (Performance): response time, throughput, percentiles and SLO/SLA — section 2. Second (Reliability): fault vs failure, no single point of failure, fault injection and blameless reviews — section 3. Third (Scalability): describing load, scaling up or out, rethinking at every 10x — section 4. Fourth (Maintainability): operability, simplicity and evolvability — section 5. The footnote states the book’s boundary: security and compliance are out of scope.

۱1 بررسی یک نمونه: تایم‌لاین خانگی شبکهٔ اجتماعیCase study: a social network's home timeline

ایدهٔ کلیدیKey idea

صفحهٔ خانهٔ یک شبکهٔ اجتماعی باید پست‌های افرادِ دنبال‌شده را جمع کند. مسئله این است که این جمع‌کردن را هنگام انتشار پست انجام دهیم یا هنگام باز شدن صفحه.

مثال فنی: ساخت تایم‌لاین یک کاربر

وضعیت اولیه: کاربر u1 دویست نفر را دنبال می‌کند و صفحه ۲۰ پست می‌خواهد.

اجرای مرحله‌به‌مرحله: در مسیر خواندن، پست‌های آن ۲۰۰ نفر جمع و مرتب می‌شوند؛ در مسیر پیش‌ساخته، شناسهٔ پست‌ها قبلاً وارد فهرست u1 شده‌اند.

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

We want to build an X-style (formerly Twitter) network: users post and follow each other. The main read operation is the home timeline: the latest posts from people you follow. Everything lives in one relational database with three tables: users, posts, follows.

اعداد بار کاری (برای فهمِ اثرِ بار بر طراحی)Workload numbers (memorize these — interviews ask for them)

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

مثال فنی: محاسبهٔ بار polling

وضعیت اولیه: یک میلیون کاربر فعال هر 5 s صفحه را تازه می‌کنند.

اجرای مرحله‌به‌مرحله: نرخ درخواست 1,000,000 / 5 = 200,000 req/s است؛ با دنبال کردن ۲۰۰ نفر، هر درخواست ممکن است دادهٔ چندین نویسنده را ترکیب کند.

نتیجه و دلیل: تعداد کاربران به‌تنهایی ظرفیت لازم را نمی‌گوید؛ فاصلهٔ polling و هزینهٔ هر درخواست هم تعیین‌کننده‌اند.

  • روزانه ۵۰۰ میلیون پست ≈ میانگین ۵,۸۰۰ پست/ثانیه؛ اوج رویدادهای خاص تا ۱۵۰,۰۰۰ پست/ثانیه.
  • هر کاربر به‌طور میانگین ۲۰۰ نفر را دنبال می‌کند و ۲۰۰ فالوور دارد (اما توزیع بسیار نامتقارن است: اکثر افراد فالوور کم، چند سلبریتی بیش از ۱۰۰ میلیون).
  • 500 million posts per day ≈ an average of 5,800 posts/second; special-event peaks up to 150,000 posts/second.
  • Each user follows 200 people on average and has 200 followers (but the distribution is heavily skewed: most people have few followers, a few celebrities have over 100 million).

نمودار ER — اسکیمای شبکهٔ اجتماعیER diagram — the social-network schema

نمودار ER (Entity-Relationship) — جدول‌ها و کلیدهای خارجیER diagram — tables and foreign keys باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: ساخت تایم‌لاین یک کاربر

وضعیت اولیه: کاربر u1 دویست نفر را دنبال می‌کند و صفحه ۲۰ پست می‌خواهد.

اجرای مرحله‌به‌مرحله: در مسیر خواندن، پست‌های آن ۲۰۰ نفر جمع و مرتب می‌شوند؛ در مسیر پیش‌ساخته، شناسهٔ پست‌ها قبلاً وارد فهرست u1 شده‌اند.

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

این یک نمودار ER است: جعبه‌ها «موجودیت» (جدول)‌اند و فلش‌ها رابطه‌های کلید خارجی با کاردینالیتی. از جعبهٔ سمت چپ شروع کنید: «users» با زیرنویسِ «PK user_id, name». دو فلش از آن بیرون می‌آید: «1..N writes» به بالا، به جدولِ «posts» («PK post_id, FK sender_id, ts»)، و «1..N follower» به پایین، به جدولِ «follows».

جدولِ follows برچسبِ «join table» دارد و زیرنویسش دو کلید خارجی را نشان می‌دهد: follower_id و followee_id. فلشِ چین‌چینِ «N..1 followee» از follows به users دقیقاً همین را می‌گوید: هر ردیفِ follows به دو کاربر اشاره می‌کند، یکی به‌عنوان دنبال‌کننده و یکی دنبال‌شونده — همین، کارِ کوئریِ تایم‌لاین را گران می‌کند.

حالا سمت راست، جعبهٔ «Home Timeline» را ببینید با برچسبِ «derived view». دو فلشِ پررنگ به آن می‌رسند: «JOIN follows» از posts و «JOIN posts» از follows. این دقیقاً همان کوئریِ گرانِ بالاست: ۲ میلیون کوئری در ثانیه × ۲۰۰ فالوور ≈ ۴۰۰ میلیون lookup در ثانیه. و چون «derived» است، می‌توانیم آن را از پیش بسازیم — همان راه‌حلی که در نمودار بعدی می‌بینید.

This is an ER diagram: boxes are entities (tables) and arrows are foreign-key relationships with cardinality. Start from the left box: “users”, subtitled “PK user_id, name”. Two arrows leave it: “1..N writes” upward to the “posts” table (“PK post_id, FK sender_id, ts”), and “1..N follower” downward to the “follows” table.

The follows table is tagged “join table”, and its subtitle shows two foreign keys: follower_id and followee_id. The dashed “N..1 followee” arrow from follows to users says exactly that: each follows row points at two users, one as follower and one as followee — and that is what makes the timeline query expensive.

Now look right, at the “Home Timeline” box tagged “derived view”. Two bold arrows reach it: “JOIN follows” from posts and “JOIN posts” from follows. That is precisely the expensive query above: 2M queries/second × 200 followers ≈ 400M lookups/second. And because it is “derived”, we can precompute it — the fix you will see in the next diagram.

راه‌حل اولیه: کوئری JOIN + Polling (چرا شکست می‌خورد)First attempt: JOIN query + polling (why it fails)

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

مثال فنی: پرس‌وجوی تکراری بدون تغییر داده

وضعیت اولیه: کاربر u1 هر ۵ ثانیه پرس‌وجوی پست‌های افراد دنبال‌شده را اجرا می‌کند؛ در یک دقیقه پست تازه‌ای منتشر نشده است.

اجرای مرحله‌به‌مرحله: ۱۲ بار همان join و مرتب‌سازی انجام می‌شود؛ cache یا ارسال تغییر می‌تواند محاسبهٔ تکراری را کاهش دهد.

نتیجه و دلیل: polling همیشه ممنوع نیست؛ در این بار، هزینهٔ تکرارِ بی‌تغییر علت فشار است.

The timeline query is a JOIN between posts and follows, ordered by time, with LIMIT 1000. If each client runs it every 5 seconds (polling) with 10 million users online:

  • ۲ میلیون کوئری تایم‌لاین در ثانیه (۱۰ میلیون ÷ ۵ ثانیه)
  • هر کوئری باید پست‌های ۲۰۰ نفر را جمع کند ← ۴۰۰ میلیون lookup در ثانیه — آن هم در حالت میانگین!
  • کاربرانی که ده‌ها هزار نفر را دنبال می‌کنند، عملاً این کوئری را غیرممکن می‌کنند.
  • 2 million timeline queries per second (10M ÷ 5s)
  • Each query must gather 200 people’s posts → 400M lookups/second — and that is the average case!
  • Users following tens of thousands of accounts make this query effectively impossible.

راه‌حل بهتر: Materialization و Fan-out on WriteBetter fix: materialization and fan-out on write

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

مثال فنی: Fan-out در لحظهٔ انتشار

وضعیت اولیه: پست p9 نویسنده‌ای با ۱۰۰۰ دنبال‌کننده دارد.

اجرای مرحله‌به‌مرحله: worker شناسهٔ p9 را به ۱۰۰۰ timeline اضافه می‌کند؛ خواننده بعداً فقط فهرست آماده را می‌خواند.

نتیجه و دلیل: یک نوشتنِ اولیه ۱۰۰۰ نوشتنِ مشتق می‌سازد. برای نویسندهٔ بسیار پرطرفدار می‌توان ادغام هنگام خواندن را جدا بررسی کرد.

Instead of assembling data at read time, precompute the query result and keep it:

  1. Push به‌جای Poll: سرور پست‌های تازه را به فالوورهای آنلاین هل بدهد.
  2. Materialized View: برای هر کاربر یک تایم‌لاینِ از پیش محاسبه‌شده نگه دار (مثل رساندن نامه به صندوق پستی).
  3. Fan-out: وقتی کسی پست می‌گذارد، آن پست در تایم‌لاینِ هر فالوور درج می‌شود. نسبتِ درخواست‌های پایین‌دستی به درخواست اولیه، fan-out factor نامیده می‌شود.
  4. محاسبه: ۵,۸۰۰ پست/ثانیه × ۲۰۰ فالوور ≈ ۱.۱۶ میلیون نوشتنِ تایم‌لاین در ثانیه — بله زیاد است، اما در برابر ۴۰۰ میلیون lookup/ثانیه، صرفه‌جویی عظیمی است.
  5. در اوج بار، تحویل‌ها را در صف بگذار (enqueue)؛ تایم‌لاین‌ها همچنان سریع بارگذاری می‌شوند چون از کش می‌آیند.
  1. Push instead of poll: the server pushes fresh posts to online followers.
  2. Materialized view: keep a precomputed timeline per user (like delivering letters to mailboxes).
  3. Fan-out: when someone posts, that post is inserted into every follower’s timeline. The ratio of downstream requests to the original request is the fan-out factor.
  4. Math: 5,800 posts/s × 200 followers ≈ 1.16M timeline writes/second — a lot, yes, but a huge saving next to 400M lookups/second.
  5. At peak load, queue the deliveries (enqueue); timelines still load fast because they come from cache.
حالت‌های حدی (Extreme Cases)Edge cases
  • کاربر با دنبال‌شونده‌های بسیار زیاد: چون همه‌چیز را نمی‌خواند، می‌توان بخشی از نوشتن‌ها را حذف کرد (نمونه‌برداری) — قابل قبول است.
  • سلبریتی با میلیون‌ها فالوور: حذف نوشتن‌ها قابل قبول نیست. راه‌حل: پست‌های سلبریتی را جدا ذخیره کن و هنگام خواندن با تایم‌لاینِ متریالایزشده ادغام کن.
  • User following a huge number of accounts: since they never read everything, some writes can be dropped (sampling) — acceptable.
  • Celebrity with millions of followers: dropping writes is not acceptable. Fix: store celebrity posts separately and merge them with the materialized timeline at read time.
نمودار ۱ · Sequence — fan-out on write و تایم‌لاین متریالایزشدهDiagram 1 · Sequence — fan-out on write and the materialized timeline باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: Fan-out در لحظهٔ انتشار

وضعیت اولیه: پست p9 نویسنده‌ای با ۱۰۰۰ دنبال‌کننده دارد.

اجرای مرحله‌به‌مرحله: worker شناسهٔ p9 را به ۱۰۰۰ timeline اضافه می‌کند؛ خواننده بعداً فقط فهرست آماده را می‌خواند.

نتیجه و دلیل: یک نوشتنِ اولیه ۱۰۰۰ نوشتنِ مشتق می‌سازد. برای نویسندهٔ بسیار پرطرفدار می‌توان ادغام هنگام خواندن را جدا بررسی کرد.

این یک نمودار Sequence است: شش شرکت‌کننده از چپ به راست، زیر سه بخشِ جدا. اول همه را نام ببریم: «Author» با زیرنویسِ «posts 5,800/s»، «Publish API» («ingest post»)، «Posts DB» («system of record»)، «Fan-out Worker» («per-follower insert»)، «Timeline Cache» («materialized view») و «Reader App» («home timeline»). سه بخشِ بالای جدول هم این‌ها هستند: «Publish»، «Fan-out on write» و «Read home timeline».

از بخشِ اول، «Publish»، شروع کنید. فلشِ پررنگِ «POST /posts» از Author به Publish API می‌رود، بعد فلشِ پررنگِ «insert post» از Publish API به Posts DB — که با زیرنویسِ «system of record» مشخص شده، یعنی سیستم مرجعِ نوشتنِ پست. فلشِ برگشتیِ «post id» از Posts DB به Publish API برمی‌گردد و مسیرِ نوشتن با فلشِ برگشتیِ «201 created» به Author بسته می‌شود.

بخشِ دوم، «Fan-out on write»: فلشِ چین‌چینِ «enqueue delivery» از Publish API به Fan-out Worker می‌رود — یعنی تحویلِ پست در queue گذاشته می‌شود و صف تا اوجِ ۱۵۰,۰۰۰ پست/ثانیه نوسان را جذب می‌کند («queue absorbs spikes up to 150,000 posts/s»). بعد فلشِ پررنگِ «insert per follower» از Fan-out Worker به Timeline Cache می‌رود: پست در تایم‌لاینِ هر فالوور درج می‌شود — برچسبِ کنارِ فلش می‌گوید «fan-out factor 200: about 1.16M timeline writes/s»، یعنی عامل ۲۰۰ و تقریباً ۱.۱۶ میلیون نوشتنِ تایم‌لاین در ثانیه. فلشِ برگشتیِ «ack» هم از Timeline Cache به Fan-out Worker برمی‌گردد.

بخشِ سوم، «Read home timeline»: فلشِ پررنگِ «GET home timeline» از Reader App به Timeline Cache می‌رود و برگشتیِ «1,000 recent posts» هزار پستِ آخر را می‌آورد. برچسبِ کنارِ این مسیر می‌گوید «served from cache, no joins»: خواندنِ تایم‌لاین فقط یک hit روی کش است — نه JOIN، نه مرتب‌سازی. و فلشِ چین‌چینِ آخر، «push new items»، مستقیم از Fan-out Worker به Reader App می‌رود: «push replaces polling every 5s» — هل دادنِ پست‌های تازه به‌جای پرس‌وجوی هر ۵ ثانیه.

درسِ شکل در دو فلشِ چین‌چینِ «enqueue delivery» و «push new items» است: هر دو async‌اند (در legend با «async trace» مشخص شده‌اند) — صف در اوج بار و push به‌جای polling. نتیجه همان trade-off معروف است: خواندن ارزان و نوشتن گران؛ ۱.۱۶ میلیون نوشتن/ثانیه را می‌پذیریم تا ۴۰۰ میلیون lookup/ثانیه را حذف کنیم.

This is a sequence diagram: six participants left to right, under three separate bands. First, name them all: “Author” (“posts 5,800/s”), “Publish API” (“ingest post”), “Posts DB” (“system of record”), “Fan-out Worker” (“per-follower insert”), “Timeline Cache” (“materialized view”) and “Reader App” (“home timeline”). The three bands above are: “Publish”, “Fan-out on write” and “Read home timeline”.

Start with the first band, “Publish”. The bold “POST /posts” arrow goes from Author to Publish API, then the bold “insert post” arrow from Publish API to Posts DB — subtitled “system of record”, i.e. the authoritative place posts are written. The “post id” return arrow comes back from Posts DB to Publish API, and the write path closes with “201 created” back to Author.

Second band, “Fan-out on write”: the dashed “enqueue delivery” arrow goes from Publish API to Fan-out Worker — post delivery is queued, and the queue absorbs spikes up to 150,000 posts/s. Then the bold “insert per follower” arrow runs from Fan-out Worker to Timeline Cache: the post is inserted into every follower’s timeline — the label reads “fan-out factor 200: about 1.16M timeline writes/s”. The “ack” return arrow goes from Timeline Cache back to Fan-out Worker.

Third band, “Read home timeline”: the bold “GET home timeline” arrow goes from Reader App to Timeline Cache, and the “1,000 recent posts” return brings the latest thousand posts. The label on this path reads “served from cache, no joins”: reading the timeline is a single cache hit — no JOIN, no sorting. And the last dashed arrow, “push new items”, runs straight from Fan-out Worker to Reader App: “push replaces polling every 5s”.

The lesson sits in the two dashed arrows, “enqueue delivery” and “push new items”: both are async (marked “async trace” in the legend) — a queue for peak load, push instead of polling. The result is the famous trade-off: cheap reads, expensive writes; we accept 1.16M writes/second to delete 400M lookups/second.

نکتهٔ مصاحبه (سیستم‌دیزاین)Interview tip (system design)

این کیس دقیقاً سوال معروفِ «Design Twitter Timeline» است. جواب قوی سه لایه دارد: ۱) اعداد را حساب کن (load)، ۲) trade-off را بگو (materialized view: خواندن ارزان، نوشتن گران)، ۳) حالت حدی را فراموش نکن (سلبریتی و کاربران پرمصرف). همین الگو در دامنه‌های دیگر هم تکرار می‌شود: مثلاً گزارش‌های مالی دوره‌ای را «متریالایز» می‌کنند تا خواندن سریع بماند و هزینه‌اش را هنگام نوشتن می‌پردازند.

This case is exactly the famous “Design Twitter Timeline” question. A strong answer has three layers: 1) do the math (load), 2) state the trade-off (materialized view: cheap reads, expensive writes), 3) don’t forget the edge case (celebrities and heavy users). The same pattern repeats elsewhere: e.g. periodic financial reports are “materialized” to keep reads fast, paying the cost at write time.

۲2 توصیف PerformanceDescribing performance

Response Time در برابر ThroughputResponse time vs throughput

معیارتعریفمثال بررسی یک نمونه
Response Timeزمان سپری‌شده از درخواست کاربر تا دریافت جواب (ثانیه/میلی‌ثانیه)زمان بارگذاری تایم‌لاین
Throughputتعداد درخواست یا حجم داده در ثانیه؛ برای هر سخت‌افزار، سقفی داردپست/ثانیه، نوشتن تایم‌لاین/ثانیه
MetricDefinitionCase-study example
Response TimeElapsed time from user request to answer (seconds/milliseconds)Timeline load time
ThroughputRequests or data volume per second; every hardware has a ceilingPosts/second, timeline writes/second
زیر ذره‌بینUnder the lens
  • رابطهٔ این دو: با افزایش بار، response time بالا می‌رود؛ دلیلش صف‌کشی (queueing) است. وقتی نزدیک ظرفیت سخت‌افزار می‌شوی، تأخیر صف به‌شدت منفجر می‌شود.
  • متغیرهای چهارگانه: response time (آنچه کلاینت می‌بیند) = service time (پردازش فعال) + queueing delay (انتظار) + network latency (سفر در شبکه).
  • نوسان (jitter): حتی یک درخواست تکراری هم زمان متفاوتی دارد: جابه‌جایی context، از دست رفتن بسته و retransmission، توقفِ garbage collection، page fault، حتی لرزش مکانیکی رک!
  • Head-of-line blocking: چون سرور فقط چند کار را موازی می‌کند، چند درخواست کند می‌توانند پشتِ خود صف بسازند؛ بقیه با اینکه service time کمی دارند، کند دیده می‌شوند. ← response time را سمت کلاینت اندازه بگیر، نه سمت سرور.
  • How they relate: as load grows, response time rises; the cause is queueing. Near hardware capacity, queue delay explodes.
  • The four parts: response time (what the client sees) = service time (active processing) + queueing delay (waiting) + network latency (travel over the network).
  • Jitter: even a repeated request takes different times: context switches, lost packets and retransmission, garbage collection pauses, page faults, even a rack’s mechanical vibration!
  • Head-of-line blocking: since a server parallelizes only a few tasks, a few slow requests queue up behind themselves; the rest look slow despite a small service time. → Measure response time at the client, not the server.
وقتی سیستمِ اضافه‌بار از افتادن بلند نمی‌شود: Metastable FailureWhen an overloaded system never recovers: metastable failure

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

مثال فنی: زمان پاسخ و ظرفیت

وضعیت اولیه: یک worker هر درخواست را در 20 ms پردازش می‌کند، اما درخواست 180 ms در صف می‌ماند.

اجرای مرحله‌به‌مرحله: زمان پاسخِ سمت سرور 200 ms است؛ با چند worker ممکن است مجموعاً 1000 req/s تکمیل شود.

نتیجه و دلیل: service time همان زمان اجرای کار است؛ response time انتظار را هم دارد. throughput تعداد کارِ تمام‌شده در واحد زمان است.

Long queue → high response time → client timeouts → retries → more load. This vicious cycle (retry storm) can keep the system overloaded even after load returns to normal. Fixes: exponential backoff (growing, randomized retry gaps), circuit breaker or token bucket (temporarily stop sending to the broken service), load shedding (deliberately rejecting requests) and backpressure (tell clients to slow down).

نمودار وضعیت — از اضافه‌بار تا فروپاشی پایدارState diagram — from overload to stable collapse

نمودار وضعیت (State Machine) — حلقهٔ بازخوردیِ retryState machine — the retry feedback loop باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: حلقهٔ اضافه‌بار و retry

وضعیت اولیه: ظرفیت مصرف ۱۰۰ درخواست در ثانیه است، ولی ۱۲۰ درخواست می‌رسد.

اجرای مرحله‌به‌مرحله: صف و timeout زیاد می‌شود؛ اگر ۳۰ درخواست هم فوراً retry شوند، ورودی به ۱۵۰ می‌رسد. admission control، backoff و بودجهٔ retry باید ورودی را مهار کنند.

نتیجه و دلیل: صف بزرگ‌تر فقط زمان می‌خرد؛ برای خروج از چرخه باید بارِ پذیرفته‌شده با ظرفیت سازگار شود.

این شکل یک ماشین وضعیت است و باید خط‌به‌خط خوانده شود. از ردیفِ بالا شروع کنید، «Service health»: جعبهٔ آغازِ «Healthy» با زیرنویسِ «load within capacity». فلشِ «load grows» به «Near Overload» می‌رسد («throughput at capacity»)، و از آن‌جا فلشِ پررنگِ «near capacity» ما را به ردیفِ وسطی می‌اندازد: «Overload spiral».

در این لین سه جعبه پشت‌سرهم‌اند: «Queueing» («response time rises») ← فلشِ «delay exceeds timeout» → «Client Timeouts» («requests abandoned») ← فلشِ چین‌چینِ «clients retry» → «Retry Storm» («requests multiply»). حالا ترسناک‌ترین فلشِ شکل: «more load» از «Retry Storm» دوباره به «Queueing» برمی‌گردد. این یک حلقهٔ بازخوردی است که خودش را تغذیه می‌کند.

سه راه خروج از این مارپیچ دیده می‌شود. فلشِ امنیتیِ «levers applied» به لینِ «Mitigations» می‌رود: جعبهٔ «Mitigation On» با زیرنویسِ «backoff, breaker, shedding» و بعد «queue drains» به «Recovered». اما فلشِ «no reset» مسیرِ دیگری است: به «Metastable Failure» می‌رسد — جعبه‌ای که زیرنویسش می‌گوید «persists after load drops». یعنی حتی بعد از رفعِ بار هم سیستم بلند نمی‌شود؛ همان‌طور که فلشِ چین‌چینِ «reboot / reset» نشان می‌دهد، گاهی فقط یک ری‌استارت آن را به «Healthy» برمی‌گرداند.

This is a state machine — read it line by line. Start from the top lane, “Service health”: the “Healthy” start box (“load within capacity”). The “load grows” arrow reaches “Near Overload” (“throughput at capacity”), and from there the bold “near capacity” arrow drops us into the middle lane: “Overload spiral”.

In this lane three boxes sit in sequence: “Queueing” (“response time rises”) → “delay exceeds timeout” → “Client Timeouts” (“requests abandoned”) → dashed “clients retry” → “Retry Storm” (“requests multiply”). Now the scariest arrow: “more load” loops from “Retry Storm” back to “Queueing”. A feedback loop feeding itself.

Three exits leave this spiral. The “levers applied” guard arrow goes to the “Mitigations” lane: the “Mitigation On” box (“backoff, breaker, shedding”), then “queue drains” to “Recovered”. But the “no reset” arrow takes another path: to “Metastable Failure” — subtitled “persists after load drops”. Even after load is gone, the system never recovers; as the dashed “reboot / reset” arrow shows, sometimes only a restart returns it to “Healthy”.

میانگین در برابر PercentileMean vs percentiles

میانگین ممکن است تجربهٔ بدِ بخشی از کاربران را پنهان کند. صدک می‌گوید چه سهمی از درخواست‌ها زیر یک زمان مشخص پاسخ گرفته‌اند.

مثال فنی: میانگین، کندترین‌ها را پنهان می‌کند

وضعیت اولیه: از ۱۰۰۰ درخواست، ۹۹۰ مورد 10 ms و ۱۰ مورد 1000 ms طول می‌کشند.

اجرای مرحله‌به‌مرحله: میانگین 19.9 ms است؛ با روش nearest-rank، p99=10 ms و p99.9=1000 ms می‌شود.

نتیجه و دلیل: هدفِ صدک باید مشخص باشد؛ میانگین خوب می‌تواند با تجربهٔ بدِ گروهی از کاربران همراه باشد.

Since response time is a distribution, not a number, we must treat it as one:

  • میانگین (mean): برای تخمین سقف throughput مفید است، اما «معمولی بودن» تجربهٔ کاربر را نشان نمی‌دهد.
  • میانه / p50: نصف درخواست‌ها سریع‌تر و نصف کندتر — بهترین معیار برای «کاربر معمولی چقدر منتظر می‌ماند».
  • p95، p99، p999: برای دیدن بدترین‌ها (outlier). مثلاً p95 = ۱.۵ ثانیه یعنی ۵ درخواست از هر ۱۰۰ تا، ۱.۵ ثانیه یا بیشتر.
  • Tail latency: درصدهای بالا را می‌گویند. آمازون الزام سرویس‌های داخلی‌اش را روی p99.9 می‌گذارد چون کندترین کاربران، پرارزش‌ترین مشتریان‌اند (سابقهٔ خرید زیاد). بهینه‌سازی p99.99 گران و کم‌بازده بود.
  • Mean: useful for estimating the throughput ceiling, but it does not show how “typical” the user experience is.
  • Median / p50: half of requests faster, half slower — the best metric for “how long does a typical user wait”.
  • p95, p99, p999: for seeing the worst cases (outliers). E.g. p95 = 1.5s means 5 requests in 100 take 1.5s or more.
  • Tail latency: the high percentiles. Amazon pins internal services at p99.9 because the slowest users are the most valuable customers (long purchase history). Optimizing p99.99 proved expensive and low-yield.
توزیع تأخیر — میانگین پنهان می‌کند، صدک آشکارLatency distribution — the mean hides, the percentile reveals باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: میانگین، کندترین‌ها را پنهان می‌کند

وضعیت اولیه: از ۱۰۰۰ درخواست، ۹۹۰ مورد 10 ms و ۱۰ مورد 1000 ms طول می‌کشند.

اجرای مرحله‌به‌مرحله: میانگین 19.9 ms است؛ با روش nearest-rank، p99=10 ms و p99.9=1000 ms می‌شود.

نتیجه و دلیل: هدفِ صدک باید مشخص باشد؛ میانگین خوب می‌تواند با تجربهٔ بدِ گروهی از کاربران همراه باشد.

میله‌ها را از چپ بخوانید: بیشتر درخواست‌ها (most users) سمت چپ و سریع‌اند، بعد میله‌ها کوتاه و کوتاه‌تر می‌شوند تا دمِ باریکِ سمت راست (tail: rare but real). چهار خط‌چین عمودی را به ترتیب ببینید: p50 median وسطِ انبوه، mean کمی جلوتر چون مقادیر پرت آن را به راست می‌کشند، p95 و p99 tail در دلِ دم. جملهٔ پایین شکل حکم را می‌گوید: ۵ درخواست از هر ۱۰۰ تا از p95 کندترند — میانگین آن‌ها را پنهان می‌کند.

Read the bars left to right: most requests (most users) are on the left and fast, then bars shrink into the thin right-hand tail (tail: rare but real). Take the four dashed lines in order: p50 median amid the bulk, mean slightly ahead since outliers drag it right, p95 and p99 tail inside the tail. The bottom line states the verdict: 5 requests in 100 are slower than p95 — the average hides them.

نکات فنیِ اندازه‌گیریMeasurement details
  • در backendهایی که چند فراخوانی برای یک درخواست کاربر دارند، درصدهای بالا حیاتی‌اند (به نمودار ۲ نگاه کن).
  • محاسبهٔ percentile: پنجرهٔ متحرک (مثلاً ۱۰ دقیقه) + کتابخانه‌های تخمین مثل HdrHistogram، t-digest، OpenHistogram، DDSketch.
  • هشدار: میانگین گرفتن از percentileها (برای ترکیب دادهٔ چند ماشین یا کاهش دقت زمانی) ریاضی بی‌معنی است؛ راه درست، جمع‌کردن هیستوگرام‌هاست.
  • In backends where one user request fans out to several calls, high percentiles are vital (see Diagram 2).
  • Computing percentiles: a sliding window (e.g. 10 minutes) + estimation libraries like HdrHistogram, t-digest, OpenHistogram, DDSketch.
  • Warning: averaging percentiles (to combine machines or downsample time) is mathematical nonsense; the right way is adding histograms.

SLO، SLA و نمودار ۲SLO, SLA and Diagram 2

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

مثال فنی: بودجهٔ خطا

وضعیت اولیه: SLO فرضی، موفقیت 99.9% از یک میلیون درخواست در ماه است.

اجرای مرحله‌به‌مرحله: حداکثر ۱۰۰۰ درخواستِ ناموفق در بودجه می‌گنجد؛ SLA ممکن است برای نقض هدف قراردادی جبران مالی تعیین کند.

نتیجه و دلیل: SLO هدف مهندسی است؛ پیامد قراردادی فقط وقتی SLA چنین تعهدی دارد مطرح می‌شود.

  • SLO (Service Level Objective): هدف درونی سرویس — مثلاً «میانه < 200ms و p99 < 1s و ۹۹.۹٪ درخواست‌های معتبر بدون خطا».
  • SLA (Service Level Agreement): قرارداد بیرونی دربارهٔ پیامدِ نقض SLO (مثلاً استرداد وجه).
  • SLO (Service Level Objective): the service’s internal target — e.g. “median < 200ms, p99 < 1s, 99.9% of valid requests error-free”.
  • SLA (Service Level Agreement): the external contract on the consequences of breaching the SLO (e.g. refunds).
نمودار ۲ · Sequence — کالبد response time و تقویت tail latencyDiagram 2 · Sequence — response-time anatomy and tail-latency amplification باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: تقویت tail latency

وضعیت اولیه: یک صفحه منتظر پاسخ ۱۰۰ سرویس مستقل است؛ احتمال عبور هر سرویس از آستانه ۱٪ فرض شده است.

اجرای مرحله‌به‌مرحله: احتمال اینکه حداقل یکی کند شود 1 - 0.99^100 ≈ 63.4% است.

نتیجه و دلیل: حتی اگر کندیِ هر سرویس نادر باشد، fan-out بزرگ صفحه را زیاد معطل می‌کند؛ فرض استقلال در این محاسبه مهم است.

این هم یک نمودار Sequence است با پنج شرکت‌کننده. از چپ: «End User» با زیرنویسِ «sees response time»، بعد «Frontend API» («one user request»)، و سپس سه سرویسِ backend: «Recommendation» با زیرنویسِ «fast service»، «Timeline Svc» («slow service») و «Ads Svc» («fast service»). دو قابِ بالای جدول مسیر را نام‌گذاری کرده‌اند: «Fan-out to backends» و «Rejoin at the slowest».

از بالا شروع کنید: فلشِ پررنگِ «GET /home» از End User به Frontend API می‌رود و برچسبِ کنارِ آن نکتهٔ کلیدیِ اندازه‌گیری است — «response time is what the client measures»: آنچه response time می‌نامیم، همان چیزی است که کلاینت می‌سنجد، نه آنچه سرور حس می‌کند. حالا در قابِ «Fan-out to backends» سه فلشِ موازی با یک برچسبِ یکسان می‌بینید: «call: network 2ms» از Frontend API به هر سه سرویس — یعنی یک درخواستِ کاربر به چند سرویسِ backend می‌رود (حتی موازی) و هر فراخوانی ۲ میلی‌ثانیه تأخیرِ شبکه دارد.

حالا فلش‌های برگشتی را یکی‌یکی بخوانید؛ هر پاسخ دو بخش دارد: تأخیرِ صف + زمانِ سرویس. Recommendation برمی‌گرداند «queue 2ms + service 25ms» و Ads Svc برمی‌گرداند «queue 5ms + service 20ms» — هر دو سریع. اما Timeline Svc با زیرنویسِ «slow service» برمی‌گرداند «queue 180ms + service 40ms»؛ برچسبِ کنارِ همین فلش می‌گوید «head-of-line blocking in the queue»: چند درخواستِ کند پشتِ خود در صف ساخته‌اند و همین، صف را ۱۸۰ میلی‌ثانیه کرده است. سرویسِ Timeline با این صفِ ۱۸۰ms کلِ درخواست را کند می‌کند.

در قابِ «Rejoin at the slowest» همه‌چیز جمع می‌شود: فلشِ نهایی «200 OK in 230ms» از Frontend API به End User برمی‌گردد — همان عددی که کاربر حس می‌کند. برچسبِ پایانی حکم شکل را می‌نویسد: «the slowest call dominates the whole request»؛ فراخوانی‌های موازیِ سریع بی‌فایده‌اند، چون کلِ درخواست به اندازهٔ کندترین فراخوانی طول می‌کشد.

درسِ شکل همان tail latency amplification است: حتی اگر درصد کمی از فراخوانی‌هایِ backend کند باشند، چون هر درخواستِ کاربر چند فراخوانی دارد، احتمالِ گیر افتادن به یکی از آن‌ها بالا می‌رود و تجربهٔ کاربر کند می‌شود. به همین دلیل است که درصدِ بالای تأخیرها (مثل p99) را باید سمتِ کلاینت و به تفکیکِ هر فراخوانی دید.

Another sequence diagram, with five participants. From the left: “End User” (“sees response time”), then “Frontend API” (“one user request”), then three backend services: “Recommendation” (“fast service”), “Timeline Svc” (“slow service”) and “Ads Svc” (“fast service”). Two frames above name the path: “Fan-out to backends” and “Rejoin at the slowest”.

Start at the top: the bold “GET /home” arrow goes from End User to Frontend API, and its side label is the key measurement point — “response time is what the client measures”: response time is what the client clocks, not what the server feels. In the “Fan-out to backends” frame you see three parallel arrows with one shared label: “call: network 2ms” from Frontend API to all three services — one user request fans out to several backends (even in parallel), each call costing 2ms of network delay.

Now read the return arrows one by one; each reply has two parts: queue delay + service time. Recommendation returns “queue 2ms + service 25ms”, Ads Svc “queue 5ms + service 20ms” — both fast. But Timeline Svc (“slow service”) returns “queue 180ms + service 40ms”; the label on that arrow reads “head-of-line blocking in the queue”: a few slow requests queued up behind themselves, stretching the queue to 180ms. Timeline’s 180ms queue slows the whole request.

In the “Rejoin at the slowest” frame it all adds up: the final “200 OK in 230ms” arrow returns from Frontend API to End User — the number the user feels. The closing label writes the verdict: “the slowest call dominates the whole request”; fast parallel calls don’t help, because the whole request lasts as long as the slowest call.

The lesson is tail latency amplification: even if only a small share of backend calls are slow, since each user request needs several calls, the odds of hitting a slow one grow and the user experience slows. That is why high percentiles (like p99) must be measured at the client, per call.

۳3 Reliability و Fault ToleranceReliability and fault tolerance

ایدهٔ کلیدیKey idea

قابلیت اطمینان یعنی سیستم در شرایط تعریف‌شده، کار درست را ادامه دهد. تحمل خرابی یعنی خراب شدن یک جزء، الزاماً کل خدمت را از کار نیندازد.

مثال فنی: خرابی قطعه بدون قطع سرویس

وضعیت اولیه: سه replica پشت یک مسیریاب هستند و یک replica خاموش می‌شود.

اجرای مرحله‌به‌مرحله: health check مسیر آن را قطع می‌کند و دو replica باقی‌مانده درخواست‌ها را می‌گیرند؛ ظرفیت باقی‌مانده باید بار را تحمل کند.

نتیجه و دلیل: خاموشی یک قطعه fault است؛ اگر هدف سرویس حفظ شود failure در سطح کاربر رخ نداده است.

Reliability means “continuing to work correctly, even when something breaks”: the program does its job, tolerates user mistakes, performs well enough, and grants no unauthorized access.

Fault (part failure): one component stops working (a disk, a machine, a dependency).
Failure (whole failure): the system as a whole stops serving the user (breaches the SLO).

Fault Tolerance، SPOF و محدودیت ذاتیFault tolerance, SPOF and inherent limits
  • سیستمِ تحمل‌خرابی یعنی با وجود مشخصی از خرابی‌ها همچنان سرویس دهد.
  • هر بخشی که تحمل خرابی‌اش نداشته باشی، Single Point of Failure (SPOF) است.
  • تحمل خرابی همیشه محدود است (مثلاً حداکثر ۲ دیسک از چند دیسک، یا ۱ گره از ۳ گره). تحمل «هر تعداد خرابی» بی‌معنی است!
  • در مسیر fan-out، اگر ماشینِ به‌روزرسانی تایم‌لاین بیفتد، باید ماشین دیگری کار را بدون از دست رفتن و بدون تکرارِ پست ادامه دهد ← مفهوم exactly-once semantics (فصل ۱۲).
  • تزریق خرابی (Fault Injection) و Chaos Engineering: برخلاف شهود، عمدی کشتنِ تصادفی فرآیندها باعث می‌شود مکانیزم‌های تحمل خرابی همیشه تمرین شوند؛ بسیاری از باگ‌های بحرانی در «مدیریت خطا» هستند نه مسیر اصلی.
  • A fault-tolerant system keeps serving despite a specified set of faults.
  • Any part whose failure you cannot tolerate is a Single Point of Failure (SPOF).
  • Fault tolerance is always bounded (e.g. at most 2 of N disks, or 1 of 3 nodes). Tolerating “any number of faults” is meaningless!
  • On the fan-out path, if the timeline-updating machine dies, another must continue with no loss and no duplicate posts → exactly-once semantics (Chapter 12).
  • Fault injection and chaos engineering: counterintuitively, deliberately killing random processes keeps fault-tolerance machinery always exercised; many critical bugs live in error handling, not the happy path.

سه منبع خرابی: سخت‌افزار، نرم‌افزار، انسانThree fault sources: hardware, software, humans

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

مثال فنی: سه خرابی با سه درمان

وضعیت اولیه: دیسک یک replica خراب می‌شود، نسخهٔ جدید برنامه همهٔ ورودی‌های خاص را crash می‌کند، و دستور اشتباه جدول را پاک می‌کند.

اجرای مرحله‌به‌مرحله: replica خرابی دیسک را پوشش می‌دهد؛ rollback انتشار باگ را محدود می‌کند؛ restore یا بازیابی زمانی برای حذف اشتباه لازم است.

نتیجه و دلیل: چند کپی زنده جای backup را نمی‌گیرد؛ حذف اشتباه ممکن است به همهٔ replicaها تکثیر شود.

سخت‌افزارHardware

خرابیِ قطعه را باید در طراحی محتمل بدانیم و راهِ ادامهٔ خدمت و بازیابی داشته باشیم.

مثال فنی: از دست رفتن دیسک

وضعیت اولیه: replica A داده را روی دیسک محلی دارد و دیسکش از کار می‌افتد.

اجرای مرحله‌به‌مرحله: B با کپی سالم سرویس را ادامه می‌دهد؛ A از snapshot و لاگِ بعدی بازسازی می‌شود.

نتیجه و دلیل: افزونگی باید در دامنهٔ خرابی مناسب باشد؛ دو فرایند روی همان دیسک از خرابی آن مستقل نیستند.

  • سالانه ۲ تا ۵ درصد هارد دیسک‌ها خراب می‌شوند؛ در خوشهٔ ۱۰,۰۰۰ دیسکی ≈ یک خرابی در روز.
  • SSDها کمی بهتر (۰.۵ تا ۱ درصد) ولی خطای اصلاح‌ناپذیر ~سالی یک بار.
  • تقریباً ۱ از ۱,۰۰۰ ماشین، هستهٔ CPU گاهی نتیجهٔ اشتباه می‌دهد؛ RAM ممکن است بیت جابه‌جا کند.
  • کل دیتاسنتر ممکن است از دست برود (برق، آتش، سیل، حتی طوفان خورشیدی).
  • پاسخ: افزونگی (RAID، منبع تغذیهٔ دوگانه، رپلیکا) + پخش در چند availability zone.
  • 2–5% of hard disks fail per year; in a 10,000-disk cluster ≈ one failure a day.
  • SSDs are a bit better (0.5–1%) but see an uncorrectable error ~once a year.
  • Roughly 1 in 1,000 machines has a CPU core that occasionally returns wrong results; RAM can flip bits.
  • A whole datacenter can be lost (power, fire, flood, even solar storms).
  • Answer: redundancy (RAID, dual power, replicas) + spreading across availability zones.

نرم‌افزار (خطرناک‌تر چون همبسته است)Software (riskier because it is correlated)

باگِ مشترک ممکن است همهٔ نسخه‌ها را در شرایطِ مشابه خراب کند؛ بنابراین چند نسخهٔ سخت‌افزاری همیشه کافی نیست.

مثال فنی: باگ مشترک همهٔ نسخه‌ها

وضعیت اولیه: ورودی size=-1 در نسخهٔ جدید worker باعث crash می‌شود.

اجرای مرحله‌به‌مرحله: توزیع همان نسخه روی ۲۰ replica، باگ را ۲۰ بار تکثیر می‌کند؛ canary و امکان rollback، دامنهٔ اثر انتشار را محدود می‌کنند.

نتیجه و دلیل: replication خرابی مستقل سخت‌افزار را بهتر پوشش می‌دهد؛ خطای مشترک کد می‌تواند همهٔ کپی‌ها را هم‌زمان بزند.

  • همهٔ گره‌ها یک نرم‌افزار دارند ⇒ یک باگ، همه را هم‌زمان می‌اندازد.
  • نمونه‌ها: باگِ ثانیهٔ کبیسه ۲۰۱۲ که اپ‌های Java را هنگ کرد؛ باگ firmware که SSDها بعد از دقیقاً ۳۲,۷۶۸ ساعت می‌میرند.
  • فرآیندِ سرکش که منبع مشترک (CPU، حافظه، نوار شبکه) را می‌خورد؛ سرویس وابسته که کند می‌شود؛ رفتار اضطراری.
  • خرابی آبشاری: مشکلِ یک جزء، جزء بعدی را اضافه‌بار و کند می‌کند و زنجیره ادامه می‌یابد.
  • All nodes run one software ⇒ one bug fells them all at once.
  • Examples: the 2012 leap-second bug that hung Java apps; the firmware bug killing SSDs after exactly 32,768 hours.
  • Rogue processes eating shared resources (CPU, memory, network); slow dependencies; emergent behavior.
  • Cascading failure: one component’s trouble overloads and slows the next, and the chain continues.
انسان‌ها و قابلیت اطمینانHumans and reliability
  • مطالعه‌ها نشان می‌دهد تغییرات پیکربندی توسط اپراتورها علت اصلی قطعی‌هاست؛ خرابی سخت‌افزار فقط ۱۰ تا ۲۵ درصد موارد.
  • برچسب زدن به «خطای انسانی» ضدتولید است؛ آنچه «خطای انسانی» می‌نامیم، نشانه است نه علت — مشکل در سیستمِ اجتماعی-فنی است.
  • راهکارها: تست دقیق (تست دستی + property testing)، مکانیزم rollback برای تغییرات پیکربندی، انتشار تدریجی، مانیتورینگ شفاف، و رابط‌هایی که «کار درست» را آسان کنند.
  • فرهنگِ blameless postmortem: بعد از حادثه، بدون ترس از تنبیه، جزئیات به اشتراک گذاشته شود تا سازمان یاد بگیرد.
  • به جواب‌های ساده‌لوحانه مشکوک باش: نه «بوب باید دقت می‌کرد»، نه «باید backend را با Haskell بنویسیم».
  • Studies show operator config changes are the top cause of outages; hardware faults are only 10–25% of cases.
  • Blaming “human error” is counterproductive; what we call human error is a symptom, not a cause — the problem is in the socio-technical system.
  • Fixes: thorough testing (manual + property testing), rollback for config changes, gradual rollout, transparent monitoring, and interfaces that make the right thing easy.
  • A blameless postmortem culture: after an incident, details are shared without fear of punishment so the organization learns.
  • Distrust naive answers: neither “Bob should have been careful” nor “rewrite the backend in Haskell”.

نمودار ۳ — از SPOF به معماری متحمل خرابیDiagram 3 — from SPOF to a fault-tolerant architecture

نمودار ۳ · Architecture — افزونگی در دو Availability ZoneDiagram 3 · Architecture — redundancy across two availability zones باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: خرابی قطعه بدون قطع سرویس

وضعیت اولیه: سه replica پشت یک مسیریاب هستند و یک replica خاموش می‌شود.

اجرای مرحله‌به‌مرحله: health check مسیر آن را قطع می‌کند و دو replica باقی‌مانده درخواست‌ها را می‌گیرند؛ ظرفیت باقی‌مانده باید بار را تحمل کند.

نتیجه و دلیل: خاموشی یک قطعه fault است؛ اگر هدف سرویس حفظ شود failure در سطح کاربر رخ نداده است.

این نمودار Architecture است و کلِ شکل داخلِ دو قابِ مرزبندی‌شده است: «Availability Zone A» (برچسبِ ریزِ «AZ-a») و «Availability Zone B» («AZ-b»). بیرون از این دو قاب، بالای شکل، جعبهٔ «Users» با زیرنویسِ «web / mobile» نشسته است. داخلِ قابِ A دو جعبه است: «Load Balancer» («health checks») و «Service Node A» («stateless handlers») و کنارش «Primary DB» با زیرنویسِ «writes accepted». داخلِ قابِ B آینهٔ همین‌هاست: «Service Node B» («stateless handlers») و «Replica DB» («failover target»).

مسیرِ عادیِ ترافیک را دنبال کنید: فلشِ «HTTPS» از Users به Load Balancer می‌رود، و فلشِ پررنگِ «route request» از Load Balancer به Service Node A. زیرنویسِ «health checks» همین‌جا معنا می‌دهد: Load Balancer با health check ترافیک را بین دو گرهٔ stateless پخش می‌کند. بعد فلشِ «read / write» از Service Node A به Primary DB می‌رود — پایگاه دادهِ اصلی که فقط در همین قابِ A نوشتن می‌پذیرد.

حالا مسیرِ خرابی: فلشِ چین‌چینِ «failover» از Load Balancer به Service Node B می‌رود. معنایش این است: خرابیِ یک گره ⇐ failover؛ چون گره‌ها stateless‌اند، Load Balancer می‌تواند ترافیک را سمتِ گرهٔ سالم ببرد. فلشِ «read / write» از Service Node B هم به Replica DB می‌رسد — جعبه‌ای که با زیرنویسِ «failover target» دقیقاً همین نقش را اعلام کرده: رپلیکا هدفِ failover است.

و آخرین فلشِ شکل، چین‌چینِ «async replication»، از Primary DB به Replica DB می‌رود: پایگاه دادهِ اصلی و رپلیکا با async replication به‌روز می‌مانند. کلِ طراحی روی این شرط استوار است که هر گره در یک Availability Zone جدا افتاده — چون خرابی‌ها همبسته‌اند و یک رک یا یک منطقه می‌تواند با هم بیفتد؛ اگر هر دو گره در یک قاب بودند، خرابیِ قاب هر دو را با خود می‌برد و دوباره به SPOF می‌رسیدیم.

درسِ شکل: افزونگی به‌جای نقطهٔ شکستِ واحد، و تحملِ خرابی همیشه محدود است (اینجا: یک گره یا یک منطقه). مزیتِ عملیاتیِ همین معماری rolling upgrade است — یکی‌یکی گره‌ها را ری‌استارت کن بدونِ downtime، چون Load Balancer همیشه یک گرهِ سالم دارد.

This is an architecture diagram, and the whole figure sits inside two bounded frames: “Availability Zone A” (“AZ-a”) and “Availability Zone B” (“AZ-b”). Outside both frames, at the top, sits the “Users” box (“web / mobile”). Inside frame A are two boxes: “Load Balancer” (“health checks”) and “Service Node A” (“stateless handlers”), plus “Primary DB” (“writes accepted”). Frame B mirrors them: “Service Node B” (“stateless handlers”) and “Replica DB” (“failover target”).

Follow the normal traffic path: the “HTTPS” arrow goes from Users to Load Balancer, and the bold “route request” arrow from Load Balancer to Service Node A. The “health checks” subtitle earns its meaning here: the Load Balancer spreads traffic across two stateless nodes via health checks. Then “read / write” runs from Service Node A to Primary DB — the primary that accepts writes only in frame A.

Now the failure path: the dashed “failover” arrow goes from Load Balancer to Service Node B. Meaning: a node failure ⇒ failover; since nodes are stateless, the Load Balancer can steer traffic to the healthy node. The “read / write” arrow from Service Node B reaches Replica DB — the box whose “failover target” subtitle declares exactly that role: the replica is the failover target.

And the last arrow, dashed “async replication”, runs from Primary DB to Replica DB: primary and replica stay current via async replication. The whole design rests on each node sitting in a separate availability zone — because faults are correlated and one rack or region can fall together; if both nodes shared one frame, the frame’s failure would take both, and we would be back at a SPOF.

The lesson: redundancy instead of a single point of failure, and fault tolerance always bounded (here: one node or one zone). The operational bonus of this same architecture is rolling upgrades — restart nodes one by one with no downtime, since the Load Balancer always has a healthy node.

۴4 ScalabilityScalability

ایدهٔ کلیدیKey idea

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

مثال فنی: مقیاس‌پذیری برای کدام بُعد؟

وضعیت اولیه: ۱۰ هزار راننده هر ۵ ثانیه موقعیت می‌فرستند: 2000 update/s.

اجرای مرحله‌به‌مرحله: افزایش به ۱۰۰ هزار راننده نرخ را به 20,000 update/s می‌رساند، حتی اگر تعداد جست‌وجوی سفر ثابت بماند.

نتیجه و دلیل: ظرفیت نوشتن موقعیت، جست‌وجوی مکانی و تاریخچه باید جدا سنجیده شوند؛ «مقیاس‌پذیر» یک عدد واحد نیست.

Scalability is a system’s ability to cope with growing load. It is not a one-dimensional label; saying “X is scalable” is meaningless. The sharper questions are: if the system grows in a specific way, what options do we have? How do we add resources? When do we hit the current architecture’s ceiling?

هشدار به استارتاپ‌هاA note to startups

اگر محصول تازه و کاربران کمی داری، هدف اصلی مهندسی، «ساده و منعطف نگه داشتن» سیستم است تا سریع یاد بگیری. نگرانی بابت مقیاسِ فرضی، یا تلاش هدررفته است (بهینه‌سازی زودهنگام) یا بدتر: تو را در طراحیِ غیرمنعطف قفل می‌کند. وقتی محصول موفق شد، گلوگاه‌ها را می‌بینی و آن‌وقت نوبتِ فکر کردن به مقیاس است.

If the product is new and users are few, engineering’s main goal is keeping the system “simple and flexible” so you learn fast. Worrying about hypothetical scale is either wasted effort (premature optimization) or worse: it locks you into an inflexible design. Once the product succeeds, you will see the bottlenecks — then it is time to think about scale.

فهم بار و دو سؤال کلیدیUnderstanding load, and two key questions

دو سؤال اصلی این است: با منابع فعلی، افزایش بار چه اثری بر کیفیت دارد؟ و برای ثابت نگه‌داشتن کیفیت، چه مقدار منابع باید اضافه کنیم؟

مثال فنی: دو سؤال آزمایش ظرفیت

وضعیت اولیه: در 500 req/s، p99=100 ms است؛ در 1000 req/s به 900 ms می‌رسد.

اجرای مرحله‌به‌مرحله: اول روی سخت‌افزار ثابت علت صف را پیدا می‌کنیم؛ سپس می‌سنجیم برای حفظ 100 ms چه منابع یا تغییر طراحی لازم است.

نتیجه و دلیل: دو برابر کردن بار الزاماً فقط دو برابر کردن CPU نمی‌خواهد؛ گلوگاه ممکن است قفل یا دیسک باشد.

  • بار را توصیف کن: درخواست/ثانیه، گیگابایت/روز، اوجِ کاربران همزمان، نسبت خواندن/نوشتن، نرخ hit کش، تعداد آیتم به‌ازای هر کاربر. گاهی میانگین مهم است و گاهی چند حالتِ حدی (مثل سلبریتی) کل سیستم را اداره می‌کند.
  • سؤال اول: با منابع ثابت، اگر بار دو برابر شود performance چه می‌شود؟
  • سؤال دوم: برای ثابت نگه داشتن performance، چقدر باید منابع اضافه کرد؟
  • مقیاس‌پذیری خطی: دو برابر منابع ⇐ دو برابر بار با performance ثابت (مطلوب، ولی نادر؛ معمولاً هزینه تندتر از خطی رشد می‌کند).
  • Describe the load: requests/second, GB/day, concurrent-user peaks, read/write ratio, cache hit rate, items per user. Sometimes the average matters; sometimes a few edge cases (like celebrities) run the whole system.
  • First question: with fixed resources, what happens to performance if load doubles?
  • Second question: how many resources must we add to hold performance steady?
  • Linear scalability: double resources ⇒ double load at constant performance (ideal, but rare; cost usually grows superlinearly).

مدل‌های مقیاس‌پذیریScalability models

معماریایدهنقطهٔ ضعف اصلی
Shared-Memory (مقیاس عمودی / scale up) ماشین قدرتمندتر با هسته/RAM/دیسک بیشتر؛ موازی‌سازی با thread و فرآیند هزینه تندتر از خطی رشد می‌کند؛ گلوگاه‌ها مانعِ دو برابر شدن واقعی می‌شوند
Shared-Disk (NAS / SAN) چند ماشین با CPU/RAM مستقل، روی یک آرایهٔ دیسک مشترک رقابت بر سر منابع و هزینهٔ قفل‌گذاری، مقیاس را محدود می‌کند (سنتی در انباردادهٔ on-prem)
Shared-Nothing (مقیاس افقی / scale out) گره‌های مستقل با CPU/RAM/دیسک خودشان؛ هماهنگی در سطح نرم‌افزار و شبکه نیازمند sharding صریح و همهٔ پیچیدگی‌های سیستم توزیع‌شده
ArchitectureIdeaMain weakness
Shared-Memory (scale up) A beefier machine with more cores/RAM/disk; parallelism via threads and processes Cost grows superlinearly; bottlenecks block true doubling
Shared-Disk (NAS / SAN) Several machines with independent CPU/RAM on one shared disk array Resource contention and locking costs cap scale (traditional in on-prem warehouses)
Shared-Nothing (scale out) Independent nodes with their own CPU/RAM/disk; coordination in software over the network Needs explicit sharding plus all distributed-system complexity

در مقیاس عمودی، ماشین قوی‌تری می‌گیریم؛ در مقیاس افقی، ماشین‌های بیشتری اضافه می‌کنیم. اینکه حافظه و دیسک مشترک باشند یا جدا، هزینهٔ هماهنگی را تغییر می‌دهد.

مثال فنی: عمودی و افقی

وضعیت اولیه: job روی ۸ هسته ۱۰ دقیقه زمان می‌برد و حافظه‌اش پر است.

اجرای مرحله‌به‌مرحله: افزودن RAM و هسته به همان ماشین scale-up است؛ تقسیم ورودی بین چند ماشین scale-out است و انتقال و ادغام خروجی می‌خواهد.

نتیجه و دلیل: تقسیم‌پذیری کار و هزینهٔ هماهنگی تعیین می‌کند روش افقی چقدر سود بدهد.

Cloud-native databases that separate storage and compute resemble shared-disk, but instead of a filesystem or block device they expose a purpose-built database API — without the old scalability problems.

اصول مقیاس‌پذیری + نمودار ۴Scalability principles + Diagram 4

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

مثال فنی: گلوگاهِ اشتباه

وضعیت اولیه: ۱۰ سرور API فقط ۳۰٪ CPU مصرف می‌کنند، اما اتصال‌های پایگاه داده منتظر یک قفل‌اند.

اجرای مرحله‌به‌مرحله: افزودن ۱۰ سرور API درخواست‌های منتظر قفل را بیشتر می‌کند؛ کوتاه کردن تراکنشِ نگهدارندهٔ قفل مسیر مؤثرتری است.

نتیجه و دلیل: برای ظرفیت بیشتر باید منبع محدودکننده را اصلاح کرد؛ افزایش هر منبع دلخواه کافی نیست.

  • «سسِ جادویی مقیاس» وجود ندارد: سیستمِ ۱۰۰ هزار درخواست/ثانیه × ۱ کیلوبایت، شبیه سیستمِ ۳ درخواست/دقیقه × ۲ گیگابایت نیست — با اینکه throughput یکسانی دارند!
  • معماری مناسبِ یک سطح بار، برای ۱۰ برابر آن کار نمی‌کند: با هر مرتبهٔ بزرگی (order of magnitude) رشد، معماری را بازاندیشی کن؛ برنامه‌ریزی بیش از یک مرتبه جلوتر، نمی‌ارزد.
  • تجزیه به اجزای مستقل: اصلِ پشتِ میکروسرویس، sharding، پردازش جریانی و shared-nothing. هنر در «کجا خط بکشی» است.
  • بیش از نیاز پیچیده نکن: اگر پایگاه داده تک‌ماشینی کار را می‌کند، همان بهتر. ۵ سرویس از ۵۰ سرویس ساده‌تر است؛ autoscaling برای بارِ قابل پیش‌بینی، شگفتی عملیاتی کمتری دارد.
  • There is no magic scaling sauce: 100K requests/s × 1KB is nothing like 3 requests/min × 2GB — despite equal throughput!
  • An architecture fit for one load level fails at 10x: rethink the architecture at every order of magnitude of growth; planning more than one order ahead rarely pays.
  • Decompose into independent parts: the principle behind microservices, sharding, stream processing and shared-nothing. The art is where to draw the line.
  • Don’t over-complicate: if a single-machine database does the job, keep it. 5 services beat 50 for simplicity; autoscaling for predictable load holds fewer operational surprises.
نمودار ۴ · Workflow — از توصیف بار تا تصمیم مقیاسDiagram 4 · Workflow — from describing load to the scaling decision باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: گلوگاهِ اشتباه

وضعیت اولیه: ۱۰ سرور API فقط ۳۰٪ CPU مصرف می‌کنند، اما اتصال‌های پایگاه داده منتظر یک قفل‌اند.

اجرای مرحله‌به‌مرحله: افزودن ۱۰ سرور API درخواست‌های منتظر قفل را بیشتر می‌کند؛ کوتاه کردن تراکنشِ نگهدارندهٔ قفل مسیر مؤثرتری است.

نتیجه و دلیل: برای ظرفیت بیشتر باید منبع محدودکننده را اصلاح کرد؛ افزایش هر منبع دلخواه کافی نیست.

این نمودار یک Workflow است: سه قابِ مرزبندی‌شده به نام‌های «01 / Understand Load»، «02 / Scaling Decision» و «EX / Revisit»، و بالای جریان سه عنوانِ مرحله: «Describe the load»، «Ask and decide» و «Scale and review». جریان از قابِ اول شروع می‌شود، در قابِ دوم به تصمیم می‌رسد، و با قابِ سوم حلقه می‌بندد.

در قابِ «01 / Understand Load» دو جعبه پشتِسرِ هم‌اند: «Load Metrics» با زیرنویسِ «QPS, GB/day, peaks» و بعد فلشِ پررنگِ «collect» به «Access Profile» («read/write, cache hits») که برچسبِ کوچکِ «avg vs extremes» کنارش نشسته است — یعنی بار را توصیف کن: حجم (کوئری/ثانیه، گیگابایت/روز، اوج‌ها) و الگوی دسترسی (نسبتِ خواندن/نوشتن، نرخِ hit کش)، هم میانگین و هم حالت‌های حدی.

از «Access Profile» فلشِ «feed» پایین می‌رود و به زیرگروهِ «Two questions before scaling» در قابِ «02 / Scaling Decision» می‌رسد. آن‌جا دو جعبهٔ پرسشی هستند: اول «Load Doubles?» با زیرنویسِ «fixed resources: then what» (سؤالِ اول: با منابعِ ثابت، دو برابر شدنِ بار چه می‌کند؟)، بعد فلشِ «answer both» به «Cost to Hold SLA?» («resources for same speed»؛ سؤالِ دوم: برای سرعتِ ثابت چقدر منبع لازم است؟). پاسخِ هر دو از فلشِ پررنگِ «decide» می‌گذرد و به جعبهٔ «Scaling Model» می‌رسد: زیرنویسِ «up, shared-disk, out» سه مدلِ مقیاس‌پذیری را نام می‌برد و برچسبِ «minimal complexity» کنارش قاعدهٔ بازی است — کمینهٔ پیچیدگی.

حالا فلشِ «grow» از «Scaling Model» پایین می‌رود و به قابِ «EX / Revisit» می‌رسد: جعبهٔ «Revisit per 10x» با زیرنویسِ «rethink architecture». اما شکل این‌جا تمام نمی‌شود؛ فلشِ چین‌چینِ «new order of magnitude» از «Revisit per 10x» به عقب برمی‌گردد و دوباره به «Load Doubles?» وصل می‌شود. این یک حلقه است: با هر مرتبهٔ بزرگیِ تازه (order of magnitude)، معماری را از نو بازاندیشی کن و به سؤال‌های تصمیم برگرد.

درسِ شکل دقیقاً همان چیزی است که در فهرستِ بالا خواندید: «سسِ جادوییِ مقیاس» وجود ندارد، معماریِ مناسبِ یک سطح بار برای ۱۰ برابرِ آن کار نمی‌کند، و بیش از نیاز پیچیده نکن. ترتیبِ فلش‌ها هم گویاست: اول بار را توصیف کن («collect»)، بعد هر دو سؤال را جواب بده («answer both»)، بعد تصمیم بگیر («decide»)، و هر بار که بار یک مرتبه بزرگ شد، همین مسیر را دوباره برو («new order of magnitude»).

This is a workflow diagram: three bounded frames named “01 / Understand Load”, “02 / Scaling Decision” and “EX / Revisit”, with three stage titles above the flow: “Describe the load”, “Ask and decide” and “Scale and review”. The flow starts in the first frame, reaches a decision in the second, and loops with the third.

In frame “01 / Understand Load” two boxes sit in sequence: “Load Metrics” (“QPS, GB/day, peaks”), then the bold “collect” arrow to “Access Profile” (“read/write, cache hits”) with the small “avg vs extremes” tag beside it — describe the load: volume (queries/second, GB/day, peaks) and access pattern (read/write ratio, cache hit rate), both average and edge cases.

From “Access Profile” the “feed” arrow drops into the “Two questions before scaling” group in frame “02 / Scaling Decision”. Two question boxes live there: first “Load Doubles?” (“fixed resources: then what” — question one: with fixed resources, what does doubled load do?), then “answer both” to “Cost to Hold SLA?” (“resources for same speed”; question two: how many resources keep speed constant?). Both answers pass the bold “decide” arrow into “Scaling Model”: its “up, shared-disk, out” subtitle names the three scalability models, and the “minimal complexity” tag beside it is the rule of the game.

Now the “grow” arrow from “Scaling Model” drops into frame “EX / Revisit”: the “Revisit per 10x” box (“rethink architecture”). But the figure doesn’t end there; the dashed “new order of magnitude” arrow loops back from “Revisit per 10x” into “Load Doubles?”. It is a loop: with every new order of magnitude, rethink the architecture and return to the decision questions.

The lesson is exactly the list above: no magic scaling sauce, an architecture fit for one load level fails at 10x, and never over-complicate. The arrow order tells it too: first describe the load (“collect”), answer both questions (“answer both”), then decide (“decide”), and every time load grows an order of magnitude, walk the path again (“new order of magnitude”).

۵5 MaintainabilityMaintainability

ایدهٔ کلیدیKey idea

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

مثال فنی: تغییرِ قابل‌فهم و قابل‌بازگشت

وضعیت اولیه: قرار است phone به دو فیلد country_code و number تبدیل شود.

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

نتیجه و دلیل: تغییر مرحله‌ای زمان تشخیص و بازگشت می‌دهد؛ حذف فوری فیلد قدیمی انتشار نسخهٔ قبلی را ناممکن می‌کند.

Software doesn’t wear out like a mechanical part, but requirements change, environments shift, and bugs exist. Most of software’s cost is in maintenance, not initial development. Three principles: operability, simplicity, evolvability.

Operability — زندگی را برای عملیات آسان کنOperability — make life easy for operations

عملیات‌پذیری یعنی تیم بتواند وضعیتِ سرویس را ببیند و مشکلاتش را با ابزارِ روشن مدیریت کند.

مثال فنی: پیدا کردن علت تأخیر

وضعیت اولیه: درخواست r42 از API به پایگاه داده می‌رود؛ کل مسیر 1200 ms است.

اجرای مرحله‌به‌مرحله: trace نشان می‌دهد 1100 ms صرف انتظار اتصال شده، نه اجرای SQL؛ نمودار تعداد اتصال‌های فعال این یافته را تأیید می‌کند.

نتیجه و دلیل: شناسهٔ مشترک و سنجهٔ صف، محل اصلاح را معلوم می‌کنند؛ پیام کلیِ «سیستم کند است» چنین کمکی نمی‌کند.

  • مانیتورینگِ معیارهای کلیدی + ابزار observability برای رفتار زمان اجرا.
  • بی‌نیازی از ماشین خاص (بتوان گرهی را برای تعمیرات پایین کشید).
  • مستندات و مدل عملیاتیِ روشن: «اگر X کنم، Y می‌شود».
  • پیش‌فرض‌های خوب + حقِ override؛ خودترمیمی + کنترل دستی.
  • رفتار قابل پیش‌بینی، کمینه‌کردن غافلگیری.
  • اتوماسیون شمشیر دو لبه است: مواردِ خاص همیشه به انسانِ ماهر نیاز دارند.
  • Monitoring key metrics + observability tooling for runtime behavior.
  • No dependence on a specific machine (any node can go down for maintenance).
  • Clear docs and operational model: “if I do X, Y happens”.
  • Good defaults + override rights; self-healing + manual control.
  • Predictable behavior, minimizing surprises.
  • Automation is a double-edged sword: edge cases always need a skilled human.

Simplicity — پیچیدگی را مهار کنSimplicity — tame complexity

سادگی یعنی قواعدِ مهمِ سیستم آشکار باشند و برای انجام یک تغییر، وابستگی‌های بی‌دلیل زیاد نشوند.

مثال فنی: یک قانون به‌جای چند نسخه

وضعیت اولیه: شرط تخفیف «خرید بالای ۵۰۰» در وب و worker به دو شکل کپی شده است.

اجرای مرحله‌به‌مرحله: شرط در یک ماژول دامنه قرار می‌گیرد و هر دو مسیر از همان منطق و ورودی استفاده می‌کنند.

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

  • پروژهٔ بزرگِ درهم‌وبرهم را «big ball of mud» می‌نامند؛ پیچیدگی، ریسک باگِ هنگام تغییر را بالا می‌برد.
  • تفکیکِ پیچیدگیِ ذاتی (از جنس مسئله) در برابرِ اکتسابی (از جنس ابزار) — مفید ولی نه کامل.
  • بهترین ابزار مبارزه با پیچیدگی: انتزاع (Abstraction) — مثل SQL که ساختارهای دیسک، درخواست‌های همزمان و ناسازگاری بعد از crash را پنهان می‌کند.
  • A big tangled project is called a “big ball of mud”; complexity raises the risk of bugs on change.
  • Essential vs accidental complexity is a useful but imperfect split.
  • The best weapon against complexity: abstraction — like SQL hiding disk layouts, concurrent requests and post-crash inconsistency.
Evolvability — تغییر را آسان کنEvolvability — make change easy
  • نیازمندی‌ها همیشه در تغییرند: کشف واقعیت‌های تازه، اولویت‌های جدید، قوانین تازه، رشد سیستم.
  • سیستم‌های ساده و کم‌وابسته (loosely coupled) راحت‌تر تغییر می‌کنند؛ چابکی در سطح سیستم داده را evolvability می‌نامیم.
  • بازگشت‌ناپذیری (Irreversibility) دشمنِ تغییر است: اگر در مهاجرت از پایگاه داده A به B نتوانی برگردی، ریسک بسیار بالاتر است. کمینه‌کردن بازگشت‌ناپذیری ⇐ انعطاف بیشتر.
  • Requirements always change: newly discovered facts, new priorities, new laws, system growth.
  • Simple, loosely coupled systems change more easily; agility at the data-system level is evolvability.
  • Irreversibility is change’s enemy: migrating from database A to B with no way back is far riskier. Minimizing irreversibility ⇒ more flexibility.
نقشهٔ فصل — همهٔ مفاهیم کلیدی در یک نگاهChapter map — all key concepts at a glance باز کردن در صفحهٔ جدا ↗Open standalone ↗
روایت مدرس از نمودارTeacher narration

مثال فنی برای خواندنِ نمودار: تغییرِ قابل‌فهم و قابل‌بازگشت

وضعیت اولیه: قرار است phone به دو فیلد country_code و number تبدیل شود.

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

نتیجه و دلیل: تغییر مرحله‌ای زمان تشخیص و بازگشت می‌دهد؛ حذف فوری فیلد قدیمی انتشار نسخهٔ قبلی را ناممکن می‌کند.

گرهٔ مرکزی (Nonfunctional Requirements) را بخوانید و شش یال را یکی‌یکی دنبال کنید: story به قصهٔ تایم‌لاین (۵۰۰ میلیون پست در روز)، measure به Performance با p99 و SLO، survive به Reliability بدون SPOF، grow به Scalability با بازاندیشیِ هر ۱۰ برابر، simplify به Maintainability برای تکاملِ امن، و recall به بستهٔ ۲۰ سؤالی مصاحبه. هر یال یک فعل است: قصه بگو، اندازه بگیر، زنده بمان، رشد کن، ساده کن، مرور کن.

Read the central node (Nonfunctional Requirements) and follow the six edges one by one: story to the timeline tale (500M posts/day), measure to performance with p99 and SLO, survive to reliability with no SPOF, grow to scalability with a rethink per 10x, simplify to maintainability for safe evolution, and recall to the 20-question interview pack. Each edge is a verb: tell the story, measure, survive, grow, simplify, recall.

۶6 بستهٔ مصاحبهInterview pack

۶.۱ همه‌چیز در یک نگاه6.1 Everything at a glance

اصطلاحتعریف یک‌خطیکلیدواژهٔ مصاحبه
Response Timeزمانِ دیده‌شده توسط کلاینتشامل صف، سرویس و شبکه
Throughputدرخواست یا داده در ثانیهسقفِ سخت‌افزار
Latencyزمانی که درخواست در حال پردازش نیستnetwork delay، queueing
p50 / p95 / p99درصدی از درخواست‌ها که سریع‌تر از این مقدارندtail latency
Tail Latency Amplificationچند فراخوانی backend، احتمال کندی کل درخواست را بالا می‌بردکندترین فراخوانی تعیین‌کننده است
SLO / SLAهدف درونی / قرارداد بیرونیِ سطح سرویسدرصدها + دسترس‌پذیری
Materialized Viewنتیجهٔ کوئری از پیش محاسبه‌شدهخواندن سریع، نوشتن گران
Fan-outضریب افزایش درخواست‌های پایین‌دستی×200 در کیس تایم‌لاین
Metastable Failureماندگاریِ اضافه‌بار حتی بعد از رفع بارretry storm
Fault vs Failureخرابی جزء در برابر خرابی کلِ سرویسدو سطح از یک رویداد
SPOFبخشی که تحمل خرابی‌اش نداریsingle point of failure
Fault Injectionخرابی عمدی برای تمرین تحمل خرابیchaos engineering
Blameless Postmortemتحلیل حادثه بدون تنبیه انسانعلت سیستمی، نه خطای انسانی
Scalabilityکنار آمدن با افزایش بارنه یک‌بعدی
Shared-Nothingگره‌های مستقل + هماهنگی نرم‌افزاریscale out + sharding
Operability / Simplicity / Evolvabilityسه اصل نگهداریهزینهٔ اصلی در نگهداری است
TermOne-line definitionInterview keyword
Response TimeTime seen by the clientQueue + service + network
ThroughputRequests or data per secondHardware ceiling
LatencyTime a request spends not being processedNetwork delay, queueing
p50 / p95 / p99Share of requests faster than this valueTail latency
Tail Latency AmplificationSeveral backend calls raise the odds the whole request is slowSlowest call decides
SLO / SLAInternal target / external service contractPercentiles + availability
Materialized ViewPrecomputed query resultFast reads, costly writes
Fan-outDownstream-request multiplication factor×200 in the timeline case
Metastable FailureOverload persisting after load is goneRetry storm
Fault vs FailurePart failure vs whole-service failureTwo levels, one event
SPOFA part whose failure you cannot surviveSingle point of failure
Fault InjectionDeliberate faults to exercise toleranceChaos engineering
Blameless PostmortemIncident review without punishing humansSystemic cause, not human error
ScalabilityCoping with growing loadNot one-dimensional
Shared-NothingIndependent nodes + software coordinationScale out + sharding
Operability / Simplicity / EvolvabilityThe three maintenance principlesCost centers on maintenance

۶.۲ سوالات پرتکرار مصاحبه (برای دیدن جواب کلیک کن)6.2 Frequent interview questions (click for answers)

۱. نیازمندی غیرعملکردی یعنی چه؟ سه مثال بزن.1. What is a nonfunctional requirement? Give three examples.

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

مثال فنی: نیاز قابل‌آزمون برای جست‌وجو

وضعیت اولیه: هدف فرضی API این است: با 1000 req/s، حداقل ۹۹٪ درخواست‌های موفق زیر 300 ms تمام شوند.

اجرای مرحله‌به‌مرحله: آزمون بار، نرخ ورودی، خطا و توزیع زمان پاسخ را با هم ثبت می‌کند؛ صرفِ پاسخ سریع به ۱۰ درخواست معیار نیست.

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

It says “how” the system must work, not “what” it does: fast (performance), reliable (reliability), scalable (scalability), maintainable (maintainability), secure and compliant.

۲. چرا polling برای تایم‌لاین شکست می‌خورد؟ اعدادش را بگو.2. Why does polling fail for the timeline? Give the numbers.

Polling بار را با تعدادِ کاربران و دفعاتِ تازه‌سازی زیاد می‌کند، حتی وقتی داده تغییر نکرده است. در سناریوی فصل، ده میلیون کاربر با فاصلهٔ پنج ثانیه، دو میلیون پرس‌وجو در ثانیه می‌سازند.

مثال فنی: محاسبهٔ بار polling

وضعیت اولیه: یک میلیون کاربر فعال هر 5 s صفحه را تازه می‌کنند.

اجرای مرحله‌به‌مرحله: نرخ درخواست 1,000,000 / 5 = 200,000 req/s است؛ با دنبال کردن ۲۰۰ نفر، هر درخواست ممکن است دادهٔ چندین نویسنده را ترکیب کند.

نتیجه و دلیل: تعداد کاربران به‌تنهایی ظرفیت لازم را نمی‌گوید؛ فاصلهٔ polling و هزینهٔ هر درخواست هم تعیین‌کننده‌اند.

10M online users polling every 5s → 2M queries/second; each query merges 200 followees → 400M lookups/second.

۳. fan-out و fan-out factor را تعریف کن.3. Define fan-out and the fan-out factor.

Fan-out یعنی یک عمل چند کارِ پایین‌دستی ایجاد کند. ضریبِ fan-out تعدادِ این کارها نسبت به عملِ اولیه است و برای تخمینِ بار اهمیت دارد.

مثال فنی: Fan-out در لحظهٔ انتشار

وضعیت اولیه: پست p9 نویسنده‌ای با ۱۰۰۰ دنبال‌کننده دارد.

اجرای مرحله‌به‌مرحله: worker شناسهٔ p9 را به ۱۰۰۰ timeline اضافه می‌کند؛ خواننده بعداً فقط فهرست آماده را می‌خواند.

نتیجه و دلیل: یک نوشتنِ اولیه ۱۰۰۰ نوشتنِ مشتق می‌سازد. برای نویسندهٔ بسیار پرطرفدار می‌توان ادغام هنگام خواندن را جدا بررسی کرد.

When one incoming request spawns several downstream requests, the multiplication factor is the fan-out factor. In the timeline case: each post × 200 followers ≈ 1.16M timeline writes/second.

۴. Materialized View چه چیزی را قربانی می‌کند؟4. What does a materialized view sacrifice?

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

مثال فنی: Fan-out در لحظهٔ انتشار

وضعیت اولیه: پست p9 نویسنده‌ای با ۱۰۰۰ دنبال‌کننده دارد.

اجرای مرحله‌به‌مرحله: worker شناسهٔ p9 را به ۱۰۰۰ timeline اضافه می‌کند؛ خواننده بعداً فقط فهرست آماده را می‌خواند.

نتیجه و دلیل: یک نوشتنِ اولیه ۱۰۰۰ نوشتنِ مشتق می‌سازد. برای نویسندهٔ بسیار پرطرفدار می‌توان ادغام هنگام خواندن را جدا بررسی کرد.

It makes reads fast (a precomputed cache) and writes expensive. Every change must reach all derived views; at peak load, deliveries can be queued.

۵. مسئلهٔ سلبریتی‌ها چطور حل می‌شود؟5. How is the celebrity problem solved?

می‌توان از روشِ ترکیبی استفاده کرد: پستِ کاربرانِ معمولی را در فهرست‌ها پخش کنیم و پستِ کاربرانِ بسیار پرطرفدار را هنگامِ خواندن اضافه کنیم.

مثال فنی: خواندنِ پست سلبریتی

وضعیت اولیه: کاربر عادی ۵۰۰ دنبال‌کننده دارد، اما یک نویسنده ۲۰ میلیون دنبال‌کننده دارد.

اجرای مرحله‌به‌مرحله: برای عادی‌ها fan-out هنگام نوشتن اجرا می‌شود؛ پست نویسندهٔ بزرگ هنگام خواندن با فهرست آماده ادغام می‌شود.

نتیجه و دلیل: از ۲۰ میلیون نوشتن برای یک پست جلوگیری می‌شود؛ در عوض خواندن به ادغام دو مسیر نیاز دارد.

We store celebrity posts separately and merge them with the materialized timeline at read time, avoiding millions of writes. For heavy-follow users, dropping some writes (sampling) is acceptable.

۶. response time، service time و latency چه فرقی دارند؟6. How do response time, service time and latency differ?

زمانِ پاسخ کلِ مدتِ دیده‌شده از سوی کلاینت است؛ زمانِ خدمت مدتِ پردازشِ فعال است و انتظارِ صف جداست. واژهٔ latency در منابع کاربردهای متفاوت دارد، پس تعریفش را روشن کنیم.

مثال فنی: زمان پاسخ و ظرفیت

وضعیت اولیه: یک worker هر درخواست را در 20 ms پردازش می‌کند، اما درخواست 180 ms در صف می‌ماند.

اجرای مرحله‌به‌مرحله: زمان پاسخِ سمت سرور 200 ms است؛ با چند worker ممکن است مجموعاً 1000 req/s تکمیل شود.

نتیجه و دلیل: service time همان زمان اجرای کار است؛ response time انتظار را هم دارد. throughput تعداد کارِ تمام‌شده در واحد زمان است.

Response time = whatever the client sees; service time = active processing duration; queueing delay = waiting before processing; latency = any time the request is not being processed (e.g. network travel).

۷. چرا به‌جای میانگین از percentile استفاده می‌کنیم؟7. Why use percentiles instead of the mean?

میانگین یک عددِ خلاصه است و توزیع را پنهان می‌کند. صدک‌ها نشان می‌دهند چه سهمی از درخواست‌ها زیرِ یک زمان‌اند؛ میانه و صدک‌های انتهایی مکمل‌اند.

مثال فنی: میانگین، کندترین‌ها را پنهان می‌کند

وضعیت اولیه: از ۱۰۰۰ درخواست، ۹۹۰ مورد 10 ms و ۱۰ مورد 1000 ms طول می‌کشند.

اجرای مرحله‌به‌مرحله: میانگین 19.9 ms است؛ با روش nearest-rank، p99=10 ms و p99.9=1000 ms می‌شود.

نتیجه و دلیل: هدفِ صدک باید مشخص باشد؛ میانگین خوب می‌تواند با تجربهٔ بدِ گروهی از کاربران همراه باشد.

The mean neither shows the “typical” user experience nor resists outliers. The median (p50) shows the typical experience; p95/p99/p999 show the worst (tail latency).

۸. مثالِ کتاب دربارهٔ p99.9 و p99.99 چه بده‌بستانی را نشان می‌دهد؟8. Why is Amazon strict on p99.9 but not p99.99?

مثالِ کتاب نشان می‌دهد بهبودِ تأخیرِ کاربرانِ کند می‌تواند ارزشِ زیادی داشته باشد، اما رسیدن به صدک‌های بسیار دور ممکن است پرهزینه باشد. این مثال را سیاستِ ثابتِ امروزِ یک شرکت فرض نکنیم.

مثال فنی: بهینه‌سازیِ دو بخش انتهایی توزیع

وضعیت اولیه: فرض کنید p99.9=1 s و p99.99=8 s است، اما هدف محصول روی p99.9 تعریف شده است.

اجرای مرحله‌به‌مرحله: کاهش موارد ۸ثانیه‌ای به ۴ ثانیه p99.99 را بهتر می‌کند؛ ممکن است p99.9 و بخش بزرگی از تجربهٔ کاربران تغییر نکند.

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

The slowest users are usually the most valuable customers (long purchase history); but optimizing p99.99 (one request in 10,000) is very costly and low-yield, driven by random events outside your control.

۹. tail latency amplification چیست؟9. What is tail latency amplification?

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

مثال فنی: تقویت tail latency

وضعیت اولیه: یک صفحه منتظر پاسخ ۱۰۰ سرویس مستقل است؛ احتمال عبور هر سرویس از آستانه ۱٪ فرض شده است.

اجرای مرحله‌به‌مرحله: احتمال اینکه حداقل یکی کند شود 1 - 0.99^100 ≈ 63.4% است.

نتیجه و دلیل: حتی اگر کندیِ هر سرویس نادر باشد، fan-out بزرگ صفحه را زیاد معطل می‌کند؛ فرض استقلال در این محاسبه مهم است.

When one user request needs several backend services, a single slow call suffices to slow the whole request; the more calls, the likelier you hit a slow one.

۱۰. SLO و SLA چه تفاوتی دارند؟10. How do SLO and SLA differ?

SLO هدفِ کیفیتِ سرویس است؛ SLA تعهدِ قراردادی است و ممکن است پیامدِ نقض داشته باشد. معیار، بازهٔ سنجش و شرایطِ هرکدام باید مشخص باشند.

مثال فنی: بودجهٔ خطا

وضعیت اولیه: SLO فرضی، موفقیت 99.9% از یک میلیون درخواست در ماه است.

اجرای مرحله‌به‌مرحله: حداکثر ۱۰۰۰ درخواستِ ناموفق در بودجه می‌گنجد؛ SLA ممکن است برای نقض هدف قراردادی جبران مالی تعیین کند.

نتیجه و دلیل: SLO هدف مهندسی است؛ پیامد قراردادی فقط وقتی SLA چنین تعهدی دارد مطرح می‌شود.

An SLO is the internal service-level target (e.g. p99 under 1s, 99.9% error-free requests); an SLA is the contract stating the consequences of missing that target (e.g. refunds).

۱۱. چرا میانگین گرفتن از percentileها اشتباه است؟11. Why is averaging percentiles wrong?

میانگینِ صدک‌های چند مجموعه، صدکِ مجموعهٔ ترکیبی نیست. برای ترکیب باید داده یا خلاصهٔ آماریِ قابل‌ادغام، مانند هیستوگرامِ مناسب، همراه با تعدادِ نمونه‌ها داشته باشیم.

مثال فنی: صدکِ دو گروه جمع‌پذیر نیست

وضعیت اولیه: گروه A هزار درخواست سریع و گروه B ده درخواست کند دارد؛ هر گروه p99 خودش را گزارش می‌کند.

اجرای مرحله‌به‌مرحله: میانگینِ دو p99 به هر گروه وزن برابر می‌دهد؛ برای p99 کل باید نمونه‌ها یا histogram قابل‌ادغام را تجمیع کرد.

نتیجه و دلیل: صدک روی رتبهٔ درخواست‌های کل تعریف می‌شود، نه میانگینِ رتبه‌های دو گروه با اندازهٔ متفاوت.

It is mathematical nonsense: the median of medians is not the overall median. The right way to combine machines or time ranges is adding histograms (libraries like HdrHistogram, t-digest, DDSketch).

۱۲. fault و failure را از هم جدا کن.12. Separate fault from failure.

Fault خرابیِ یک جزء است؛ failure یعنی خدمتِ موردانتظار در سطحِ سیستم انجام نمی‌شود. تحملِ خرابی می‌کوشد خرابیِ جزء به شکستِ خدمت تبدیل نشود.

مثال فنی: خرابی قطعه بدون قطع سرویس

وضعیت اولیه: سه replica پشت یک مسیریاب هستند و یک replica خاموش می‌شود.

اجرای مرحله‌به‌مرحله: health check مسیر آن را قطع می‌کند و دو replica باقی‌مانده درخواست‌ها را می‌گیرند؛ ظرفیت باقی‌مانده باید بار را تحمل کند.

نتیجه و دلیل: خاموشی یک قطعه fault است؛ اگر هدف سرویس حفظ شود failure در سطح کاربر رخ نداده است.

Fault = one part failing (disk, machine, dependency); failure = the whole system stopping service for the user (SLO breach). Same event, different name per level.

۱۳. SPOF چیست؟ مثال بزن.13. What is a SPOF? Give an example.

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

مثال فنی: Load balancerِ تنها

وضعیت اولیه: پنج replica برنامه داریم، ولی تنها load balancer ورودی خاموش می‌شود.

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

نتیجه و دلیل: همین وابستگیِ مشترک SPOF است؛ زیاد کردن replicaهای پشت آن، خرابیِ خودِ ورودی را پوشش نمی‌دهد.

A part whose failure takes the whole system down because you cannot tolerate it; e.g. a database with no replica or a lone load balancer. Answer: redundancy + spreading across availability zones.

۱۴. چرا خرابی‌های نرم‌افزاری از سخت‌افزاری بدترند؟14. Why are software faults worse than hardware ones?

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

مثال فنی: باگ مشترک همهٔ نسخه‌ها

وضعیت اولیه: ورودی size=-1 در نسخهٔ جدید worker باعث crash می‌شود.

اجرای مرحله‌به‌مرحله: توزیع همان نسخه روی ۲۰ replica، باگ را ۲۰ بار تکثیر می‌کند؛ canary و امکان rollback، دامنهٔ اثر انتشار را محدود می‌کنند.

نتیجه و دلیل: replication خرابی مستقل سخت‌افزار را بهتر پوشش می‌دهد؛ خطای مشترک کد می‌تواند همهٔ کپی‌ها را هم‌زمان بزند.

Because they are correlated: all nodes share one software and one bug (like the 2012 leap-second bug or the SSD firmware bug). Other examples: rogue processes eating shared resources, slow dependencies, cascading failure.

۱۵. fault injection و chaos engineering چه فایده‌ای دارند؟15. What is fault injection and chaos engineering good for?

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

مثال فنی: تزریق خرابیِ محدود

وضعیت اولیه: در محیط آزمون با بار مشخص، اتصال replica B برای ۳۰ ثانیه قطع می‌شود.

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

نتیجه و دلیل: تمرین خرابی، فرضِ «failover کار می‌کند» را آزمایش می‌کند؛ دامنه و معیار آزمون باید از پیش معلوم باشند.

Deliberate, random faults keep tolerance machinery constantly exercised; because many critical bugs live on the error-handling path, not the happy path.

۱۶. چرا «خطای انسانی» را علت حادثه نمی‌دانیم؟16. Why isn’t “human error” the cause of an incident?

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

مثال فنی: حفاظ عملیاتی برای حذف

وضعیت اولیه: اپراتور می‌خواهد رکوردهای tenant A را پاک کند، اما دستور بدون شرط tenant نوشته شده است.

اجرای مرحله‌به‌مرحله: preview تعداد ردیف و دامنهٔ tenant را نشان می‌دهد و دسترسی محدود اجازهٔ حذف tenant B را نمی‌دهد.

نتیجه و دلیل: بررسی باید به نبود حفاظ برسد؛ گفتنِ «اپراتور اشتباه کرد» سازوکار جلوگیری از تکرار را مشخص نمی‌کند.

Because it signals a socio-technical system problem; research shows operator config changes are the top outage cause. The right answer: blameless postmortems, better processes and priorities — not blaming the person.

۱۷. scalability چرا یک برچسب یک‌بعدی نیست؟17. Why isn’t scalability a one-dimensional label?

مقیاس‌پذیری به نوعِ بار و کیفیتِ موردنظر وابسته است. دو سؤال داریم: با منابعِ ثابت چه اتفاقی می‌افتد و برای حفظِ کیفیت چه منابعی لازم است؟

مثال فنی: مقیاس‌پذیری برای کدام بُعد؟

وضعیت اولیه: ۱۰ هزار راننده هر ۵ ثانیه موقعیت می‌فرستند: 2000 update/s.

اجرای مرحله‌به‌مرحله: افزایش به ۱۰۰ هزار راننده نرخ را به 20,000 update/s می‌رساند، حتی اگر تعداد جست‌وجوی سفر ثابت بماند.

نتیجه و دلیل: ظرفیت نوشتن موقعیت، جست‌وجوی مکانی و تاریخچه باید جدا سنجیده شوند؛ «مقیاس‌پذیر» یک عدد واحد نیست.

Because it depends on how load grows. The right questions: what happens with fixed resources? How many resources hold performance steady? When do we hit the architecture’s ceiling?

۱۸. shared-memory، shared-disk و shared-nothing را مقایسه کن.18. Compare shared-memory, shared-disk and shared-nothing.

در shared-memory پردازنده‌ها حافظهٔ مشترک دارند؛ در shared-disk چند گره به ذخیرهٔ مشترک دسترسی دارند؛ در shared-nothing هر گره منابعِ خودش را دارد و از شبکه هماهنگ می‌شود.

مثال فنی: سه معماریِ اشتراک منابع

وضعیت اولیه: دو CPU ممکن است RAM مشترک داشته باشند؛ دو سرور ممکن است فقط دیسک مشترک داشته باشند.

اجرای مرحله‌به‌مرحله: در shared-nothing هر سرور حافظه و دیسک خودش را دارد و داده از شبکه جابه‌جا می‌شود.

نتیجه و دلیل: گلوگاه و دامنهٔ خرابی متفاوت‌اند؛ افزایش تعداد CPU در هر سه مدل رفتار یکسانی ندارد.

Shared-memory (scale up): beefier machine, superlinear cost; shared-disk (NAS/SAN): shared disk, locking and contention limits; shared-nothing (scale out): independent nodes, linear potential, but needs sharding and distribution complexity.

۱۹. چرا طراحی برای مقیاسِ فرضی می‌تواند به یک محصولِ تازه آسیب بزند؟19. Why shouldn’t a startup worry about scale?

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

مثال فنی: مقیاسِ فرضی، هزینهٔ واقعی

وضعیت اولیه: محصول تازه هزار کاربر دارد و آزمون بار روی یک پایگاه داده هدف را تأمین کرده است.

اجرای مرحله‌به‌مرحله: ساختِ پیشاپیشِ ۳۲ shard، مسیرِ join و migration و پایش را پیچیده می‌کند و زمان تیم را از قابلیت‌های لازم می‌گیرد.

نتیجه و دلیل: به‌جای ظرفیتِ خیالی، بار و حاشیهٔ ظرفیتِ فعلی را اندازه می‌گیریم و مرزِ نیاز به تغییر را ثبت می‌کنیم.

Because the priority is learning, simplicity and flexibility; worrying about hypothetical scale is either wasted effort or locks the design rigid. Once you succeed, you see the bottlenecks and rethink the architecture per 10x of growth.

۲۰. سه اصل maintainability و مثال irreversibility؟20. The three maintainability principles, plus an irreversibility example?

سه اصل عبارت‌اند از آسانیِ عملیات، سادگیِ فهم و امکانِ تغییر. تصمیمِ برگشت‌ناپذیر هزینهٔ آینده را زیاد می‌کند؛ نگه داشتنِ مسیرِ بازگشت ارزش دارد.

مثال فنی: تغییرِ قابل‌فهم و قابل‌بازگشت

وضعیت اولیه: قرار است phone به دو فیلد country_code و number تبدیل شود.

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

نتیجه و دلیل: تغییر مرحله‌ای زمان تشخیص و بازگشت می‌دهد؛ حذف فوری فیلد قدیمی انتشار نسخهٔ قبلی را ناممکن می‌کند.

Operability (making smooth operation easy), simplicity (understandability via abstraction), evolvability (making change easy). Irreversibility example: migrating databases with no way back — a reversible decision is far less risky.

۶.۳ اشتباهات رایج در جواب‌های مصاحبه6.3 Common interview mistakes

  • دادنِ فقط «میانگین response time» بدون percentile.
  • گفتنِ «X مقیاس‌پذیر است» بدون توصیف بار و نوع رشد.
  • مقایسه نکردن عددیِ راه‌حل‌ها (۴۰۰M lookup در برابر ۱.۱۶M نوشتن).
  • فراموش کردن حالت‌های حدی (سلبریتی، اوج بار، کاربران پرمصرف).
  • یکی دانستنِ fault و failure، یا فراموش کردن SPOF.
  • سرزنش «خطای انسانی» به‌جای تحلیل سیستمیِ حادثه.
  • Quoting only “mean response time” with no percentile.
  • Saying “X is scalable” without describing load and growth type.
  • No numeric comparison of fixes (400M lookups vs 1.16M writes).
  • Forgetting edge cases (celebrities, peaks, heavy users).
  • Conflating fault and failure, or forgetting SPOF.
  • Blaming “human error” instead of analyzing the system.

۶.۴ چک‌لیست مرور ۵ دقیقه‌ای قبل از مصاحبه6.4 Five-minute pre-interview checklist

  1. اعداد کیس تایم‌لاین را از حفظ بگو: ۵,۸۰۰ پست/ثانیه، ×۲۰۰ فالوور، ۱.۱۶M نوشتن/ثانیه، ۴۰۰M lookup/ثانیه.
  2. تعریف response/service/latency/queueing + چرا سمت کلاینت اندازه بگیریم.
  3. p50/p95/p99 + tail latency amplification + قانونِ «percentileها را average نکن».
  4. fault vs failure، SPOF، chaos engineering.
  5. سه مدل مقیاس‌پذیری + دو سؤالِ قبل از scale.
  6. سه اصل maintainability + irreversibility.
  1. Recite the timeline-case numbers: 5,800 posts/s, ×200 followers, 1.16M writes/s, 400M lookups/s.
  2. Response/service/latency/queueing definitions + why we measure at the client.
  3. p50/p95/p99 + tail latency amplification + the “never average percentiles” rule.
  4. Fault vs failure, SPOF, chaos engineering.
  5. Three scalability models + the two pre-scaling questions.
  6. Three maintainability principles + irreversibility.

۷7 تمرین‌های مصاحبه و آزمایشگاهInterview exercises and hands-on labs

این تمرین‌ها برای مسیر Backend / Distributed Systems در ۱۴ اکتبر ۲۰۲۶ بازبینی شدند. سطح مربوط به نسخهٔ مصاحبه‌ای است؛ زمان آزمایشگاه جداست.Reviewed on 14 October 2026 for Backend / Distributed Systems. Level refers to the interview brief; full-lab time is separate. روش ارزیابی و مسیر مطالعهMethod and study path

تمرین ۱ — محاسبهٔ بارExercise 1 — load math

L1 · پایهL1 · Foundationsاولویت اصلیCore priority25 دقیقه بحث25 min discussion

روز آموزشی ۸۶۴۰۰ ثانیه و ضریب اوج ۱۰ است؛ هر سفارش سه پیام اعلان می‌سازد. کش، یک سطر خلاصهٔ سفارش با کلید order_id در k مقصد منطقی است؛ k را جدا از سه سرویس تعیین کن.A teaching day has 86400 seconds and the peak factor is 10; each order emits three notifications. The cache stores an order-summary row keyed by order_id in k logical destinations; k is independent of the three services.

نسخهٔ بازبینی‌شده برای مصاحبهReviewed interview brief

برای دو میلیون سفارش در روز و اوج ده‌برابری، RPS و تعداد سه پیام داخلی به‌ازای هر سفارش را حساب کن. سپس یک view مشخص تعریف کن که هر سفارش در k مقصد آن نوشته می‌شود؛ write amplification، نرخ خواندن، اندازهٔ payload و lag را مقایسه کن و دربارهٔ materialization تصمیم بگیر.Calculate average and peak orders/s and three internal notifications per order. Define a materialized view with k destinations, then compare extra writes, read savings, payload size and freshness.

معیار پاسخ خوبWhat a good answer demonstrates

  • ۲۳٫۱۵ و ۲۳۱٫۵ سفارش در ثانیه را درست محاسبه می‌کندCorrect average/peak math
  • سه پیام را با k نوشتن کش یکی نمی‌گیردNotification versus view fan-out
  • فرض‌ها و نقطهٔ سربه‌سر را بیان می‌کندExplicit break-even assumptions

پیگیری مصاحبه:Interview follow-up: اگر یک مشتری بزرگ k را صد برابر کند چه اتفاقی برای tail latency می‌افتد؟What happens to tail latency when one large customer increases k by a factor of 100?

آزمایشگاه تکمیلی · زمان تقریبی ۳۰–۶۰ دقیقهExtended hands-on laboratory · estimated 30–60 minutes

روزانه ۲ میلیون سفارش و سه اعلان به سرویس‌های موجودی، ریسک و اطلاع‌رسانی داریم. با فرض آموزشی اوج ده‌برابری، سفارش و پیام بر ثانیه را جدا حساب کن. برای کش، یک سطر خلاصهٔ سفارش در k مقصد منطقی تعریف کن؛ ابتدا k=1 و سپس k=5 را بسنج. هر تغییر سفارش در هر مقصد یک upsert دارد؛ تکثیر فیزیکی ذخیره‌ساز را جدا بشمار. نرخ خواندن Q، هزینهٔ خواندن مرجع C_db، هزینهٔ خواندن کش C_cache و هزینهٔ نگهداری هر نوشتن C_w را با واحد سازگار تعریف کن. شرط Q×(C_db−C_cache) > λ_order×k×C_w را از مقایسهٔ هزینه‌ها به دست بیاور؛ اندازهٔ payload، ترافیک و lag مجاز را هم بسنج. توضیح بده چرا سه اعلان لزوماً سه نوشتن کش نیست.

Assume 2M orders/day, three internal notifications per order and a teaching peak factor of ten. Calculate orders/s and messages/s separately. Define a cache containing one order-summary row in k logical destinations; compare k=1 and k=5, with one upsert per destination per order change. Count physical storage replication separately. Define read rate Q, source-read cost C_db, cache-read cost C_cache and per-write maintenance cost C_w in compatible units. Derive Q×(C_db−C_cache) > λ_order×k×C_w by comparing saved read cost with added write cost. Include payload, bandwidth and allowed lag; explain why three notifications do not imply three cache writes.

برای یادگیریِ عمیق است؛ انجام کاملش در زمان بحثِ مصاحبه انتظار نمی‌رود.The full laboratory is optional depth work and is not expected within the discussion time.

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Amazon · SDE III interview preparation · Google SRE · implementing SLOs

تمرین ۲ — نوشتن SLO و SLAExercise 2 — writing SLO and SLA

L2 · SeniorL2 · Seniorاولویت اصلیCore priority30 دقیقه بحث30 min discussion

SLO، تجربهٔ کاربر و پیامد خرابی از کاربردی‌ترین بخش‌های این مجموعه‌اند؛ داشبورد باید به اقدام وصل شود.A strong operational exercise when monitoring leads to action.

نسخهٔ بازبینی‌شده برای مصاحبهReviewed interview brief

برای استعلام وضعیت سفارش SLI و SLO با بازهٔ اندازه‌گیری، latency percentile و availability تعریف کن. مرز success، timeout و خطای وابستگی را روشن کن. error budget و هشدار burn rate را طراحی کن و SLO داخلی را از تعهد قراردادی SLA جدا کن.Define order-status SLIs, latency and availability SLOs, measurement windows, error budget and burn-rate alerts. Distinguish internal objectives from contractual SLA remedies.

معیار پاسخ خوبWhat a good answer demonstrates

  • صورت و مخرج SLI روشن استSLI numerator and denominator
  • percentile و availability دو معیار مستقل‌اندLatency versus availability
  • هشدار، rollback و تصمیم انتشار به budget وصل‌اندBudget-driven alerts and rollback

پیگیری مصاحبه:Interview follow-up: میانگین عالی است ولی p99 خراب؛ کدام گروه کاربران را بررسی می‌کنی؟The average latency is excellent but p99 is poor. Which user groups do you investigate?

آزمایشگاه تکمیلی · زمان تقریبی ۳۰–۶۰ دقیقهExtended hands-on laboratory · estimated 30–60 minutes

برای سرویسِ «استعلام وضعیت سفارش» یک SLO بنویس با percentile و دسترس‌پذیری؛ بعد مشخص کن اگر SLO نقض شد، SLA چه پیامدی برای مشتری دارد و چه چیزی در داشبورد مانیتورینگ باید دیده شود.

Write an SLO with percentiles and availability for an “order status” service; then state what the SLA promises the customer if the SLO breaks, and what must show on the monitoring dashboard.

برای یادگیریِ عمیق است؛ انجام کاملش در زمان بحثِ مصاحبه انتظار نمی‌رود.The full laboratory is optional depth work and is not expected within the discussion time.

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Google SRE · implementing SLOs · Atlassian · engineering interview handbook

تمرین ۳ — حذف SPOFExercise 3 — removing SPOFs

L2 · SeniorL2 · Seniorاولویت اصلیCore priority45 دقیقه بحث45 min discussion

multi-AZ و rolling upgrade مهم‌اند؛ داشتن دو ماشین به‌تنهایی SPOFهای کنترل، state و شبکه را حذف نمی‌کند.Two machines do not eliminate every single point of failure.

نسخهٔ بازبینی‌شده برای مصاحبهReviewed interview brief

یک API ثبت سفارش با پایگاه داده و صف طراحی کن که خرابی یک ماشین یا AZ را تحمل کند. failure domain، quorum، fencing، session state، health check، ظرفیت AZ باقی‌مانده و RPO/RTO را مشخص کن. انتشار سازگار و rollback بدون توقف را توضیح بده.Design an order API, database and queue that survive one host or AZ failure. Specify quorum, fencing, health checks, spare capacity, RPO/RTO, compatible rolling upgrades and rollback.

معیار پاسخ خوبWhat a good answer demonstrates

  • SPOFهای data plane و control plane را می‌یابدData/control-plane failure domains
  • HA را با backup و disaster recovery یکی نمی‌گیردHA versus backup/DR
  • در ظرفیت و schema rollout شرط قابل آزمون می‌دهدCapacity and rollout acceptance gates

پیگیری مصاحبه:Interview follow-up: در rolling upgrade نسخه‌های قدیم و جدید هم‌زمان فعال‌اند؛ تغییر schema را چگونه منتشر می‌کنی؟Old and new binaries coexist during rolling deployment. How do you roll out a schema change?

آزمایشگاه تکمیلی · زمان تقریبی ۳–۶ ساعتExtended hands-on laboratory · estimated 3–6 hours

معماری یک سرویس نمونه را بردار، SPOFهایش را فهرست کن و طرحی بده که در آن: از دست رفتن یک ماشین یا یک منطقهٔ در دسترسی (AZ) سرویس را نکشد، و بتوان بدون downtime انتشار نسخهٔ جدید داد (rolling upgrade). هر تصمیم را با تریدآفش بنویس.

Take a sample service architecture, list its SPOFs, and sketch a design where losing one machine or one availability zone (AZ) doesn’t kill the service, and new versions ship with no downtime (rolling upgrades). Write each decision with its trade-off.

برای یادگیریِ عمیق است؛ انجام کاملش در زمان بحثِ مصاحبه انتظار نمی‌رود.The full laboratory is optional depth work and is not expected within the discussion time.

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Microsoft · technical interviewing · Atlassian · engineering interview handbook