DDIA·مطالعهSTUDY

نقشه راهRoadmap/ فصل ۶Chapter 6

تکثیر (Replication)Replication

تکثیر داده به ادامهٔ خدمت و تقسیمِ خواندن کمک می‌کند، اما نسخه‌ها همیشه هم‌زمان به‌روز نمی‌شوند. این فصل روشِ تک‌رهبر، چندرهبر و بدون‌رهبر و پیامدِ تأخیر و تعارض را توضیح می‌دهد.Keeping one copy of the data on several machines sounds simple — until you send the first write; all of replication's pain lives in change. Three algorithm families — single-leader, multi-leader and leaderless — pay different prices: from failover and split brain to replication lag, read-your-write anomalies, and concurrent-write conflicts tamed with version vectors and CRDTs. With 9 interactive diagrams (6 Archify stages + 3 concept sketches), a teacher narration for each diagram, and an interview question pack.

۹9 نمودار تعاملیinteractive diagrams· محور: رهبر · تأخیر تکثیر · حل تعارض · quorumTheme: leaders · replication lag · conflict resolution · quorums· زمان مطالعه: حدود ۶۰ دقیقهReading time: ~60 min

۰0 نقشهٔ راه فصلChapter roadmap

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

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

مثال فنی: نسخهٔ سالم، نسخهٔ تازه؟

وضعیت اولیه: رهبر قیمت P7=120 را ثبت کرده، اما follower هنوز 100 دارد.

اجرای مرحله‌به‌مرحله: درخواست نوشتن موفق شده است؛ خواندن از follower عقب‌مانده ممکن است ۱۰۰ را برگرداند.

نتیجه و دلیل: replication هم دسترس‌پذیری می‌دهد و هم مسئلهٔ تازگی می‌سازد؛ باید مشخص کنیم کدام خواندن چه تضمینی می‌خواهد.

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

Replication means keeping a copy of the same data on several networked machines. If data were static, the job would be done: copy once and finish. All of replication's difficulty lies in managing change — and every solution brings its own anomaly.

This chapter compares three algorithm families: single-leader (one leader accepts all writes), multi-leader (several leaders write concurrently) and leaderless (every replica writes directly). Almost every distributed database uses one of these three approaches.

پنج هدفی که تکثیر دنبال می‌کند (از جمع‌بندیِ پایان فصل):

Five goals replication pursues (from the chapter's closing summary):

  • High availability: سیستم حتی وقتی یک ماشین (یا یک zone یا کل یک region) می‌افتد، کار کند.
  • Durability: حتی اگر یک ماشین برای همیشه از بین رفت، داده از بین نرود.
  • Disconnected operation: برنامه با وجود قطعیِ شبکه به کارش ادامه دهد.
  • Latency: داده جغرافیایی نزدیکِ کاربران باشد تا تعامل سریع‌تر شود.
  • Scalability: با افزودنِ replica، حجمِ خواندن بیش از توانِ یک ماشین پاسخ داده شود.
  • High availability: the system keeps working even when one machine (or one zone, or a whole region) goes down.
  • Durability: even if a machine is destroyed forever, the data is not lost.
  • Disconnected operation: the application keeps working despite network outages.
  • Latency: data sits geographically close to users so interaction is faster.
  • Scalability: adding replicas serves read volume beyond one machine's capacity.
دامِ اصطلاح: تکثیر ≠ بکاپTerm trap: replication ≠ backup

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

Replication ships one node's writes to the rest quickly; a backup keeps an old snapshot of the data so you can return to the past. If you delete data by mistake, that deletion replicates too! Restoring data needs a backup — and conversely, archiving the replication log can be part of the backup process.

نکتهٔ مصاحبهInterview tip

جملهٔ طلایی این فصل: «تمامِ سختیِ تکثیر در تغییرات است» و اینکه replication lag حد بالا ندارد. در حالتِ عادی چند دهمِ ثانیه است، اما تحتِ بار یا مشکلِ شبکه به دقیقه‌ها می‌رسد — و هر تضمینِ «در نهایت سازگار» باید با این واقعیت سنجیده شود.

The golden line of this chapter: “all of replication's pain lives in change”, and replication lag has no upper bound. Normally it is a fraction of a second, but under load or network trouble it reaches minutes — and every “eventually consistent” guarantee must be measured against that reality.

۱1 تکثیر چرا و چیست؟Why and what is replication?

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

مثال فنی: سه کپی با backup فرق دارد

وضعیت اولیه: جدول به سه replica تکثیر می‌شود؛ دستور اشتباه همهٔ سفارش‌ها را حذف می‌کند.

اجرای مرحله‌به‌مرحله: حذف به هر سه replica می‌رسد؛ snapshot قدیمی و لاگِ مناسب امکان بازیابیِ قبل از حذف را می‌دهند.

نتیجه و دلیل: replication بیشتر برای ادامهٔ سرویس است؛ نگهداری تاریخچهٔ بازیابی وظیفهٔ backup است.

Every node holding a copy of the database is a replica. The fundamental question: how do we make sure all data reaches all replicas? Every write must be processed by every replica, otherwise they no longer hold the same data.

خانوادهچه کسی می‌نویسد؟نقطهٔ قوتبهایش
Single-leader فقط leader؛ followerها از روی replication log کپی می‌گیرند ساده‌فهم؛ ترتیبِ نوشته‌ها مشخص؛ سازگاریِ نسبتاً قوی اگر leader در دسترس نباشد، نوشتن ممکن نیست؛ failover دردسرساز است
Multi-leader چند node هم‌زمان (مثلاً یک leader در هر region) تحملِ قطعیِ region و شبکه؛ تأخیرِ نوشتنِ محلی تعاریضِ نوشته‌های هم‌زمان و نیاز به conflict resolution؛ سازگاریِ ضعیف‌تر
Leaderless هر replica مستقیم از کلاینت می‌نویسد (Dynamo-style) بدون failover؛ مقاوم در برابر node کند/خراب (gray failure) quorum، خواندنِ stale و نیاز به تشخیصِ تعارضِ نوشته‌ها
FamilyWho writes?StrengthPrice
Single-leaderOnly the leader; followers copy from the replication logEasy to reason about; defined write order; relatively strong consistencyNo writes when the leader is unavailable; failover is painful
Multi-leaderSeveral nodes concurrently (e.g. one leader per region)Survives region/network outages; local write latencyConcurrent-write conflicts and conflict-resolution duty; weaker consistency
LeaderlessEvery replica writes directly from clients (Dynamo-style)No failover; resilient to slow/failed nodes (gray failure)Quorums, stale reads, and write-conflict detection duty

کتاب برای سادگی فرض می‌کند کلِ دیتاست روی هر ماشین جا می‌شود؛ فصل ۷ این فرض را کنار می‌گذارد و از sharding حرف می‌زند. تمرکزِ این فصل هم روی «تغییرات» است: هم‌زمان یا ناهمزمان تکثیر کنیم؟ نوشته‌های ازدست‌رفته هنگامِ failover چه می‌شوند؟ lag چه ناهنجاری‌هایی می‌سازد؟ و نوشته‌های هم‌زمان چطور ادغام می‌شوند؟

For simplicity the book assumes the whole dataset fits on every machine; Chapter 7 drops that assumption and talks about sharding. This chapter's focus is “change”: replicate synchronously or asynchronously? What happens to lost writes during failover? What anomalies does lag create? And how do concurrent writes merge?

۲2 تکثیر تک‌رهبرSingle-leader replication

در تکثیر تک‌رهبر، نوشتن از یک گرهٔ اصلی می‌گذرد و بقیه تغییرات او را دنبال می‌کنند. خواندن از دنبال‌کننده ممکن است دادهٔ کمی قدیمی‌تر نشان دهد.

مثال فنی: رهبر ترتیب می‌دهد

وضعیت اولیه: دو کلاینت قیمت P7 را به ۱۲۰ و ۱۳۰ تغییر می‌دهند.

اجرای مرحله‌به‌مرحله: رهبر آن‌ها را در لاگ با موقعیت‌های 51 و 52 می‌نویسد؛ followerها همان ترتیب را اعمال می‌کنند.

نتیجه و دلیل: follower‌ای که تا ۵۱ رسیده، ۱۲۰ را دارد؛ پس ترتیب یکسان الزاماً به‌معنی هم‌زمان تازه بودن نیست.

The most common solution is leader-based replication (other names: primary-backup, active/passive):

  1. یکی از replicaها به‌عنوان leader (primary / source of truth) انتخاب می‌شود و همهٔ نوشته‌ها را می‌پذیرد.
  2. بقیهٔ replicaها follower‌اند (read replica، secondary، hot standby). leader هر تغییرِ داده را در قالبِ replication log یا change stream برایشان می‌فرستد.
  3. خواندن از هر replica ممکن است؛ بعضی پایگاه داده‌ها از follower فقط خواندن می‌پذیرند، بعضی نوشتنِ مستقیمِ follower را هم (برای تکثیرِ چندرهبر) مجاز می‌دانند.
  1. One replica is elected leader (primary / source of truth) and accepts all writes.
  2. The rest are followers (read replicas, secondaries, hot standbys). The leader ships every data change to them as a replication log or change stream.
  3. Reads may come from any replica; some databases accept only reads on followers, others also allow direct follower writes (for multi-leader replication).

این مدل در PostgreSQL، MySQL، Oracle، SQL Server، MongoDB، DynamoDB و حتی Kafka و سیستم‌های consensus مثل Raft (CockroachDB، TiDB، etcd) پایهٔ کار است.

This model underlies PostgreSQL, MySQL, Oracle, SQL Server, MongoDB, DynamoDB, even Kafka and consensus systems like Raft (CockroachDB, TiDB, etcd).

هم‌زمان در برابر ناهمزمانSynchronous vs asynchronous

حالترهبر چه می‌کند؟مزیتخطر
Synchronous منتظر تأییدِ follower می‌ماند، بعد به کلاینت «موفق» می‌گوید follower حتماً نسخهٔ به‌روز دارد؛ داده روی دو node مطمئن است اگر آن follower پاسخ ندهد، همهٔ نوشته‌ها متوقف می‌شوند
Semisynchronous یک follower هم‌زمان، بقیه ناهمزمان؛ اگر آن یکی افتاد، follower دیگری جایگزین می‌شود همیشه حداقل دو نسخهٔ به‌روز (leader + یک follower) سربارِ بیشتر؛ هنوز هم ممکن است یک نوشته «تأییدشده» اما ناپایدار باشد
Asynchronous پیام را می‌فرستد و منتظر نمی‌ماند leader حتی اگر همهٔ followerها عقب باشند، نوشتن را ادامه می‌دهد اگر leader از بین برود، نوشته‌های تأییدشده ولی تکثیرنشده گم می‌شوند
ModeWhat does the leader do?AdvantageRisk
SynchronousWaits for the follower's ack, then tells the client “success”The follower is guaranteed up to date; data is safe on two nodesIf that follower stops responding, all writes stall
SemisynchronousOne follower synchronous, the rest async; if it fails, another follower replaces itAlways at least two up-to-date copies (leader + one follower)More overhead; an “acknowledged” write may still be non-durable
AsynchronousSends the message and does not waitThe leader keeps writing even if all followers lagIf the leader dies, acknowledged-but-unreplicated writes are lost
نمودار مفهومی · خط زمانی — هم‌زمان، نیمه‌هم‌زمان و ناهمزمانConcept sketch · Timeline — synchronous, semisynchronous and asynchronous باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار مفهومیTeacher narration of the concept sketch

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

وضعیت اولیه: رهبر تغییر شمارهٔ ۵۲ را روی دیسک خود پایدار کرده؛ follower هنوز آن را نگرفته است.

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

نتیجه و دلیل: خرابیِ رهبر پیش از انتقال می‌تواند نوشتهٔ async را در failover از دست بدهد؛ sync هزینهٔ انتظار و وابستگی دارد.

سه خط افقی، سه قرارداد متفاوت با کلاینت را نشان می‌دهد. خط بالا (Synchronous): کلاینت می‌نویسد، رهبر لاگ را می‌فرستد و منتظر تأیید می‌ماند؛ فقط بعد از تأیید، «موفق» برمی‌گردد — پس داده روی دو node مطمئن است، اما یک follower کند همهٔ نوشتن‌ها را می‌بندد.

خط وسط (Semisync): رهبر فقط منتظر یک تأیید است و بقیه ناهمزمان‌اند؛ همیشه دو نسخهٔ به‌روز داری و اگر آن یکی افتاد، follower دیگری جایش را می‌گیرد. خط پایین (Asynchronous): فلش چین‌چین یعنی «بفرست و فراموش کن» — رهبر هیچ‌وقت منتظر نمی‌ماند و «موفق» فوری برمی‌گردد، اما نوشته‌های تأییدشده‌ای که هنوز تکثیر نشده‌اند با مرگ رهبر گم می‌شوند.

Three horizontal lanes show three different contracts with the client. Top lane (Synchronous): the client writes, the leader ships the log and waits for the ack; only then does “success” return — so data is safe on two nodes, but one slow follower blocks all writes.

Middle lane (Semisync): the leader waits for just one ack while the rest stay async; you always hold two fresh copies, and if that one fails another follower takes its place. Bottom lane (Asynchronous): the dashed arrow means “fire and forget” — the leader never waits and “success” returns at once, but acknowledged-yet-unreplicated writes are lost if the leader dies.

چرا «همه‌چیز هم‌زمان» غیرعملی است؟Why is “everything synchronous” impractical?

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

مثال فنی: زمان تأیید در دو روش

وضعیت اولیه: رهبر تغییر شمارهٔ ۵۲ را روی دیسک خود پایدار کرده؛ follower هنوز آن را نگرفته است.

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

نتیجه و دلیل: خرابیِ رهبر پیش از انتقال می‌تواند نوشتهٔ async را در failover از دست بدهد؛ sync هزینهٔ انتظار و وابستگی دارد.

If all followers were synchronous, any single node failure would halt the whole system — and the more nodes, the likelier one fails. That is why “synchronous replication” in practice means semisynchronous, or in some systems a majority of replicas (e.g. 3 of 5) synchronous — an instance of quorum, returning in Chapter 10.

۲.۱ نمودار ۱ + روایت مدرس2.1 Diagram 1 + teacher narration

نقشهٔ کلیِ مسیرِ نوشتن و مسیرِ خواندن در تکثیرِ تک‌رهبر — از کلاینتِ نویسنده تا followerهای هم‌زمان و ناهمزمان، و از کلاینتِ خواننده تا همان followerهایی که ممکن است عقب باشند:

The big picture of the write path and the read path in single-leader replication — from the writing client to synchronous and asynchronous followers, and from the reading client to those same followers that may lag:

نمودار ۱ · Dataflow — تکثیر تک‌رهبر: نوشتن، لاگ و خواندن‌های کهنهDiagram 1 · Dataflow — single-leader replication: writes, log and stale reads باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۱Teacher narration of diagram 1

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

وضعیت اولیه: دو کلاینت قیمت P7 را به ۱۲۰ و ۱۳۰ تغییر می‌دهند.

اجرای مرحله‌به‌مرحله: رهبر آن‌ها را در لاگ با موقعیت‌های 51 و 52 می‌نویسد؛ followerها همان ترتیب را اعمال می‌کنند.

نتیجه و دلیل: follower‌ای که تا ۵۱ رسیده، ۱۲۰ را دارد؛ پس ترتیب یکسان الزاماً به‌معنی هم‌زمان تازه بودن نیست.

نمودار دو خطِ موازی دارد که از چپ به راست در چهار مرحله جلو می‌روند: خطِ بالا مسیرِ نوشتن است و خطِ پایین مسیرِ خواندن. در مرحلهٔ اول، Clients، دو گره می‌بینی: «Write Client» (زیرنویس: «sends every write») و پایین‌تر «Read Client» (زیرنویس: «scales out reads»). برچسبِ گرهِ بالا صریح می‌گوید «leader-only writes» و پایینی «any replica» — همین دو برچسب، کلِ معماری را خلاصه می‌کند.

فلشِ پررنگِ «all writes through the leader» از Write Client به مرحلهٔ دوم می‌رود، به گرهِ «Leader / Primary» (زیرنویس: «accepts all writes»، برچسبِ «serializes writes»). در همان مرحله، گرهِ پایینی «Read-Your-Writes» نشسته است: قاعده‌ای که می‌گوید اگر کاربر همین حالا چیزی نوشته، خواندنِ همان داده باید از leader باشد (برچسبِ «stale-read remedy»).

مرحلهٔ سوم، Replication، قلبِ داستان است: «Replication Log» (زیرنویس: «stmt · WAL · logical») هر تغییر را ثبت می‌کند. فلشِ پررنگِ «ship log, wait for ack» به چهارمین گرهِ بالا می‌رود: «Sync Follower» (زیرنویسِ «leader waits for ack»، برچسبِ «semisynchronous»). در خطِ پایین اما فلشِ چین‌چینِ «ship log, fire and forget» به «Async Follower» می‌رود: کسی منتظرش نمی‌ماند و برچسبش هشدار می‌دهد: «unbounded lag».

مسیرِ خواندن هم روایتِ خودش را دارد: «Read Client» با فلشِ «if the user just wrote» به Read-Your-Writes می‌رود، بعد با «otherwise read from a follower» به گرهِ «Replication Lag» (زیرنویس: «seconds to minutes»)، و سرانجامِ فلشِ «stale value until it catches up» می‌رسد به Async Follower.

درسِ شکل: نوشتن یک مسیرِ مشخص و مرکزی دارد (leader → log → follower)، اما خواندن پراکنده است و «کهنه بودنِ موقت» را باید به‌عنوان بخشی از قراردادِ سیستم پذیرفت. راه‌های مبارزه با همین کهنگی را در بخش ۳ می‌بینیم.

The diagram has two parallel lines running left to right in four stages: the upper line is the write path, the lower the read path. In stage one, Clients, you see two nodes: “Write Client” (caption: “sends every write”) and below it “Read Client” (caption: “scales out reads”). The upper node's label says it outright — “leader-only writes” — and the lower one “any replica”; those two labels summarize the whole architecture.

The bold “all writes through the leader” arrow runs from the Write Client to stage two, the “Leader / Primary” node (caption: “accepts all writes”, label: “serializes writes”). In the same stage sits the lower node “Read-Your-Writes”: the rule that if a user just wrote something, reads of that same data must come from the leader (label: “stale-read remedy”).

Stage three, Replication, is the heart of the story: the “Replication Log” (caption: “stmt · WAL · logical”) records every change. The bold “ship log, wait for ack” arrow goes to the fourth upper node, the “Sync Follower” (caption: “leader waits for ack”, label: “semisynchronous”). On the lower line, though, the dashed “ship log, fire and forget” arrow goes to the “Async Follower”: nobody waits for it, and its label warns “unbounded lag”.

The read path has its own story: the “Read Client” goes via “if the user just wrote” to Read-Your-Writes, then via “otherwise read from a follower” to the “Replication Lag” node (caption: “seconds to minutes”), and finally the “stale value until it catches up” arrow lands on the Async Follower.

The diagram's lesson: writing has one fixed, central path (leader → log → follower), but reading is scattered, and temporary staleness must be accepted as part of the system's contract. Section 3 shows how to fight that staleness.

راه‌اندازیِ follower جدیدSetting up a new follower

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

مثال فنی: Snapshot با نقطهٔ لاگ

وضعیت اولیه: هنگام ساخت follower تازه، snapshot در موقعیت 500 گرفته می‌شود.

اجرای مرحله‌به‌مرحله: snapshot کپی می‌شود و follower تغییرهای 501 به بعد را دریافت می‌کند؛ لاگ لازم تا پایان انتقال نگه داشته می‌شود.

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

Simply copying files is not enough: clients keep writing, and a naive copy sees different parts of the database at different moments. The usual zero-downtime path is:

  1. یک snapshot سازگار از پایگاه دادهِ leader در لحظه‌ای مشخص بگیر (ترجیحاً بدون قفلِ کلِ پایگاه داده؛ مثل Percona XtraBackup برای MySQL).
  2. snapshot را به node جدید کپی کن.
  3. follower به leader وصل شود و همهٔ تغییراتِ بعد از snapshot را بگیرد — snapshot باید به «موقعیتِ دقیقِ» لاگِ تکثیر گره خورده باشد (در PostgreSQL: log sequence number؛ در MySQL: binlog coordinates یا GTID).
  4. وقتی follower از عقب‌ماندگی جلو زد (caught up)، مثل قبل جریانِ تغییرات را ادامه می‌دهد.
  1. Take a consistent snapshot of the leader's database at a fixed point (preferably without locking the whole database; e.g. Percona XtraBackup for MySQL).
  2. Copy the snapshot to the new node.
  3. The follower connects to the leader and fetches every change after the snapshot — the snapshot must be tied to the replication log's exact position (in PostgreSQL: log sequence number; in MySQL: binlog coordinates or GTID).
  4. Once the follower catches up, it continues the change stream as normal.

بایگانیِ لاگِ تکثیر + اسنپ‌شات‌های دوره‌ای در object storage (مثل WAL-G برای PostgreSQL/MySQL یا Litestream برای SQLite) هم بکاپ می‌سازد و هم گام‌های ۱ و ۲ را ساده می‌کند.

Archiving the replication log + periodic snapshots in object storage (like WAL-G for PostgreSQL/MySQL or Litestream for SQLite) both builds backups and simplifies steps 1 and 2.

نکتهٔ مدرن: پایگاه‌های داده متکی به object storageModern note: object-storage-backed databases

object storage ارزان است، بازماندگیِ (durability) بسیار بالایی دارد و با قابلیتِ conditional write (همان compare-and-set) حتی می‌تواند انتخابِ leader و تراکنش را هم به عهده بگیرد. بها: تأخیرِ خواندن/نوشتنِ بسیار بالاتر از دیسکِ محلی و هزینهٔ به‌ازای هر API call. بعضی سیستم‌ها storage لایه‌ای می‌سازند، بعضی WAL را کنار می‌گذارند (مثل Safekeeperهای Neon)، و بعضی به zero-disk architecture می‌رسند: همه‌چیز در object storage، دیسک و حافظه فقط cache.

Object storage is cheap, has extremely high durability, and with conditional write (compare-and-set) can even take over leader election and transactions. The price: far higher read/write latency than local disk, plus per-API-call cost. Some systems build tiered storage, some drop the WAL (like Neon's Safekeepers), and some reach zero-disk architecture: everything in object storage, disk and memory just caches.

۲.۲ خرابیِ nodeها و failover + نمودار ۲2.2 Node failure and failover + diagram 2

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

مثال فنی: ترفیعِ کپی عقب‌مانده

وضعیت اولیه: رهبر تا موقعیت 105 را به کلاینت تأیید کرده، اما نامزد جدید فقط تا 103 دارد.

اجرای مرحله‌به‌مرحله: با ترفیع آن بدون تضمین لازم، نوشته‌های ۱۰۴ و ۱۰۵ در نمای سرویس ناپدید می‌شوند.

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

Any node can go down — from failure or from planned maintenance. The goal is for the overall system to keep working despite one node's failure.

  • خرابیِ follower (catch-up recovery): follower لاگِ تغییراتِ دریافتی را روی دیسکش دارد؛ بعد از ری‌استارت می‌داند آخرین تراکنشِ پردازش‌شده کدام بوده و از leader همان نقطه به بعد را می‌گیرد. اگر مدتها offline بوده باشد، فشارِ زیادی به leader و خودش وارد می‌شود، و leader باید بین «نگه‌داشتنِ لاگ تا بازگشتِ follower (خطرِ پر شدنِ دیسک)» و «حذفِ لاگِ تأییدنشده (follower مجبور به بازیابی از بکاپ می‌شود)» انتخاب کند.
  • خرابیِ leader (failover): باید follower جدیدی leader شود، کلاینت‌ها به او متصل شوند و بقیه از او پیروی کنند — یا دستی، یا خودکار. این فرایند پر از دام است و نمودار ۲ همان را روایت می‌کند.
  • Follower failure (catch-up recovery): the follower has its received change log on disk; after restart it knows which transaction was processed last and fetches everything after that point from the leader. If it was offline long, it puts heavy pressure on the leader and itself, and the leader must choose between “keeping the log until the follower returns (disk-full risk)” and “deleting unacknowledged log (the follower must recover from backup)”.
  • Leader failure (failover): a new follower must become leader, clients must connect to it and the rest follow it — manually or automatically. That process is full of traps, and diagram 2 narrates exactly that.
نمودار ۲ · Sequence — failover: ارتقای leader تازه و خطرهایشDiagram 2 · Sequence — failover: promoting a fresh leader and its risks باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۲Teacher narration of diagram 2

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

وضعیت اولیه: رهبر تا موقعیت 105 را به کلاینت تأیید کرده، اما نامزد جدید فقط تا 103 دارد.

اجرای مرحله‌به‌مرحله: با ترفیع آن بدون تضمین لازم، نوشته‌های ۱۰۴ و ۱۰۵ در نمای سرویس ناپدید می‌شوند.

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

چهار شرکت‌کننده از چپ به راست نشسته‌اند: «Client»، «Leader (old)» با زیرنویسِ «crashes mid-write»، «Follower 1» (زیرنویس: «most up-to-date» — به‌روزترین نسخه را دارد) و «Follower 2» (زیرنویس: «falling behind»). زمان از بالا به پایین می‌گذرد و سه نوارِ عنوان، سه پرده را جدا می‌کنند.

پردهٔ اول، «Normal operation»: فلشِ «write request» از Client به Leader، بعد فلشِ پررنگِ «replicate, wait for ack» به Follower 1 (تکثیرِ هم‌زمان) و فلشِ چین‌چینِ «replicate, no waiting» به Follower 2 (ناهمزمان).

پردهٔ دوم، «Leader failure and failover»: فلشِ «write request — no response» بی‌جواب می‌ماند و یادداشتِ زیرش می‌گوید «failure detected only by timeout» — یعنی هیچ راهِ مطمئنی برای تشخیصِ «مرگ» نیست؛ فقط timeout. بعد Follower 2 به Follower 1 پیام می‌دهد: «elect the most up-to-date follower» با یادداشتِ «consensus problem — Chapter 10»، و Follower 1 به Client اعلام می‌کند: «I am the new leader — send writes here».

پردهٔ سوم، «Old leader rejoins»: leader قدیمی برمی‌گردد («old leader rejoins the cluster») و ممکن است هنوز خودش را leader بداند. پاسخِ Follower 1 قاطع است: «step down — fencing rejects your writes» و یادداشتِ تلخِ زیرِ همان فلش: «unreplicated writes are discarded» — نوشته‌هایی که کلاینت «موفق» شنیده بود ولی تکثیر نشده بودند، دور ریخته می‌شوند.

درسِ شکل: failover سه گام دارد (تشخیص با timeout، انتخابِ به‌روزترین replica، بازپیکربندی) و سه خطرِ بزرگ: از دست رفتنِ نوشته‌ها در تکثیرِ ناهمزمان، split brain (دو node هم‌زمان خود را leader بدانند — راه‌حلش fencing است)، و انتخابِ نادرستِ timeout (کوتاه ← failoverهای بی‌دلیل زیرِ بار؛ بلند ← بازیابیِ کند).

Four participants sit left to right: “Client”, “Leader (old)” captioned “crashes mid-write”, “Follower 1” (“most up-to-date” — it holds the freshest copy) and “Follower 2” (“falling behind”). Time flows top to bottom, and three title bars split three acts.

Act one, “Normal operation”: the “write request” arrow from Client to Leader, then the bold “replicate, wait for ack” arrow to Follower 1 (synchronous replication) and the dashed “replicate, no waiting” arrow to Follower 2 (async).

Act two, “Leader failure and failover”: the “write request — no response” arrow goes unanswered, with the note “failure detected only by timeout” — there is no reliable way to detect “death”, only timeouts. Then Follower 2 messages Follower 1: “elect the most up-to-date follower” with the note “consensus problem — Chapter 10”, and Follower 1 announces to the Client: “I am the new leader — send writes here”.

Act three, “Old leader rejoins”: the old leader returns (“old leader rejoins the cluster”) and may still consider itself leader. Follower 1's answer is firm: “step down — fencing rejects your writes”, with the bitter note under that same arrow: “unreplicated writes are discarded” — writes the client heard as “successful” but never replicated are thrown away.

The diagram's lesson: failover has three steps (timeout detection, electing the freshest replica, reconfiguration) and three big risks: lost writes under async replication, split brain (two nodes both believing they lead — cured by fencing), and wrong timeout choice (too short → needless failovers under load; too long → slow recovery).

ماجرای واقعی: یک failover خطرناکTrue story: a dangerous failover

در یک حادثهٔ معروف، follower قدیمیِ یک MySQL به‌عنوان leader ارتقا یافت؛ شمارندهٔ autoincrement شناسه‌ها عقب‌تر بود و شناسه‌های تکراری تولید کرد. چون همان شناسه‌ها در یک ذخیره‌سازِ دیگر هم استفاده شده بودند، دادهٔ خصوصی به کاربرِ اشتباه نمایش داده شد. درس: تغییرِ leader فقط «یک پایگاه داده» نیست؛ هر سیستمِ دیگری که با داده هماهنگ است هم در معرض خطر است.

In one famous incident, an old MySQL follower was promoted to leader; its autoincrement ID counter lagged behind and produced duplicate IDs. Because the same IDs were used in another store, private data was shown to the wrong user. The lesson: a leader change is not “one database”; every other system coordinated with the data is at risk too.

۲.۳ ماشینِ وضعیتِ failover + نمودار ۶2.3 The failover state machine + diagram 6

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

مثال فنی: رهبر قدیمی برمی‌گردد

وضعیت اولیه: A در epoch ۷ رهبر بوده و بعد از قطع شبکه، B در epoch ۸ انتخاب شده است.

اجرای مرحله‌به‌مرحله: مقصد نوشتن epoch قدیمی را رد می‌کند؛ A پس از برگشت نمی‌تواند با مجوز ۷ نوشتن معتبر انجام دهد.

نتیجه و دلیل: تشخیص timeout به‌تنهایی A را متوقف نمی‌کند؛ fencing جلوی اثرِ رهبر منقضی را می‌گیرد.

The previous diagram showed failover as a timeline: what happens in what order. But if instead of the whole cluster you follow the life of one node, failover is a state machine: at each moment the node is in a defined state, each transition fires on a defined event, and each transition is either safe or dangerous. That definition tells us exactly where on the path to worry.

سخت‌ترین نکته این است که node خراب و node کند از بیرون یک‌سان دیده می‌شوند. وقتی heartbeatها قطع می‌شوند، هیچ راهی برای تشخیصِ «مرگِ واقعی» از «وقفهٔ طولانی» نیست: شاید leader فقط درگیرِ یک garbage collectionِ چند ثانیه‌ای باشد یا شبکه‌اش موقتاً اشباع شده باشد. در همین پنجرهٔ عدمِ قطعیت (uncertainty window) ممکن است leader قدیمی هنوز نوشته بپذیرد و هم‌زمان leader جدیدی انتخاب شود — و این یعنی split brain.

The hardest point: a crashed node and a slow node look identical from outside. When heartbeats stop, there is no way to tell “real death” from “a long pause”: the leader may just be stuck in a multi-second garbage collection or its network briefly saturated. In that uncertainty window the old leader may still accept writes while a new leader is elected — and that means split brain.

و ماجرا با «مرگ» تمام نمی‌شود: leader قدیمیِ گم‌شده ممکن است برگردد و هنوز خودش را leader بداند؛ و nodeای که مدتی بیرون بوده باید اول لاگش را به‌روز کند و بعد دوباره follower شود. نمودار ۶ همین چرخه را به‌صورتِ ماشینِ وضعیت می‌کشد: وضعیت‌های اصلی، پنجرهٔ عدمِ قطعیت، مسیرِ بازگشت، و وضعیتِ بن‌بستِ «رهبرِ کهنه‌ای که برمی‌گردد».

And the story doesn't end with “death”: the lost old leader may return still believing it leads; and a node that was away a while must first refresh its log before becoming a follower again. Diagram 6 draws that cycle as a state machine: the main states, the uncertainty window, the return path, and the dead-end state of “a stale leader that comes back”.

نمودار ۶ · Lifecycle — ماشینِ وضعیتِ failoverDiagram 6 · Lifecycle — the failover state machine باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۶Teacher narration of diagram 6

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

وضعیت اولیه: A در epoch ۷ رهبر بوده و بعد از قطع شبکه، B در epoch ۸ انتخاب شده است.

اجرای مرحله‌به‌مرحله: مقصد نوشتن epoch قدیمی را رد می‌کند؛ A پس از برگشت نمی‌تواند با مجوز ۷ نوشتن معتبر انجام دهد.

نتیجه و دلیل: تشخیص timeout به‌تنهایی A را متوقف نمی‌کند؛ fencing جلوی اثرِ رهبر منقضی را می‌گیرد.

این‌بار به‌جای خطِ زمانی، ماشینِ وضعیتِ یک node را می‌بینیم. چهار نوارِ افقی روی هم نشسته‌اند و برچسبشان عمودی کنارِ چپِ صفحه نوشته شده: بالایی «Node states» (زندگیِ عادیِ یک node سالم)، بعد «Uncertainty window» (پنجرهٔ عدمِ قطعیت)، بعد «Rejoining» (مسیرِ بازگشت) و پایینی «Terminal» (وضعیت‌های بن‌بست). سه وضعیتِ نوارِ بالا شمارهٔ گام دارند — 01 و 02 و 03 — یعنی مسیرِ اصلیِ زندگیِ یک node همین سه وضعیت است و بقیه، مسیرهای خروجی و بازگشت‌اند.

از چپِ نوارِ بالا شروع می‌کنیم: «Follower» (گامِ 01) با زیرنویسِ «replicating the log» و برچسبِ «reads may be stale». فلشِ «heartbeat timeout» او را به «Candidate» (گامِ 02) می‌برد — زیرنویسِ «requests votes» و برچسبِ «election timeout»: وقتی مدتی از leader خبری نشد، این node واردِ انتخابات می‌شود و از بقیه رأی می‌خواهد. از Candidate دو فلشِ پررنگ بیرون می‌آید: «majority of votes» به «Leader» (گامِ 03، زیرنویسِ «accepts writes»، برچسبِ «single writer»)، یا «another node wins» که Candidate را به عقب، به «Follower» برمی‌گرداند — باختنِ انتخابات ارزان‌ترین گذارِ کلِ شکل است: هیچ‌چیز خراب نشده، فقط دوباره follower شده‌ای.

حالا سراغِ خطرها. از «Leader» یک فلشِ چین‌چین به پایین می‌آید — «no heartbeats» — و به نوارِ دوم می‌رسد، یعنی «Uncertainty window»: وضعیتِ «Unreachable Leader» با زیرنویسِ «maybe just slow» و برچسبِ «cannot tell crash from pause». همین یک جعبه، تمامِ سختیِ failover است: leader «شاید» افتاده باشد و شاید فقط کند باشد. از این جعبه دو فلشِ قرمز بیرون می‌آید: «new election» که node را به «Candidate» برمی‌گرداند (مسیرِ امن: به‌جای حدس زدن، دوباره انتخابات برگزار می‌کنیم)، و فلشِ مرگبارِ «writes while away» که مستقیم به نوارِ آخر می‌رود، به «Terminal»: وضعیتِ «Stale Leader Returns» با زیرنویسِ «still thinks it leads» و برچسبِ «split brain risk». یعنی اگر leader قدیمی در همان پنجرهٔ عدمِ قطعیت به پذیرفتنِ نوشته ادامه داده باشد، بازگشتش دیگر «بازگشت» نیست؛ واردِ وضعیتِ بن‌بست می‌شود.

مسیرِ امنِ بازگشت در نوارِ سوم است، «Rejoining»: از «Leader» فلشِ «fails over» به «Recovering Node» می‌رود — زیرنویسِ «catches up on the log» و برچسبِ هشدارِ «must not lose writes»: nodeای که مدتی بیرون بوده، اول باید لاگ را تا آخرین تراکنشِ تأییدشده ببلعد. و فقط وقتی فلشِ «log is current» او را دوباره به «Follower» برمی‌گرداند که واقعاً به‌روز شده باشد. دقت کن: هیچ فلشی مستقیم از «Recovering Node» به «Leader» نمی‌رود — nodeِ عقب‌افتاده حقِ رهبری ندارد و باید از صفرِ زنجیره، یعنی از follower بودن، شروع کند.

درسِ اولِ شکل، fencing است. چون نمی‌توانیم «مرگ» را از «کندی» تشخیص دهیم، نباید به «فهمیدن» تکیه کنیم؛ باید به «بی‌اثر کردن» تکیه کنیم. راهِ عملی این است: هر leader تازه یک شمارهٔ دورهٔ بالاتر می‌گیرد (epoch یا term)، و لایهٔ ذخیره‌سازی هر نوشته‌ای را که با شمارهٔ قدیمی بیاید رد می‌کند — به این قفلِ یک‌طرفه fencing token می‌گویند. پیاده‌سازی‌های رایج: شمارهٔ epoch داخلِ خودِ لاگ، یا یک سرویسِ قفل‌گیریِ متمرکز که به محض قطع شدنِ session، قفلِ leader قدیمی را باطل می‌کند. بدونِ fencing، «رهبرِ کهنه‌ای که برمی‌گردد» همیشه یک احتمالِ واقعی است.

درسِ دوم اینکه خطرِ واقعی split brain است، نه «از دست رفتنِ یک نوشته». اگر دو node هم‌زمان خود را leader بدانند، دو تاریخچهٔ ناسازگارِ نوشتن ساخته می‌شود: شناسه‌های تکراری، مانده‌های دوبار محاسبه‌شده، و داده‌ای که دیگر قابلِ اعتماد نیست — و هیچ‌کس نمی‌داند کدام نسخه «درست» است. نکتهٔ عملیاتی هم این: failover در تولید کم اتفاق می‌افتد، پس همین مسیرِ «بن‌بست و بازگشت» کم‌ترین تست را دیده و بیشترین خرابی را می‌سازد؛ timeout را نه آن‌قدر کوتاه کن که یک موجِ ترافیک، failoverهای زنجیره‌ای راه بیندازد، و نه آن‌قدر بلند که بازیابی دقیقه‌ها طول بکشد.

This time, instead of a timeline, we see one node's state machine. Four horizontal lanes stack on top of each other, labelled vertically on the left: the top “Node states” (the normal life of a healthy node), then “Uncertainty window”, then “Rejoining” (the return path) and at the bottom “Terminal” (dead-end states). Three states on the top lane carry step numbers — 01, 02, 03 — meaning a node's main life path is these three states; the rest are exits and returns.

Start at the left of the top lane: “Follower” (step 01) captioned “replicating the log” and labelled “reads may be stale”. The “heartbeat timeout” arrow carries it to “Candidate” (step 02) — captioned “requests votes”, labelled “election timeout”: when nothing has been heard from the leader for a while, this node enters an election and asks the rest for votes. Two bold arrows leave Candidate: “majority of votes” to “Leader” (step 03, captioned “accepts writes”, labelled “single writer”), or “another node wins” sending the Candidate back to “Follower” — losing an election is the cheapest transition in the whole diagram: nothing broke, you're just a follower again.

Now the dangers. A dashed arrow drops from “Leader” — “no heartbeats” — into the second lane, the “Uncertainty window”: the “Unreachable Leader” state, captioned “maybe just slow” and labelled “cannot tell crash from pause”. That single box is the whole hardness of failover: the leader “may” be down and may just be slow. Two red arrows leave it: “new election” returning the node to “Candidate” (the safe path: hold a fresh election instead of guessing), and the deadly “writes while away” arrow dropping straight to the last lane, “Terminal”: the “Stale Leader Returns” state, captioned “still thinks it leads” and labelled “split brain risk”. If the old leader kept accepting writes during that uncertainty window, its return is no “return” — it enters a dead-end state.

The safe return path is the third lane, “Rejoining”: the “fails over” arrow runs from “Leader” to “Recovering Node” — captioned “catches up on the log” with the warning label “must not lose writes”: a node that was away a while must first swallow the log up to the last acknowledged transaction. Only when the “log is current” arrow returns it to “Follower” is it truly fresh. Note: no arrow runs directly from “Recovering Node” to “Leader” — a lagging node has no right to lead and must restart the chain from follower.

The diagram's first lesson is fencing. Because we cannot distinguish “death” from “slowness”, we must rely not on “figuring out” but on “neutralizing”. The practical way: each fresh leader takes a higher term number (epoch or term), and the storage layer rejects any write stamped with an old number — that one-way lock is called a fencing token. Common implementations: the epoch number inside the log itself, or a centralized lock service that invalidates the old leader's lock as soon as its session drops. Without fencing, “a stale leader that returns” is always a live possibility.

The second lesson: the real danger is split brain, not “losing one write”. If two nodes both believe they lead, two incompatible write histories are built: duplicate IDs, double-computed balances, data nobody can trust — with no one knowing which version is “right”. The operational note: failover is rare in production, so this “dead-end and return” path is the least tested and causes the most outages; set the timeout neither so short that one traffic wave triggers chained failovers, nor so long that recovery takes minutes.

۲.۴ پیاده‌سازیِ لاگ‌های تکثیر2.4 Implementing replication logs

روشایدهبها / نکتهٔ کلیدی
Statement-based هر دستورِ SQL (INSERT/UPDATE/DELETE) عیناً برای follower فرستاده و اجرا شود توابعِ غیرقطعی مثل NOW() و RAND()، وابستگی به دادهٔ موجود و ترتیبِ اجرای دقیق؛ اجرای side effectها متفاوت می‌شود
WAL shipping همان write-ahead log موتورِ ذخیره‌سازی (بایت‌های تغییریافته در هر بلاک) شبکه‌ای فرستاده شود لاگ در سطحِ بایت است و تکثیر را به موتورِ ذخیره‌سازی گره می‌زند؛ نسخه‌های متفاوتِ نرم‌افزار روی leader و follower معمولاً ممکن نیست ← ارتقای بدونِ downtime هم ممکن نیست
Logical (row-based) لاگِ جدا از موتور: رکوردهایی در سطحِ سطر (سطرِ جدید: همهٔ ستون‌ها؛ حذف: کلید اصلی؛ به‌روزرسانی: کلید + مقادیر تازه) سازگاریِ backward ساده‌تر است (ارتقای بدونِ downtime) و برای سیستم‌های بیرونی قابل‌تحلیل — پایهٔ change data capture (فصل ۱۲)
MethodIdeaKey trade-off
Statement-basedEvery SQL statement (INSERT/UPDATE/DELETE) is sent verbatim to the follower and executedNondeterministic functions like NOW() and RAND(), dependence on existing data and exact execution order; side effects execute differently
WAL shippingThe storage engine's own write-ahead log (changed bytes in each block) is shipped over the networkThe log is byte-level and ties replication to the storage engine; different software versions on leader and follower are usually impossible → zero-downtime upgrades impossible too
Logical (row-based)A log decoupled from the engine: row-level records (new row: all columns; delete: primary key; update: key + fresh values)Backward compatibility is simpler (zero-downtime upgrades) and analyzable for outside systems — the basis of change data capture (Chapter 12)

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

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

وضعیت اولیه: دستور UPDATE ... SET updated_at=NOW() روی رهبر اجرا می‌شود.

اجرای مرحله‌به‌مرحله: تکثیر statement ممکن است در مقصد زمان متفاوت بسازد؛ row-log مقدار نهایی ردیف را حمل می‌کند؛ لاگ فیزیکی به ساختار داخلی موتور وابسته است.

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

The important operational note on WAL shipping: if a follower can run a newer software version, you can upgrade followers first and then fail over — a zero-downtime upgrade. With a physical log, that trick is impossible.

نمودار مفهومی · رده‌بندی — سه روش پیاده‌سازی لاگ تکثیرConcept sketch · Taxonomy — three replication-log implementations باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار مفهومیTeacher narration of the concept sketch

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

وضعیت اولیه: دستور UPDATE ... SET updated_at=NOW() روی رهبر اجرا می‌شود.

اجرای مرحله‌به‌مرحله: تکثیر statement ممکن است در مقصد زمان متفاوت بسازد؛ row-log مقدار نهایی ردیف را حمل می‌کند؛ لاگ فیزیکی به ساختار داخلی موتور وابسته است.

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

سه ستون، سه جواب به یک پرسش‌اند: «تغییر را با چه دانه‌بندی‌ای بفرستیم؟» ستون اول (Statement-based): خودِ دستور SQL فرستاده می‌شود و follower دوباره اجرایش می‌کند — ساده است اما توابع غیرقطعی مثل NOW() دو نسخهٔ متفاوت می‌سازند.

ستون وسط (WAL shipping): بایت‌های فیزیکی لاگ موتور فرستاده می‌شود — سریع و دقیق است اما تکثیر را به همان موتور و همان نسخه قفل می‌کند و ارتقای بدون downtime را ناممکن می‌سازد. ستون سوم (Logical): رکوردهای سطح سطر، جدا از موتور — با نسخه‌های متفاوت سازگار است و همان لاگ، خوراک change data capture برای سیستم‌های بیرونی می‌شود. درس: اگر انتخاب با توست، منطقی (logical) را بخر.

Three columns answer one question: “at what granularity do we ship change?” First column (Statement-based): the SQL statement itself ships and the follower re-executes it — simple, but nondeterministic functions like NOW() build two different versions.

Middle column (WAL shipping): the engine's physical log bytes ship — fast and exact, but it locks replication to that same engine and version, killing zero-downtime upgrades. Third column (Logical): row-level records, decoupled from the engine — compatible across versions, and that same log feeds change data capture for outside systems. The lesson: if the choice is yours, buy logical.

۳3 مشکلاتِ replication lagReplication lag problems

تأخیر تکثیر یعنی تغییر ثبت‌شده هنوز به بعضی نسخه‌ها نرسیده است. برنامه باید بداند کدام خواندن‌ها می‌توانند کمی قدیمی باشند و کدام‌ها نیاز به تضمین بیشتری دارند.

مثال فنی: ویرایش دیده نمی‌شود

وضعیت اولیه: کاربر نامش را به «سارا» تغییر داده و پاسخ موفق گرفته است.

اجرای مرحله‌به‌مرحله: خواندن بعدی به followerِ قبل از تغییر می‌رود و نام قبلی را می‌آورد.

نتیجه و دلیل: برای این مسیر read-your-writes لازم است؛ نمایش مقدار قدیمی در دستگاه کاربر الزاماً به‌معنی از بین رفتن نوشتن نیست.

Reading from followers (read scaling) only makes sense with asynchronous replication — otherwise one small failure halts everything. But an async follower may be stale; hence the name eventual consistency: if you stop writing and wait, followers eventually catch up with the leader.

«eventually» یعنی چقدر؟“Eventually” means how long?

عمداً مبهم است: حد بالایی ندارد. در حالتِ عادی lag چند دهمِ ثانیه است، اما نزدیکِ ظرفیتِ سیستم یا هنگامِ مشکلِ شبکه به چند ثانیه یا چند دقیقه می‌رسد. این فقط مسئلهٔ NoSQL نیست؛ followerهای ناهمزمانِ یک پایگاه دادهِ رابطه‌ای هم همین‌اند.

Deliberately vague: there is no upper bound. Normally lag is a fraction of a second, but near system capacity or during network trouble it reaches seconds or minutes. This is not just a NoSQL issue; async followers of a relational database are the same.

۳.۱ سه ناهنجاریِ معروف3.1 Three famous anomalies

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

مثال فنی: سه شکلِ عقب‌ماندگی

وضعیت اولیه: A تغییر نام را ثبت کرده؛ B تا موقعیت ۹۰ و C تا ۱۰۰ پیش رفته‌اند.

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

نتیجه و دلیل: read-your-writes، monotonic reads و consistent prefix هرکدام ناهنجاری متفاوتی را محدود می‌کنند.

ناهنجاریقصهتضمینِ لازم
خواندنِ نوشتهٔ خودت کاربر پروفایلش را به‌روز می‌کند و بلافاصله صفحه را refresh می‌کند؛ مقدارِ قدیمی را می‌بیند و فکر می‌کند داده‌اش گم شده read-after-write / read-your-writes: همیشه نوشتهٔ خودت را ببین (قولی به بقیهٔ کاربرها نمی‌دهد)
عقب‌گردِ زمان کاربر دو بار یک query را می‌زند؛ بار اول comment تازه را می‌بیند، بار دوم (روی follower کندتر) ناپدید می‌شود monotonic reads: بعد از دیدنِ دادهٔ جدیدتر، هرگز دادهٔ قدیمی‌تر نبینی
جواب قبل از سوال در یک گفتگو، پاسخِ خانم از replica سریع‌تر و پرسشِ آقا از replica کندتر می‌رسد؛ شنونده گیج می‌شود consistent prefix reads: نوشته‌هایی که از نظرِ علّی به هم وصل‌اند، با همان ترتیب دیده شوند
AnomalyStoryGuarantee needed
Reading your own writesA user updates their profile and immediately refreshes; they see the old value and think their data is lostread-after-write / read-your-writes: always see your own writes (promises other users nothing)
Time moving backwardA user runs one query twice; first they see a fresh comment, then (on a slower follower) it disappearsmonotonic reads: after seeing newer data, never see older data
Answer before questionIn a conversation, her reply arrives from a faster replica and his question from a slower one; the listener is confusedconsistent prefix reads: causally connected writes are seen in the same order

۳.۲ نمودار ۳ + روایت مدرس3.2 Diagram 3 + teacher narration

راه‌حل‌های هر ناهنجاری را می‌توان مثل یک تصمیم‌گیریِ گام‌به‌گام دید: کدام ناهنجاری برای کاربرَت مهم است و کدام تضمین را باید بخری؟

Each anomaly's remedies can be seen as a step-by-step decision: which anomaly matters to your users, and which guarantee must you buy?

نمودار ۳ · Workflow — replication lag و انتخابِ تضمینِ درستDiagram 3 · Workflow — replication lag and choosing the right guarantee باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۳Teacher narration of diagram 3

مثال فنی برای خواندنِ نمودار: سه شکلِ عقب‌ماندگی

وضعیت اولیه: A تغییر نام را ثبت کرده؛ B تا موقعیت ۹۰ و C تا ۱۰۰ پیش رفته‌اند.

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

نتیجه و دلیل: read-your-writes، monotonic reads و consistent prefix هرکدام ناهنجاری متفاوتی را محدود می‌کنند.

نمودار چهار خط (lane) افقی دارد. خطِ بالا، خطِ تصمیم است و از چپ به راست جلو می‌رود؛ سه خطِ پایین، خانهٔ سه «تضمین» هستند. شروع در چپ‌ترین گره است: «Async Follower Reads» (زیرنویس: «replication lag grows») و برچسبِ «eventual consistency».

اولین تصمیم: «Own Update Disappears?» (زیرنویس: «write, then read»). اگر پاسخ «آری» است، فلشِ عمودیِ «yes» تو را به خطِ دوم می‌برد، به گرهِ «Read-Your-Writes» با زیرنویسِ «own data from leader» و برچسبِ «or track last write time». اگر «خیر»، فلشِ افقیِ «no» به تصمیمِ بعدی می‌رسد.

دومین تصمیم: «Time Moves Backward?» (زیرنویس: «two reads, two replicas»). پاسخِ «آری» یعنی به «Monotonic Reads» در خطِ سوم نیاز داری — با زیرنویسِ «same replica per user» و برچسبِ «hash of the user ID».

سومین تصمیم: «Reply Before Question?» (زیرنویس: «causal order broken»). «آری» یعنی «Consistent Prefix» در خطِ چهارم: زیرنویسِ «causally related writes» و برچسبِ «same shard, or track deps». و اگر هیچ‌کدام مهم نبود، فلشِ آخر («no») می‌رسد به گرهِ پایانیِ «Live With the Lag» — ارزان‌ترین گزینه، با همان eventual consistency.

درسِ شکل: این تضمین‌ها «خوب/بد» مطلق نیستند؛ هرکدام قیمتی دارند و فقط باید همانی را بخری که تجربهٔ کاربر لازم دارد. و اگر هیچ‌کدام کافی نبود، راهِ نهایی فرار از پیچیدگیِ اپلیکیشن است: پایگاه‌داده‌ای با تضمینِ قوی‌تر (مثل linearizable + ACID) انتخاب کن.

The diagram has four horizontal lanes. The top lane is the decision lane running left to right; the three lanes below are the homes of three “guarantees”. The start is the leftmost node: “Async Follower Reads” (caption: “replication lag grows”) labelled “eventual consistency”.

First decision: “Own Update Disappears?” (caption: “write, then read”). If “yes”, the vertical “yes” arrow takes you to the second lane, the “Read-Your-Writes” node captioned “own data from leader” and labelled “or track last write time”. If “no”, the horizontal “no” arrow reaches the next decision.

Second decision: “Time Moves Backward?” (caption: “two reads, two replicas”). A “yes” means you need “Monotonic Reads” on the third lane — captioned “same replica per user”, labelled “hash of the user ID”.

Third decision: “Reply Before Question?” (caption: “causal order broken”). “Yes” means “Consistent Prefix” on the fourth lane: captioned “causally related writes”, labelled “same shard, or track deps”. And if none mattered, the last (“no”) arrow reaches the terminal node “Live With the Lag” — the cheapest option, with the same eventual consistency.

The diagram's lesson: these guarantees are not absolutely “good/bad”; each has a price, and you should buy only what the user experience needs. And if none suffices, the final escape from application complexity is a database with stronger guarantees (like linearizable + ACID).

راه‌های عملیِ read-your-writesPractical read-your-writes paths

برای دیدنِ نوشتهٔ خود می‌توان خواندنِ مرتبط را به رهبر یا نسخه‌ای رساند که تغییر را دریافت کرده است. صرفِ انتظارِ یک مدتِ ثابت، تضمینِ کامل نیست.

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

وضعیت اولیه: پاسخِ ویرایش پروفایل، موقعیت نوشتن LSN=105 را برمی‌گرداند.

اجرای مرحله‌به‌مرحله: خواندن بعدی فقط به replica رسیده به ۱۰۵ می‌رود، یا منتظر آن می‌ماند، یا به رهبر برمی‌گردد.

نتیجه و دلیل: انتخاب replica بر اساس سنجهٔ پیشرفت، تضمین روشن‌تری از «دو ثانیه صبر کن» دارد.

  • خواندنِ چیزهایی که ممکن است کاربر تغییر داده باشد، از leader (یا follower هم‌زمان) — مثلاً همیشه پروفایلِ خودِ کاربر از leader.
  • مدتِ کوتاهی (مثلاً یک دقیقه) بعد از آخرین نوشته، همه‌چیز از leader خوانده شود؛ یا followerهایی که بیش از حد عقب‌اند از مسیرِ خواندن حذف شوند.
  • کلاینت زمانِ آخرین نوشته‌اش را نگه دارد و سیستم تضمین کند هر replica پاسخ‌دهنده، حداقل تا آن لحظه به‌روز باشد (timestamp منطقی یا ساعتِ سیستم — که خودش فصل ۹ را می‌طلبد).
  • Read things the user may have changed from the leader (or a synchronous follower) — e.g. always read the user's own profile from the leader.
  • For a short while after the last write (e.g. one minute), read everything from the leader; or drop followers that lag too far behind from the read path.
  • The client remembers its last write's timestamp, and the system guarantees every answering replica is at least that fresh (a logical timestamp or the system clock — which itself demands Chapter 9).

چند پیچیدگیِ دیگرA few more complications

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

مثال فنی: دو دستگاه و دو مسیر

وضعیت اولیه: کاربر روی موبایل نام را تغییر داده ولی لپ‌تاپ هیچ tokenِ موقعیت نوشتن ندارد.

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

نتیجه و دلیل: تضمین session با تضمین همهٔ دستگاه‌های یک حساب برابر نیست؛ دامنهٔ وعده باید روشن باشد.

  • چند دستگاهی: اگر کاربر هم از دسکتاپ و هم موبایل وصل شود، metadata «آخرین نوشته» باید مرکزی باشد و مسیریابیِ همهٔ دستگاه‌ها به regionِ leader سخت‌تر.
  • monotonic reads: ساده‌ترین راه، چسباندنِ هر کاربر به یک replica مشخص (hash شناسهٔ کاربر) است؛ اگر آن replica بیفتد، باید مسیر را عوض کنی.
  • consistent prefix: در پایگاه‌های داده shardشده چون هر shard مستقل است ترتیبِ جهانی نداری؛ راه‌حل، یک‌جا نوشتنِ نوشته‌های علّی یا نگه‌داشتنِ صریحِ وابستگی‌های علّی است.
  • Multi-device: if the user connects from both desktop and phone, the “last write” metadata must be central, and routing all devices to the leader's region is harder.
  • Monotonic reads: the simplest path is pinning each user to a fixed replica (hash of the user ID); if that replica fails, the route must change.
  • Consistent prefix: in sharded databases each shard acts independently so there is no global order; the fix is writing causally related writes in one place or tracking causal dependencies explicitly.
راهِ ساده‌تر: تضمینِ قوی‌تر بخرThe simpler path: buy a stronger guarantee

اگر رفتارِ برنامه در برابرِ lagِ چنددقیقه‌ای «مهم نیست»، عالی. اگر مهم است، یا در کدِ اپلیکیشن تضمین را پیاده کن (پیچیده و پرخطا) یا پایگاه‌داده‌ای انتخاب کن که خودش linearizability و تراکنشِ ACID بدهد (مسیرِ NewSQL). وانمود کردن به «هم‌زمان بودنِ» تکثیرِ ناهمزمان، دستورِ بروزِ مشکل در آینده است.

If application behavior facing minutes of lag “doesn't matter”, great. If it matters, either implement the guarantee in application code (complex and error-prone) or pick a database that itself offers linearizability and ACID transactions (the NewSQL path). Pretending async replication is “synchronous” is a recipe for future trouble.

۴4 تکثیر چندرهبر و تعارض نوشته‌هاMulti-leader replication and write conflicts

در تکثیر چندرهبر، بیش از یک گره نوشتن می‌پذیرد. این استقلال برای کار آفلاین یا چند منطقه مفید است، اما تغییراتِ هم‌زمان می‌توانند تعارض داشته باشند.

مثال فنی: دو رهبر، یک کلید

وضعیت اولیه: مرکز A برای P7 مقدار ۱۲۰ و مرکز B هم‌زمان ۱۳۰ را محلی می‌پذیرند.

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

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

Single-leader's big weakness: all writes must pass through one node. If your connection to the leader drops, you cannot write. In multi-leader replication (active/active) several nodes accept writes and each ships its changes to the rest — though in practice only asynchronous multi-leader is meaningful; synchronous multi-leader is effectively single-leader.

۴.۱ کجا معنا دارد؟ توپولوژی‌ها4.1 Where does it make sense? Topologies

  • توزیعِ جغرافیایی: در هر region یک leader؛ نوشتنِ محلی و تکثیرِ ناهمزمانِ بین‌regionی. تأخیرِ نوشتن از چشمِ کاربر پنهان می‌ماند، هر region مستقل کار می‌کند و قطعیِ لینکِ بین‌regionی کمتر آسیب می‌زند. بها: سازگاریِ ضعیف‌تر — دیگر نمی‌توانی قیدِ یکتاییِ username یا منفی نشدنِ موجودی را تضمین کنی (هر leader جداگانه قبول می‌کند، مجموع نقض می‌شود).
  • سرویس‌های offline-first و local-first: هر دستگاه (یا هر tab مرورگر در همکاریِ بلادرنگ) یک leader است که حتی بدونِ اینترنت می‌نویسد و بعداً sync می‌کند. به کتابخانه‌ای که این هماهنگ‌سازی را انجام می‌دهد sync engine می‌گویند (مثل PouchDB/CouchDB، Automerge، Yjs).
  • توپولوژیِ تکثیر: مسیرِ انتشارِ نوشته‌ها بینِ leaderها. circular (حلقه‌ای)، star (ریشه‌ای) یا all-to-all (کامل). در حلقه‌ای و star، یک node خراب می‌تواند جریانِ تکثیر را قطع کند؛ در all-to-all مسیرِ جایگزین هست اما پیام‌ها ممکن است از راه‌های مختلف با ترتیبِ متفاوت برسند.
  • Geographic distribution: one leader per region; local writes and async cross-region replication. Write latency stays hidden from users, each region works independently, and a cross-region link outage hurts less. Price: weaker consistency — you can no longer guarantee a username-uniqueness constraint or a never-negative balance (each leader accepts separately; the total violates).
  • Offline-first and local-first services: every device (or every browser tab in realtime collaboration) is a leader that writes even without internet and syncs later. The library performing that coordination is called a sync engine (e.g. PouchDB/CouchDB, Automerge, Yjs).
  • Replication topology: the path writes travel between leaders. Circular, star or all-to-all. In circular and star, one failed node can cut the replication flow; in all-to-all there are alternate paths but messages may arrive via different routes in different orders.
خطرِ ترتیب در all-to-allOrdering danger in all-to-all

توپولوژی تعیین می‌کند تغییرها از چه مسیرهایی میان رهبرها حرکت کنند. وجود چند مسیر، به‌معنی رسیدن تغییرهای وابسته به ترتیب درست نیست.

مثال فنی: توپولوژی و مسیرهای تکراری

وضعیت اولیه: رهبرهای A، B و C تغییرها را به یکدیگر می‌فرستند.

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

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

Client A inserts a row on leader 1 and client B updates that same row on leader 3; leader 2 may receive the “update” before the “insert” — an update to a row that doesn't exist yet! That is the causality problem, and raw timestamps don't fix it (clocks aren't synchronized enough); the cure is the version vector of section 4.3.

۴.۲ حلِ تعارض + نمودار ۴4.2 Conflict resolution + diagram 4

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

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

وضعیت اولیه: دستگاه A به سبد خالی «کتاب» و دستگاه B هم‌زمان «قلم» اضافه می‌کند.

اجرای مرحله‌به‌مرحله: انتخاب صرفاً یکی از دو نسخه یک قلم را گم می‌کند؛ union مجموعه می‌تواند هر دو را حفظ کند، اما حذفِ هم‌زمان semantics مشخص می‌خواهد.

نتیجه و دلیل: حل تعارض وابسته به نوع داده و معنای عملیات است؛ یک سیاست LWW برای همهٔ کاربردها مناسب نیست.

Multi-leader's biggest issue — in geo databases and in on-device sync engines alike — is that concurrent writes on two leaders conflict. The classic example: two users simultaneously change one wiki page's title from A to B and from A to C.

نمودار ۴ · Dataflow — تشخیص و حلِ تعارضِ نوشته‌های هم‌زمانDiagram 4 · Dataflow — detecting and resolving concurrent writes باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۴Teacher narration of diagram 4

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

وضعیت اولیه: دستگاه A به سبد خالی «کتاب» و دستگاه B هم‌زمان «قلم» اضافه می‌کند.

اجرای مرحله‌به‌مرحله: انتخاب صرفاً یکی از دو نسخه یک قلم را گم می‌کند؛ union مجموعه می‌تواند هر دو را حفظ کند، اما حذفِ هم‌زمان semantics مشخص می‌خواهد.

نتیجه و دلیل: حل تعارض وابسته به نوع داده و معنای عملیات است؛ یک سیاست LWW برای همهٔ کاربردها مناسب نیست.

نمودار دو خطِ موازی در چهار مرحله دارد. مرحلهٔ اول، «Concurrent Writes»: بالا گرهِ «Leader 1: A to B» (زیرنویس: «did not see the other»)، پایین «Leader 2: A to C» (زیرنویس: «written while offline»). برچسبِ گرهِ بالا هشدار می‌دهد: «یک رکورد، دو leader». نوشته‌ها «هم‌زمان»‌اند، چون هیچ‌کدام از دیگری خبر نداشته.

مرحلهٔ دوم، «Causality»: فلشِ پررنگِ «write tagged with causal metadata» از Leader 1 به گرهِ «happens-before» می‌رود (زیرنویس: «does one build on it?»، برچسبِ «concurrent if neither»). در خطِ پایین، فلشِ پررنگِ «arrival order differs per replica» از Leader 2 به گرهِ «Version Vector» می‌رود (زیرنویس: «counter per replica»، برچسبِ «dotted in Riak 2.0»). و فلشِ عمودیِ وسط با برچسبِ «compare version vectors» این دو گره را به هم می‌زند: فرادادهٔ علّیِ نوشته‌ها مقایسه می‌شود تا معلوم شود هر مقدار بازنویسی است یا تعارض.

مرحلهٔ سوم، «Resolution» — دو راهِ اصلی: از بالا فلشِ «conflict: discard one» به گرهِ «Last Write Wins» (زیرنویس: «greatest timestamp»، برچسبِ هشدارِ «one write is lost») می‌رود؛ از پایین فلشِ پررنگِ «conflict: keep both» به گرهِ «Siblings» (زیرنویس: «all values kept»، برچسبِ «CouchDB-style»).

مرحلهٔ چهارم، «Convergence»: فلشِ «one winner, one lost write» از LWW به گرهِ «Converged State» (زیرنویس: «same state everywhere») می‌رسد؛ اما مسیرِ پایین کامل‌تر است: فلشِ پررنگِ «merge the values» به گرهِ «CRDT / OT Merge» (زیرنویس: «keeps every edit») و بعد فلشِ عمودیِ «converge without loss» از آن بالا می‌آید و به همان Converged State می‌پیوندد.

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

The diagram has two parallel lines in four stages. Stage one, “Concurrent Writes”: above, the “Leader 1: A to B” node (caption: “did not see the other”); below, “Leader 2: A to C” (caption: “written while offline”). The upper node's label warns: “one record, two leaders”. The writes are “concurrent” because neither knew of the other.

Stage two, “Causality”: the bold “write tagged with causal metadata” arrow runs from Leader 1 to the “happens-before” node (caption: “does one build on it?”, label: “concurrent if neither”). On the lower line, the bold “arrival order differs per replica” arrow runs from Leader 2 to the “Version Vector” node (caption: “counter per replica”, label: “dotted in Riak 2.0”). And the middle vertical arrow labelled “compare version vectors” joins the two nodes: the writes' causal metadata is compared to tell whether each value is an overwrite or a conflict.

Stage three, “Resolution” — two main paths: from above, the “conflict: discard one” arrow to the “Last Write Wins” node (caption: “greatest timestamp”, warning label: “one write is lost”); from below, the bold “conflict: keep both” arrow to the “Siblings” node (caption: “all values kept”, label: “CouchDB-style”).

Stage four, “Convergence”: the “one winner, one lost write” arrow runs from LWW to the “Converged State” node (caption: “same state everywhere”); but the lower path is more complete: the bold “merge the values” arrow to the “CRDT / OT Merge” node (caption: “keeps every edit”), then the vertical “converge without loss” arrow joins it up to that same Converged State.

The diagram's lesson: convergence can be reached “by giving information” (losing no edits) or “by discarding” (LWW). LWW is simple but effectively picks one concurrent write's winner at random; if losing data is unacceptable, keep siblings and merge them with a merge algorithm (CRDT or OT).

راهبردایدهقیمت
Conflict avoidance همهٔ نوشته‌های یک رکورد از یک leader بگذرد (مثلاً هر کاربر «منطقهٔ خانگی» داشته باشد) یا شمارندهٔ IDهای زوج/فرد بین leaderها تقسیم شود با تغییرِ leaderِ مجاز (خرابیِ region، جابه‌جاییِ کاربر) می‌شکند؛ برای sync engineِ آفلاین ممکن نیست
Last Write Wins بزرگ‌ترین timestamp برنده است؛ مقادیرِ هم‌زمان عملاً تصادفی حذف می‌شوند از دست رفتنِ بی‌صدای نوشته‌ها؛ وابستگیِ خطرناک به ساعتِ واقعی (ساعتِ جلوتر = پیروزیِ ناحق). راه‌حلِ ساعت: logical clock
Manual (siblings) همهٔ مقادیرِ هم‌زمان (siblings) نگه داشته و هنگامِ خواندن برگردانده شوند؛ ادغام دستی یا با کدِ اپلیکیشن API پایگاه داده عوض می‌شود (مقدار، مجموعه می‌شود)؛ ادغامِ دستی برای کاربر خسته‌کننده است؛ ادغامِ خودکارِ نسنجیده می‌تواند خودش تعارضِ تازه بسازد
Automatic (CRDT / OT) الگوریتمِ ادغامی که نوشته‌های هم‌زمان را به وضعیتِ سازگار می‌رساند و همهٔ replicaها را همگرا می‌کند پیاده‌سازیِ سخت‌تر؛ بعضی قیدها (مثل «حداکثر ۵ آیتم») ذاتاً با هم‌زمانی ناسازگارند
StrategyIdeaPrice
Conflict avoidanceRoute all writes of one record through one leader (e.g. each user has a “home region”) or split ID counters odd/even between leadersBreaks when the allowed leader changes (region failure, user moves); impossible for offline sync engines
Last Write WinsThe greatest timestamp wins; concurrent values are silently droppedSilent write loss; dangerous dependence on wall clocks (a fast clock = an unfair win). Clock fix: logical clocks
Manual (siblings)Keep all concurrent values (siblings) and return them on read; merge manually or in application codeThe DB API changes (a value becomes a set); manual merging tires users; a careless automatic merge can itself create fresh conflicts
Automatic (CRDT / OT)A merge algorithm bringing concurrent writes to a consistent state and converging all replicasHarder to implement; some constraints (like “at most 5 items”) are inherently incompatible with concurrency
مثالِ معروف: سبد خریدِ آمازونFamous example: Amazon's shopping cart

سبدِ خریدِ هم‌زمان را با «اجتماعِ مجموعه‌ها» ادغام می‌کردند: اگر کاربر در یک دستگاه کتاب را حذف کرده بود و در نسخهٔ دیگر هنوز بود، کتابِ حذف‌شده دوباره سر از سبد درمی‌آورد. درس: ادغامِ درست باید حذف‌ها را هم ردیابی کند، نه فقط اضافه‌شدن‌ها را.

Concurrent carts were merged by “set union”: if the user had removed a book on one device while it still existed in the other copy, the removed book reappeared in the cart. The lesson: a correct merge must track deletions too, not just additions.

  • CRDT (conflict-free replicated datatype): به هر کاراکتر/آیتم شناسه‌ای ثابت می‌دهد و جایِ درج/حذف را با شناسه تعیین می‌کند؛ دو درجِ هم‌زمان در یک جایگاه با ترتیبِ قطعیِ شناسه‌ها مرتب می‌شوند و همهٔ replicaها بدونِ «تبدیل» به هم می‌رسند.
  • OT (operational transformation): شاخصِ (index) عملیات‌ها را با توجه به عملیاتِ هم‌زمانِ اعمال‌شده تبدیل می‌کند؛ مثالِ کلاسیک: «ice» که یکی «n» را در ایندکس ۰ و دیگری «!» را در ایندکس ۳ درج می‌کند؛ بدونِ تبدیل، «nic!e» غلط ساخته می‌شود و با تبدیلِ ایندکس، «nice!» درست به دست می‌آید.
  • هر دو برای متن، لیست، شمارنده و map قواعدِ ادغامِ مخصوص دارند؛ ترکیبِ مزایای هر دو هم ممکن است. OT در ویرایشگرهای بلادرنگ (مثل Google Docs) و CRDT در پایگاه‌های داده توزیع‌شده و sync engineهای متن‌باز (Automerge، Yjs) رایج‌ترند.
  • نوعِ تعارض می‌تواند ظریف باشد: در سیستمِ رزروِ اتاقِ جلسات، تعارض روی «فیلد» نیست؛ دو رزروِ هم‌زمان برای یک اتاق در یک بازه، حتی اگر هر دو قبل از ثبت «اتاق خالی» را دیده باشند، قیدِ یکتایی را می‌شکنند.
  • CRDT (conflict-free replicated datatype): gives every character/item a fixed ID and determines insert/delete position by ID; two concurrent inserts at one position order deterministically by ID, and all replicas converge with no “transformation”.
  • OT (operational transformation): transforms the operations' index against concurrently applied operations; the classic example: “ice” where one inserts “n” at index 0 and another “!” at index 3; without transformation the wrong “nic!e” is built, and with index transformation the correct “nice!” results.
  • Both have dedicated merge rules for text, lists, counters and maps; combining each one's advantages is also possible. OT is more common in realtime editors (like Google Docs), CRDT in distributed databases and open-source sync engines (Automerge, Yjs).
  • The conflict type can be subtle: in a meeting-room booking system the conflict is not on a “field”; two concurrent bookings of one room for one slot break the uniqueness constraint even though both saw “the room is free” before registering.

۴.۳ تشخیصِ هم‌زمانی: happens-before و version vector4.3 Detecting concurrency: happens-before and version vectors

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

مثال فنی: تشخیصِ concurrent با نسخه

وضعیت اولیه: دو نسخه بردارهای {A:2,B:1} و {A:1,B:2} دارند.

اجرای مرحله‌به‌مرحله: هیچ‌کدام در همهٔ مؤلفه‌ها دیگری را پوشش نمی‌دهد؛ دو تغییر concurrent‌اند. نسخهٔ {A:2,B:2} می‌تواند پس از ادغام هر دو ساخته شود.

نتیجه و دلیل: این مقایسه وابستگی علّی را می‌گوید، نه اینکه ساعت دیواری کدام ماشین جلوتر است.

With two concurrent writes, we must tell “conflict” apart from “allowed overwrite”. The conceptual tool is the happens-before relation: write B is “based on” write A if B was made after seeing A; if neither saw the other, the two are concurrent and their result is a conflict. Raw timestamps are insufficient for that detection, because machine clocks aren't synchronized enough (see Chapter 9).

راه‌حلِ عملی، version vector است: به‌جای یک شماره‌ی نسخه، به‌ازای هر کلید و به‌ازای هر replica یک شمارنده داریم. هر replica هنگامِ نوشتن، شمارنده‌ی خودش را جلو می‌برد و شمارنده‌هایی را که تا آن لحظه دیده نگه می‌دارد؛ خواندن، همین بردار را با مقدار برمی‌گرداند و نوشتنِ بعدی باید آن را پس بفرستد. حالا سرور می‌تواند تصمیم بگیرد: اگر بردارِ نوشتنِ جدید «زیرِ» بردارِ موجود باشد یعنی جایگزینیِ مجاز است (overwrite)؛ اگر نه، دو مقدار هم‌زمان‌اند و باید sibling نگه داشته شوند تا با قواعدِ بخشِ قبل ادغام شوند.

The practical cure is the version vector: instead of one version number, one counter per key per replica. Each replica advances its own counter on write and keeps the counters it has seen so far; a read returns that vector with the value, and the next write must send it back. Now the server can decide: if the new write's vector is “under” the existing vector it is an allowed replacement (overwrite); otherwise the two values are concurrent and must be kept as siblings to merge with the previous section's rules.

نکته‌های ظریفSubtle points
  • اندازه‌ی بردار: بردار باید به‌ازای هر replica ورودی داشته باشد؛ در خوشه‌هایی که replica می‌آید و می‌رود، Riak از dotted version vector استفاده می‌کند (آن را causal context می‌نامد).
  • بازگرداندنِ بردار: اگر کلاینت هنگامِ نوشتنِ بعدی بردارِ خوانده‌شده را پس نفرستد، سرور نمی‌فهمد نوشتن «بر پایه‌ی» چه چیزی بوده و همه‌چیز را تعارض حساب می‌کند.
  • تفاوت با vector clock: هر دو ساختارِ برداری دارند، اما version vector برای مقایسه‌ی «وضعیتِ replicaها به‌ازای هر کلید» طراحی شده و vector clock برای رویدادها؛ در عمل اغلب به‌جای هم به کار می‌روند.
  • Vector size: the vector needs an entry per replica; in clusters where replicas come and go, Riak uses a dotted version vector (calling it causal context).
  • Returning the vector: if the client doesn't send back the read vector on its next write, the server can't tell what the write was “based on” and counts everything as conflict.
  • Difference from vector clocks: both have vector structure, but the version vector is designed for comparing “replica states per key” and the vector clock for events; in practice each is often used in place of the other.

۵5 تکثیر بدون‌رهبرLeaderless replication

در تکثیر بدون‌رهبر، هماهنگ‌کننده با چند نسخه برای خواندن و نوشتن کار می‌کند. تعداد تأییدها و روش ترمیم تعیین می‌کنند با نسخهٔ غایب یا قدیمی چگونه برخورد شود.

مثال فنی: نوشتن بدون رهبر واحد

وضعیت اولیه: کلید K روی A، B و C نگه داشته می‌شود و کلاینت از دو replica تأیید می‌خواهد.

اجرای مرحله‌به‌مرحله: اگر C قطع باشد، A و B می‌توانند نسخه را بپذیرند؛ خواندن بعدی باید نسخه‌های برگشتی را طبق پروتکل مقایسه کند.

نتیجه و دلیل: صرفِ دریافت دو پاسخ، حل تعارض و تازه‌ترین نسخه را خودکار تضمین نمی‌کند.

In leaderless replication (the Dynamo pattern after Amazon's 2007 paper; Riak, Cassandra, ScyllaDB) there is no leader at all: every replica writes directly from the client. (Important nuance: today's same-named DynamoDB is leader-based!) Sometimes the client writes to several nodes directly, sometimes a coordinator does it — but unlike a leader, it imposes no order.

۵.۱ quorum و بازیابیِ نوشته‌های ازدست‌رفته + نمودار ۵5.1 Quorums and recovering lost writes + diagram 5

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

مثال فنی: هم‌پوشانی مجموعه‌ها

وضعیت اولیه: n=3,w=2,r=2 و نوشتن روی {A,B} موفق شده است.

اجرای مرحله‌به‌مرحله: هر مجموعهٔ خواندنِ دوعضوی از همین سه replica حداقل با یکی از A یا B مشترک است.

نتیجه و دلیل: شرط w+r>n هم‌پوشانی می‌دهد؛ برای خطی‌پذیری هنوز نسخه‌بندی، concurrency و پروتکل خواندن/نوشتن مهم‌اند.

If one of three replicas is unavailable, writes must not stop: the client sends the write to all in parallel, two acknowledge, and that's enough. But the node that returns missed the writes of its absence; so reads must also come from several nodes, and the client tells the freshest value apart by looking at each value's version/timestamp.

نمودار ۵ · Architecture — quorum، read repair و hinted handoffDiagram 5 · Architecture — quorum, read repair and hinted handoff باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نمودار ۵Teacher narration of diagram 5

مثال فنی برای خواندنِ نمودار: هم‌پوشانی مجموعه‌ها

وضعیت اولیه: n=3,w=2,r=2 و نوشتن روی {A,B} موفق شده است.

اجرای مرحله‌به‌مرحله: هر مجموعهٔ خواندنِ دوعضوی از همین سه replica حداقل با یکی از A یا B مشترک است.

نتیجه و دلیل: شرط w+r>n هم‌پوشانی می‌دهد؛ برای خطی‌پذیری هنوز نسخه‌بندی، concurrency و پروتکل خواندن/نوشتن مهم‌اند.

سمتِ چپ، گرهِ «Client» نشسته (زیرنویس: «reads and writes in parallel»)؛ سمتِ راست، داخلِ یک قابِ بزرگ با عنوانِ «n = 3 replicas · w = 2 · r = 2 · w + r > n»، سه replica عمودی: «Replica 1» و «Replica 2» (هر دو زیرنویسِ «up to date» و برچسبِ «version 7») و پایین‌تر «Replica 3» با زیرنویسِ «was unavailable» و برچسبِ «missed the write».

سه فلش از Client بیرون می‌آید: پررنگِ «write to all replicas» به Replica 1، معمولیِ «read from r replicas» به Replica 2، و چین‌چینِ «read repair: newest value pushed back» به Replica 3 — یعنی خودِ کلاینت هنگامِ خواندن می‌بیند Replica 3 کهنه است و مقدارِ تازه را به عقب برمی‌گرداند.

دو فلشِ چین‌چینِ دیگر، مکانیزم‌های پس‌زمینه را نشان می‌دهند: «hinted handoff: replay missed writes» از Replica 1 به Replica 3 (نوشته‌های دورانِ غیاب، به‌جایش نگه داشته شده و حالا تحویل می‌شوند) و «anti-entropy: background diff and copy» از Replica 2 به Replica 3 (فرایندِ پس‌زمینه‌ای که تفاوتِ داده‌ها را پیدا و کپی می‌کند، بی‌هیچ ترتیبی).

درسِ شکل: در نبودِ leader، «همه‌چیز درست شود» کارِ یک نفر نیست؛ ترکیبی از quorum در لحظه (w و r) و مکانیزم‌های ترمیمِ تنبل (read repair، hinted handoff، anti-entropy) سیستم را بالاخره همگرا می‌کند.

On the left sits the “Client” node (caption: “reads and writes in parallel”); on the right, inside a large frame titled “n = 3 replicas · w = 2 · r = 2 · w + r > n”, three vertical replicas: “Replica 1” and “Replica 2” (both captioned “up to date” and labelled “version 7”) and below them “Replica 3” captioned “was unavailable” and labelled “missed the write”.

Three arrows leave the Client: the bold “write to all replicas” to Replica 1, the plain “read from r replicas” to Replica 2, and the dashed “read repair: newest value pushed back” to Replica 3 — the client itself notices Replica 3 is stale while reading and pushes the fresh value back.

Two more dashed arrows show the background mechanisms: “hinted handoff: replay missed writes” from Replica 1 to Replica 3 (absence-era writes, held in its place and now delivered) and “anti-entropy: background diff and copy” from Replica 2 to Replica 3 (a background process finding data differences and copying them, with no order at all).

The diagram's lesson: with no leader, “making everything right” is no one person's job; a mix of instant quorum (w and r) and lazy repair mechanisms (read repair, hinted handoff, anti-entropy) eventually converges the system.

ریاضیِ quorumQuorum math

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

مثال فنی: یک پاسخ کافی نیست

وضعیت اولیه: A و B مقدار v2 دارند و C هنوز v1 دارد.

اجرای مرحله‌به‌مرحله: خواندن فقط از C می‌تواند قدیمی برگردد؛ خواندن A و C هر دو نسخه را می‌بیند و باید تازه‌تر را تشخیص دهد.

نتیجه و دلیل: بزرگ‌تر کردن r هزینه و شانسِ دیدن نسخهٔ جدید را تغییر می‌دهد؛ timestamp خراب یا نوشتن concurrent با ریاضی quorum حل نمی‌شود.

  • n نسخه از هر مقدار، نوشتن با w تأیید موفق، خواندن از r node؛ شرط: w + r > n.
  • انتخابِ رایج: n فرد (۳ یا ۵) و w = r = ⌈(n+1)/2⌉؛ اما quorum الزاماً «اکثریت» نیست — مهم، هم‌پوشانیِ مجموعه‌هاست.
  • بارِ خواندنیِ سنگین: w = n, r = 1 (خواندنِ سریع، اما یک خرابی همهٔ نوشتن‌ها را می‌بندد).
  • اگر nodeهای پاسخ‌دهنده کمتر از w یا r بودند، عملیات error می‌دهد؛ نوعِ خرابی مهم نیست، مهم «پاسخِ موفق» است.
  • n copies of each value, a write succeeds with w acks, reads query r nodes; condition: w + r > n.
  • Common pick: odd n (3 or 5) with w = r = ⌈(n+1)/2⌉; but a quorum is not necessarily a “majority” — what matters is set overlap.
  • Heavy read load: w = n, r = 1 (fast reads, but one failure closes all writes).
  • If fewer nodes respond than w or r, the operation errors; the failure type doesn't matter, what matters is a “successful response”.

سه مکانیزمِ ترمیمThree repair mechanisms

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

مثال فنی: ترمیمِ replica جا‌مانده

وضعیت اولیه: C هنگام نوشتن قطع بوده و A موقتاً hint آن را نگه داشته است.

اجرای مرحله‌به‌مرحله: hinted handoff بعداً تغییر را می‌رساند؛ read repair اختلاف در خواندن را اصلاح می‌کند؛ anti-entropy دوره‌ای کلیدهای خوانده‌نشده را هم مقایسه می‌کند.

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

  • Read repair: کلاینت هنگامِ خواندنِ موازی، پاسخِ کهنه را می‌فهمد و مقدارِ تازه را به همان node برمی‌گرداند — برای کلیدهای پرخواننده عالی.
  • Hinted handoff: اگر nodeی در دسترس نبود، node دیگری «hint» نوشته‌ها را به‌جایش نگه می‌دارد و بعد تحویل می‌دهد.
  • Anti-entropy: فرایندِ پس‌زمینه که تفاوتِ replicaها را پیدا و کپی می‌کند؛ برخلافِ لاگِ تکثیر، ترتیبی ندارد و ممکن است دیر انجام شود.
  • Read repair: during a parallel read the client spots a stale answer and writes the fresh value back to that node — excellent for hot keys.
  • Hinted handoff: if a node is unreachable, another node keeps its writes as “hints” and delivers them later.
  • Anti-entropy: a background process finding replica differences and copying them; unlike the replication log it has no order and may run late.

۵.۲ محدودیت‌های quorum و مقایسهٔ عملکرد5.2 Quorum limits and performance comparison

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

مثال فنی: Sloppy quorum و دامِ انتظار

وضعیت اولیه: replicaهای اصلی K برابر A، B و C هستند؛ A و B از دسترس خارج‌اند.

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

نتیجه و دلیل: شرط عددی quorum روی اعضای جایگزین به‌تنهایی همان تضمین مجموعهٔ ثابت را نمی‌دهد.

حتی با w + r > n هم تضمینِ مطلق نیستEven with w + r > n there is no absolute guarantee
  • اگر nodeِ حاملِ مقدارِ تازه بیفتد و از replica قدیمی بازسازی شود، تعدادِ نسخه‌های تازه ممکن است از w کمتر شود.
  • هنگامِ rebalancing (فصل ۷)، nodeها تصورِ متفاوتی از «کجا باید n نسخه باشد» دارند و هم‌پوشانیِ quorum می‌شکند.
  • خواندنِ موازی با نوشتنِ در جریان: ممکن است خواندنِ اول تازه و خواندنِ دوم کهنه ببیند.
  • نوشته‌ای که روی بعضی replicaها موفق و روی بعضی ناموفق بوده، rollback نمی‌شود؛ خواندنِ بعدی ممکن است همان «نوشتهٔ ناموفق» را ببیند.
  • اگر ترتیبِ نوشته‌ها با ساعتِ واقعی تعیین شود (مثل Cassandra)، ساعتِ جلوتر می‌تواند نوشته‌های درست را بی‌صدا حذف کند.

پس Dynamo-style پایگاه داده‌ها برای سناریوهای «تحملِ eventual consistency» بهینه‌اند؛ w و r احتمالِ خواندنِ کهنه را تنظیم می‌کنند، نه حذف.

  • If the node holding the fresh value fails and is rebuilt from an old replica, the count of fresh copies may drop below w.
  • During rebalancing (Chapter 7), nodes hold different pictures of “where the n copies should be”, breaking quorum overlap.
  • A read racing a write in flight: the first read may see fresh data and the second stale.
  • A write that succeeded on some replicas and failed on others is not rolled back; a later read may see that same “failed write”.
  • If write order is set by wall-clock time (like Cassandra), a fast clock can silently drop correct writes.

So Dynamo-style databases are optimized for “tolerating eventual consistency” scenarios; w and r tune the stale-read probability, not eliminate it.

  • پایشِ کهنگی: در تک‌رهبر، lag از تفاضلِ موقعیتِ follower و leader در لاگ اندازه‌گیری می‌شود؛ در بدون‌رهبر ترتیبی نیست و اندازه‌گیری سخت است (مثلاً تعداد hintها). «در نهایت» باید برای عملیات قابل‌سنجش شود.
  • خواندن از leader در برابر بدون‌رهبر: خواندن از leader به‌روز است اما ظرفیتش محدود است، هنگامِ failover وقفه می‌افتد و به کندیِ leader حساس است. بدون‌رهبر با «درخواست‌های موازی و استفاده از سریع‌ترین پاسخ‌ها» (request hedging) تأخیرِ دُم را کم می‌کند و در برابرِ gray failure (node زنده ولی کند) مقاوم است، چون اصلاً تفاوتی بین «حالتِ عادی» و «حالتِ خرابی» قائل نیست.
  • بهای بدون‌رهبر: تشخیصِ node ازدست‌رفته و بازپخشِ hintها درست در لحظهٔ فشار، بارِ اضافه می‌سازد؛ quorumهای بزرگ‌تر احتمالِ گیر افتادنِ پشتِ یک node کند را زیاد می‌کنند (در عمل به‌ندرت بیش از ۴ از ۷ یا ۵ از ۹)؛ و قطعیِ شبکه‌ایِ بزرگ می‌تواند اصلاً quorum نسازد — راهِ فرارِ بعضی سیستم‌ها sloppy quorum (یا consistency level ANY) است: هر node در دسترسی بنویسد، به بهای اینکه خواندنِ بعدی شاید آن مقدار را نبیند.
  • چند region: Cassandra/ScyllaDB نوشتن را به coordinatorِ محلی می‌سپارند و او به یک replica در هر region دیگر می‌فرستد؛ سطحِ سازگاری را می‌توان «quorum سراسری» یا «quorum محلی» (سریع‌تر، کهنه‌تر) انتخاب کرد. Riak ارتباطِ کلاینت را محلی نگه می‌دارد و تکثیرِ بین‌regionی را پس‌زمینه و شبیهِ چندرهبر انجام می‌دهد.
  • چندرهبر در برابر بدون‌رهبر: چندرهبر در برابرِ قطعیِ شبکه مقاوم‌تر است (کلاینت فقط با یک leader محلی حرف می‌زند) اما خواندنش می‌تواند دلخواه کهنه باشد؛ quorum حدِ وسط است: تحملِ خرابیِ خوب + احتمالِ بالای خواندنِ به‌روز.
  • Monitoring staleness: in single-leader, lag is measured from the follower-vs-leader log position gap; in leaderless there is no order and measurement is hard (e.g. hint counts). “Eventually” must become measurable for operations.
  • Leader reads vs leaderless: leader reads are fresh but its capacity is bounded, failover pauses them, and they are sensitive to leader slowness. Leaderless, with “parallel requests using the fastest answers” (request hedging), trims tail latency and resists gray failure (a live-but-slow node), because it draws no line between “normal” and “failure” modes at all.
  • Leaderless price: detecting a lost node and replaying hints right at pressure moments adds load; bigger quorums raise the odds of getting stuck behind one slow node (in practice rarely beyond 4-of-7 or 5-of-9); and a large network outage may form no quorum at all — some systems' escape is sloppy quorum (or consistency level ANY): write on any reachable node, at the price that the next read may never see that value.
  • Multi-region: Cassandra/ScyllaDB hand writes to a local coordinator that forwards to one replica in each other region; the consistency level can be “global quorum” or “local quorum” (faster, staler). Riak keeps client traffic local and does cross-region replication in the background, multi-leader-style.
  • Multi-leader vs leaderless: multi-leader resists network outages better (the client talks only to one local leader) but its reads can be arbitrarily stale; quorum is the middle ground: good failure tolerance + high odds of fresh reads.
نقشه فصل · همه مفاهیم تکثیر در یک نگاهChapter map · all of replication at a glance باز کردن در صفحه جدا ↗Open standalone ↗
روایت مدرس از نقشه فصلTeacher narration of the chapter map

مثال فنی برای خواندنِ نمودار: Sloppy quorum و دامِ انتظار

وضعیت اولیه: replicaهای اصلی K برابر A، B و C هستند؛ A و B از دسترس خارج‌اند.

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

نتیجه و دلیل: شرط عددی quorum روی اعضای جایگزین به‌تنهایی همان تضمین مجموعهٔ ثابت را نمی‌دهد.

گره مرکزی (Replication) به شش مفهوم وصل است و برچسب هر یال، ایدهٔ اصلی آن مفهوم را می‌گوید: تک‌رهبر با «یک نویسنده»، چندرهبر با «چند نویسنده»، بدون‌رهبر با w + r > n، تأخیر تکثیر با «حد بالا ندارد»، failover با «تشخیص فقط با timeout» و تعارض‌ها با «ادغام کن، نه حذف».

این نقشه را مثل چک‌لیست مرور بخوان: اگر بتوانی برای هر یال، یک پاراگراف توضیح (با مثال و بهایش) بگویی، فصل را بلدی؛ و اگر یالی ساکتت کرد، همان بخش را دوباره بخوان — بعد سراغ بستهٔ مصاحبه برو.

The central node (Replication) connects to six concepts, and each edge's label states that concept's core idea: single-leader with “one writer”, multi-leader with “many writers”, leaderless with w + r > n, replication lag with “no upper bound”, failover with “timeout-only detection”, and conflicts with “merge, don't drop”.

Read this map as a revision checklist: if you can speak one paragraph (with an example and its price) for each edge, you know the chapter; and whichever edge silences you, re-read that section — then head to the interview pack.

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

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

اصطلاحتعریف یک‌خطیکلیدواژهٔ مصاحبه
Replicationنگه‌داشتنِ نسخهٔ یک داده روی چند ماشینسختی در تغییرات است، نه در کپی
Replicaهر nodeی که نسخه‌ای از پایگاه داده را نگه می‌داردleader / follower
Leader-basedیک رهبر همهٔ نوشته‌ها را می‌پذیرد و به followerها لاگ می‌فرستدprimary-backup، active/passive
Semisynchronousیک follower هم‌زمان، بقیه ناهمزماننسخهٔ به‌روز روی ≥۲ node
Replication Lagفاصلهٔ زمانیِ اعمالِ نوشته بین leader و followerحد بالا ندارد
Eventual Consistencyاگر نوشتن بایستد، replicaها بالاخره همگرا می‌شوند«eventually» عمداً مبهم است
Read-your-writesکاربر همیشه نوشتهٔ خودش را ببیندخواندنِ دادهٔ خودی از leader
Monotonic Readsبعد از دیدنِ جدیدتر، قدیمی‌تر دیده نشودsticky: یک کاربر، یک replica
Consistent Prefixنوشته‌های علّی با همان ترتیب دیده شوندمهم در shard کردن
Failoverارتقای follower به leader بعد از خرابیِ رهبرتشخیص، انتخاب، بازپیکربندی
Split Brainدو node هم‌زمان خود را leader بدانندfencing برای جلوگیری
Catch-up Recoveryبازگشتِ follower و دریافتِ نوشته‌های دورانِ غیابلاگِ محلیِ follower
Statement-based Logتکثیر با فرستادنِ خودِ دستورهاخطرِ توابعِ غیرقطعی
WAL Shippingتکثیر با همان لاگِ فیزیکیِ موتورِ ذخیره‌سازیقفلِ نسخهٔ نرم‌افزار
Logical Replicationلاگِ سطحِ سطر، جدا از موتورِ ذخیره‌سازیپایهٔ change data capture
Multi-leaderچند node هم‌زمان نوشته می‌پذیرندgeo-distributed، offline-first
Sync Engineکتابخانه‌ای برای هماهنگ‌سازیِ state بین دستگاه‌هاlocal-first، offline-first
Last Write Winsبزرگ‌ترین timestamp برنده؛ نوشتهٔ دیگر بی‌صدا حذفبرنده تصادفی است
Siblingsمقادیرِ هم‌زمانِ یک کلید که هنوز ادغام نشده‌اندAPI مقدار را مجموعه‌ای می‌کند
CRDTdatatype با ادغامِ تضمینیِ همگراشناسهٔ ثابت به‌جای ایندکس
Operational Transformationتبدیلِ ایندکسِ عملیات‌های هم‌زمانGoogle Docs-style
Strong Eventual Consistencyeventual + همگراییِ تضمینیِ همهٔ replicaهانتیجهٔ ادغامِ درست
Leaderlessهر replica مستقیم می‌نویسد (Dynamo-style)Riak، Cassandra، ScyllaDB
Quorum (n, w, r)n نسخه، w تأییدِ نوشتن، r node برای خواندنشرطِ w + r > n
Read Repairبرگرداندنِ مقدارِ تازه به replica کهنه هنگامِ خواندنبرای کلیدهای پرخواننده
Hinted Handoffنگه‌داشتنِ نوشته‌های nodeِ غایب و تحویلِ بعدیبازیابیِ nodeِ ازدست‌رفته
Anti-entropyفرایندِ پس‌زمینه برای همگام‌سازیِ تفاوت‌هابدونِ ترتیب
Sloppy Quorumقبولِ نوشتن روی هر node در دسترسCassandra: level ANY
Request Hedgingاستفاده از سریع‌ترین پاسخ‌های موازیکاهشِ tail latency
Happens-beforeB از A خبر دارد/به A وابسته استهم‌زمان = نه A قبل از B، نه برعکس
Version Vectorشمارندهٔ هر replica برای هر کلید؛ تشخیصِ overwrite از تعارضdotted version vector در Riak
TermOne-line definitionInterview keyword
ReplicationKeeping one copy of data on several machinesThe pain is in change, not in copying
ReplicaEach node holding a copy of the databaseleader / follower
Leader-basedOne leader accepts all writes and ships logs to followersprimary-backup, active/passive
SemisynchronousOne follower synchronous, the rest asyncA fresh copy on ≥2 nodes
Replication LagTime gap applying a write between leader and followerHas no upper bound
Eventual ConsistencyIf writes stop, replicas eventually converge“Eventually” is deliberately vague
Read-your-writesA user always sees their own writesRead own data from the leader
Monotonic ReadsAfter seeing newer data, never see olderSticky: one user, one replica
Consistent PrefixCausally ordered writes are seen in the same orderMatters in sharding
FailoverPromoting a follower to leader after the leader failsDetect, elect, reconfigure
Split BrainTwo nodes both believing they leadFencing prevents it
Catch-up RecoveryA returning follower fetching absence-era writesThe follower's local log
Statement-based LogReplicating by shipping the statements themselvesNondeterministic-function risk
WAL ShippingReplicating with the storage engine's physical logLocks the software version
Logical ReplicationRow-level log, decoupled from the storage engineBasis of change data capture
Multi-leaderSeveral nodes accepting writes concurrentlygeo-distributed, offline-first
Sync EngineA library syncing state between deviceslocal-first, offline-first
Last Write WinsGreatest timestamp wins; the other write silently droppedThe winner is random
SiblingsConcurrent values of one key, not yet mergedThe API turns a value into a set
CRDTA datatype with guaranteed-convergent mergingFixed IDs instead of indexes
Operational TransformationTransforming the index of concurrent operationsGoogle Docs-style
Strong Eventual ConsistencyEventual + guaranteed convergence of all replicasResult of correct merging
LeaderlessEvery replica writes directly (Dynamo-style)Riak, Cassandra, ScyllaDB
Quorum (n, w, r)n copies, w write acks, r nodes for readsThe w + r > n condition
Read RepairPushing the fresh value to a stale replica during readsFor hot keys
Hinted HandoffHolding an absent node's writes and delivering laterRecovering a lost node
Anti-entropyA background process syncing differencesUnordered
Sloppy QuorumAccepting writes on any reachable nodeCassandra: level ANY
Request HedgingUsing the fastest of parallel responsesTrimming tail latency
Happens-beforeB knows/depends on AConcurrent = neither before the other
Version VectorPer-replica counters per key; telling overwrite from conflictDotted version vectors in Riak

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

۱. تکثیر چه هدف‌هایی دارد و چرا با بکاپ فرق می‌کند؟1. What goals does replication serve, and how does it differ from backup?

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

مثال فنی: سه کپی با backup فرق دارد

وضعیت اولیه: جدول به سه replica تکثیر می‌شود؛ دستور اشتباه همهٔ سفارش‌ها را حذف می‌کند.

اجرای مرحله‌به‌مرحله: حذف به هر سه replica می‌رسد؛ snapshot قدیمی و لاگِ مناسب امکان بازیابیِ قبل از حذف را می‌دهند.

نتیجه و دلیل: replication بیشتر برای ادامهٔ سرویس است؛ نگهداری تاریخچهٔ بازیابی وظیفهٔ backup است.

High availability, data durability, offline work, lower latency (data near users) and read scalability. Replication ships fresh writes to the rest quickly; a backup keeps an old state to return to the past. Only a backup compensates a mistaken deletion, because deletions replicate too.

۲. سه خانوادهٔ الگوریتمِ تکثیر را در یک جمله مقایسه کن.2. Compare the three replication algorithm families in one sentence each.

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

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

وضعیت اولیه: برای کلید K سه replica داریم و مقدارش باید عوض شود.

اجرای مرحله‌به‌مرحله: تک‌رهبر: درخواست به رهبر می‌رود؛ چندرهبر: دو مرکز می‌توانند محلی بنویسند و بعد تعارض حل کنند؛ بدون‌رهبر: کلاینت طبق پروتکل به چند replica می‌نویسد.

نتیجه و دلیل: تعداد کپی یکسان است، اما محل ترتیب‌دهی، مسیرِ ack و شیوهٔ حل تعارض متفاوت است.

Single-leader: one writer, defined order, relatively strong consistency, but a single write point and painful failover. Multi-leader: local writes and region-outage tolerance, but write conflicts and weaker consistency (global constraints like uniqueness can't be guaranteed). Leaderless: every replica writes, no failover and slow-node resilience, but quorums, stale reads and conflict-detection duty.

۳. تکثیرِ هم‌زمان و ناهمزمان چه تفاوتِ رفتاری دارند؟ semisynchronous چیست؟3. How do synchronous and asynchronous replication behave differently? What is semisynchronous?

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

مثال فنی: زمان تأیید در دو روش

وضعیت اولیه: رهبر تغییر شمارهٔ ۵۲ را روی دیسک خود پایدار کرده؛ follower هنوز آن را نگرفته است.

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

نتیجه و دلیل: خرابیِ رهبر پیش از انتقال می‌تواند نوشتهٔ async را در failover از دست بدهد؛ sync هزینهٔ انتظار و وابستگی دارد.

Synchronous: the leader takes the follower's ack before answering the client — freshness guaranteed, but that follower's failure closes writes. Asynchronous: the leader doesn't wait and stays available, but acknowledged-yet-unreplicated writes are lost if the leader dies. Semisynchronous means just one follower (or a small majority) is synchronous and the rest async; the common balance in practice.

۴. چرا همهٔ followerها را نمی‌توان هم‌زمان کرد؟4. Why can't all followers be synchronous?

می‌توان همه را هم‌زمان کرد، اما هزینهٔ دسترس‌پذیری بالاست: اگر نوشتن تأییدِ همه را بخواهد، کندی یا قطعیِ یکی آن را متوقف می‌کند.

مثال فنی: یک followerِ دوردست در مسیر ack

وضعیت اولیه: رهبر دو follower دارد؛ یکی در ۵ میلی‌ثانیه و دیگری معمولاً در ۱۵۰ میلی‌ثانیه پاسخ می‌دهد.

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

نتیجه و دلیل: synchronous بودن یعنی باید معلوم باشد تأییدِ چه تعداد و چه نوع پایداری لازم است؛ هر انتخاب، دوام و دسترس‌پذیری متفاوتی دارد.

Because any failure of any node would halt all writes, and the more nodes, the likelier one fails. So “everything synchronous” is impractical; we take either one synchronous follower (semisync) or a majority quorum (like 3 of 5).

۵. راه‌اندازیِ follower جدید چه مراحلی دارد و چرا کپیِ ساده کافی نیست؟5. What are the steps of setting up a new follower, and why isn't a plain copy enough?

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

مثال فنی: Snapshot با نقطهٔ لاگ

وضعیت اولیه: هنگام ساخت follower تازه، snapshot در موقعیت 500 گرفته می‌شود.

اجرای مرحله‌به‌مرحله: snapshot کپی می‌شود و follower تغییرهای 501 به بعد را دریافت می‌کند؛ لاگ لازم تا پایان انتقال نگه داشته می‌شود.

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

A plain file copy taken while writes flow gives an inconsistent state. Steps: a consistent snapshot (no full lock), copying it to the new node, requesting post-snapshot changes by the log's “exact position” (like LSN or GTID), and joining the normal stream once the lag is compensated.

۶. failover چه مراحلی دارد و چه چیزهایی ممکن است خراب شود؟6. What are failover's steps, and what can go wrong?

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

مثال فنی: ترفیعِ کپی عقب‌مانده

وضعیت اولیه: رهبر تا موقعیت 105 را به کلاینت تأیید کرده، اما نامزد جدید فقط تا 103 دارد.

اجرای مرحله‌به‌مرحله: با ترفیع آن بدون تضمین لازم، نوشته‌های ۱۰۴ و ۱۰۵ در نمای سرویس ناپدید می‌شوند.

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

Three steps: failure detection (only via timeout — no reliable way), electing a fresh leader (election or controller; usually the freshest replica), and reconfiguring clients plus demoting the old leader to follower. Risks: losing unreplicated writes under async replication, inconsistency with outside systems (like duplicate counters), split brain, and wrong timeout choice.

۷. split brain چیست و fencing چطور کمک می‌کند؟7. What is split brain, and how does fencing help?

Split brain یعنی دو گره نوشتنِ مستقل را به‌عنوان رهبر بپذیرند. Fencing مجوزِ قدیمی را در مقصدِ حفاظت‌شده رد می‌کند؛ اعلامِ رهبرِ تازه به‌تنهایی کافی نیست.

مثال فنی: رهبر قدیمی برمی‌گردد

وضعیت اولیه: A در epoch ۷ رهبر بوده و بعد از قطع شبکه، B در epoch ۸ انتخاب شده است.

اجرای مرحله‌به‌مرحله: مقصد نوشتن epoch قدیمی را رد می‌کند؛ A پس از برگشت نمی‌تواند با مجوز ۷ نوشتن معتبر انجام دهد.

نتیجه و دلیل: تشخیص timeout به‌تنهایی A را متوقف نمی‌کند؛ fencing جلوی اثرِ رهبر منقضی را می‌گیرد.

When two nodes both believe they lead and both accept writes; without a conflict-resolution process, data is lost or corrupted. Fencing is a mechanism (like a token or lease) rejecting the deposed old leader's writes. Though a badly designed “auto-kill a node” mechanism may take down both.

۸. سه روشِ پیاده‌سازیِ لاگِ تکثیر و ضعفِ هرکدام؟8. Three ways to implement the replication log, and each one's weakness?

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

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

وضعیت اولیه: دستور UPDATE ... SET updated_at=NOW() روی رهبر اجرا می‌شود.

اجرای مرحله‌به‌مرحله: تکثیر statement ممکن است در مقصد زمان متفاوت بسازد؛ row-log مقدار نهایی ردیف را حمل می‌کند؛ لاگ فیزیکی به ساختار داخلی موتور وابسته است.

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

Statement-based: shipping statements — breaks on nondeterministic functions and order dependence. WAL shipping: the engine's physical log — tied to the engine and makes zero-downtime upgrades (different versions on leader and follower) impossible. Logical/row-based: row-level records — compatible, analyzable for outside systems (CDC) and fit for gradual upgrades.

۹. eventual consistency دقیقاً چه می‌گوید و چه نمی‌گوید؟9. What does eventual consistency say exactly — and not say?

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

مثال فنی: ویرایش دیده نمی‌شود

وضعیت اولیه: کاربر نامش را به «سارا» تغییر داده و پاسخ موفق گرفته است.

اجرای مرحله‌به‌مرحله: خواندن بعدی به followerِ قبل از تغییر می‌رود و نام قبلی را می‌آورد.

نتیجه و دلیل: برای این مسیر read-your-writes لازم است؛ نمایش مقدار قدیمی در دستگاه کاربر الزاماً به‌معنی از بین رفتن نوشتن نیست.

It says: if writes stop, replicas eventually meet. It doesn't say: how long it takes (no upper bound), and in the meantime reads can be stale and even “illogical” (like an answer with no question). Both async relational databases and NoSQL systems share this property.

۱۰. read-your-writes را با سه روشِ عملی پیاده کن.10. Implement read-your-writes three practical ways.

سه روش: خواندنِ مرتبط از رهبر یا نسخهٔ تأییدشده؛ هدایتِ موقتِ کاربر پس از نوشتن؛ یا همراه داشتنِ نقطهٔ تغییر و انتخابِ دنبال‌کنندهٔ رسیده به آن. تأخیرِ ثابت تضمینِ کامل نیست.

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

وضعیت اولیه: پاسخِ ویرایش پروفایل، موقعیت نوشتن LSN=105 را برمی‌گرداند.

اجرای مرحله‌به‌مرحله: خواندن بعدی فقط به replica رسیده به ۱۰۵ می‌رود، یا منتظر آن می‌ماند، یا به رهبر برمی‌گردد.

نتیجه و دلیل: انتخاب replica بر اساس سنجهٔ پیشرفت، تضمین روشن‌تری از «دو ثانیه صبر کن» دارد.

1) Read the user's changeable data from the leader (or a synchronous follower) and the rest from followers — e.g. the user's own profile always from the leader. 2) For a short window after the last write, read everything from the leader, or drop far-behind followers. 3) The client remembers its last write's timestamp and picks a replica at least that fresh; for multi-device that metadata must go central.

۱۱. monotonic reads چیست و ساده‌ترین راه‌حلش؟11. What are monotonic reads, and the simplest fix?

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

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

وضعیت اولیه: یک کاربر ابتدا از replica A نسخهٔ ۱۰ را می‌خواند و سپس از B نسخهٔ ۸ را.

اجرای مرحله‌به‌مرحله: sticky routing یا token حداقل نسخه، خواندن بعدی را به ۱۰ یا بالاتر محدود می‌کند.

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

The guarantee that after seeing newer data, a user never sees older data in a later read (time never moves backward). Weaker than strong consistency, stronger than eventual. Simplest fix: pinning each user to a fixed replica (e.g. hash of the user ID), with the rerouting problem when that replica fails.

۱۲. consistent prefix reads چیست و چرا در پایگاه‌های داده shardشده مشکل‌ساز است؟12. What are consistent prefix reads, and why are they painful in sharded databases?

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

مثال فنی: اثر پیش از علت

وضعیت اولیه: پیام پرسش روی shard A و پاسخ آن روی shard B ثبت شده است.

اجرای مرحله‌به‌مرحله: اگر B جلوتر تکثیر شود، خواننده پاسخ را بدون پرسش می‌بیند؛ حفظ وابستگی علّی در نمایش یا هم‌مکان کردن داده کمک می‌کند.

نتیجه و دلیل: ترتیبِ درون هر shard برای ترتیبِ علّی بین shardها کافی نیست.

If a sequence of writes had an order, readers see that same order (e.g. a question and its answer never swap). In sharding each shard acts alone so there is no global order; the fix: write causally related writes in one shard or track causal dependencies explicitly.

۱۳. multi-leader چه زمانی واقعاً ارزشش را دارد؟13. When is multi-leader truly worth it?

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

مثال فنی: دو رهبر، یک کلید

وضعیت اولیه: مرکز A برای P7 مقدار ۱۲۰ و مرکز B هم‌زمان ۱۳۰ را محلی می‌پذیرند.

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

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

Within one region usually not (complexity doesn't pay). In geographic distribution: low-latency local writes, surviving one region's failure, and resisting cross-region link trouble. In offline-first/local-first: each device or tab is its own leader. Price: inability to guarantee global constraints (uniqueness, non-negative balances) and conflict-resolution duty.

۱۴. توپولوژی‌های تکثیرِ چندرهبر و مشکلِ هرکدام؟14. Multi-leader replication topologies and each one's problem?

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

مثال فنی: توپولوژی و مسیرهای تکراری

وضعیت اولیه: رهبرهای A، B و C تغییرها را به یکدیگر می‌فرستند.

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

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

Circular and star: a write may pass through several nodes (each node must forward, and node IDs are needed to prevent infinite loops) and one node's failure cuts the rest's flow. All-to-all: alternate paths exist but messages may arrive swapped (an update to a not-yet-inserted row); the principled fix is version vectors, not timestamps.

۱۵. LWW چرا گمراه‌کننده است و چه خطری با ساعت دارد؟15. Why is LWW misleading, and what clock danger does it carry?

دو تغییرِ مستقل «آخرین» مشترکی بر اساسِ آگاهی ندارند. انتخاب با ساعت می‌تواند تغییری واقعاً تازه‌تر را حذف کند، چون ساعتِ گره‌ها یکسان نیست.

مثال فنی: ساعت جلوتر، دادهٔ قدیمی‌تر

وضعیت اولیه: A با ساعت 12:05 مقدار ۱۲۰ می‌نویسد؛ B بعداً با ساعت درست 12:01 مقدار ۱۳۰ می‌نویسد.

اجرای مرحله‌به‌مرحله: LWW بر اساس timestampِ خام، ۱۲۰ را برنده می‌کند و تغییر بعدی کاربر را کنار می‌گذارد.

نتیجه و دلیل: «آخرین» در این سیاست یعنی بزرگ‌ترین timestamp، نه لزوماً آخرین رخداد واقعی؛ skew می‌تواند دادهٔ معتبر را حذف کند.

For “concurrent” writes the notion of “last” is undefined; so LWW effectively picks a winner at random and silently drops the rest. If the timestamp comes from the wall clock, a fast-clocked node can ignore genuinely later writes; logical clocks reduce that dependence. LWW is only safe when writes are inserts only, never updates.

۱۶. siblings چیست و ادغامِ دستی چه تله‌هایی دارد؟16. What are siblings, and what traps does manual merging hold?

Siblings نسخه‌های متعارض‌اند که برای تصمیمِ بعدی نگه داشته می‌شوند. ادغام باید حذف و تغییرِ هم‌زمان را هم بفهمد؛ اجتماعِ سادهٔ فهرست‌ها می‌تواند دادهٔ حذف‌شده را برگرداند.

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

وضعیت اولیه: دستگاه A به سبد خالی «کتاب» و دستگاه B هم‌زمان «قلم» اضافه می‌کند.

اجرای مرحله‌به‌مرحله: انتخاب صرفاً یکی از دو نسخه یک قلم را گم می‌کند؛ union مجموعه می‌تواند هر دو را حفظ کند، اما حذفِ هم‌زمان semantics مشخص می‌خواهد.

نتیجه و دلیل: حل تعارض وابسته به نوع داده و معنای عملیات است؛ یک سیاست LWW برای همهٔ کاربردها مناسب نیست.

When concurrent writes are detected, the database keeps all values and returns them all on read (like CouchDB). Traps: the API changes a “value” into a “set of values”; manual merging tires users; a naive automatic merge (like cart union) resurrects the deleted; and if several nodes “resolve” concurrently, the resolution itself may create a fresh conflict (B/C vs C/B).

۱۷. CRDT و OT چطور دو ویرایشِ هم‌زمانِ «ice» به «nice» و «ice!» را ادغام می‌کنند؟17. How do CRDT and OT merge two concurrent edits of “ice” into “nice” and “ice!”?

OT موقعیتِ عملیات را نسبت به عملیاتِ هم‌زمان تبدیل می‌کند. CRDT با شناسه و قواعدِ طراحی‌شده برای همگرایی کار می‌کند؛ هر دو باید برای نوعِ داده و عملیات مناسب باشند.

مثال فنی: ادغامِ متنِ هم‌زمان

وضعیت اولیه: دو کاربر متن ice را می‌بینند؛ یکی ابتدای آن n و دیگری انتها ! درج می‌کند.

اجرای مرحله‌به‌مرحله: OT موقعیت عملیات را با توجه به تغییر هم‌زمان تبدیل می‌کند؛ CRDT متنی هویت و ترتیب درج‌ها را طبق الگوریتم نگه می‌دارد.

نتیجه و دلیل: با طراحی مناسب هر دو درج به nice! می‌رسند؛ CRDT به‌معنی union سادهٔ رشته‌ها یا حل همهٔ تعارض‌های معنایی نیست.

OT records operation indexes (inserting “n” at 0 and “!” at 3) and at merge time transforms the second operation's index against the first insert (“!” becomes 4) to build “nice!”. CRDT gives every character a fixed ID and describes insertion as “after which ID”; concurrent inserts at one position order deterministically by ID. Both reach the same result by different roads.

۱۸. quorum چیست و شرطِ w + r > n چه می‌دهد؟18. What is a quorum, and what does w + r > n buy?

n تعدادِ نسخه‌ها، w تعدادِ تأییدِ نوشتن و r اندازهٔ خواندن است. شرطِ w + r > n هم‌پوشانیِ مجموعه‌ها را می‌دهد؛ با فرض‌های لازم، خواندن به نسخهٔ دارای نوشتن می‌رسد.

مثال فنی: هم‌پوشانی مجموعه‌ها

وضعیت اولیه: n=3,w=2,r=2 و نوشتن روی {A,B} موفق شده است.

اجرای مرحله‌به‌مرحله: هر مجموعهٔ خواندنِ دوعضوی از همین سه replica حداقل با یکی از A یا B مشترک است.

نتیجه و دلیل: شرط w+r>n هم‌پوشانی می‌دهد؛ برای خطی‌پذیری هنوز نسخه‌بندی، concurrency و پروتکل خواندن/نوشتن مهم‌اند.

n copies of each value, w minimum write acks, r nodes queried on reads. Because the read and write sets overlap in at least one node, reads can see the freshest value. The common pick is a majority (tolerating up to ⌊n/2⌋ failures) but only overlap matters; with w+r≤n you get lower latency and more availability at the price of more stale reads.

۱۹. read repair، hinted handoff و anti-entropy هرکدام چه می‌کنند؟19. What do read repair, hinted handoff and anti-entropy each do?

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

مثال فنی: ترمیمِ replica جا‌مانده

وضعیت اولیه: C هنگام نوشتن قطع بوده و A موقتاً hint آن را نگه داشته است.

اجرای مرحله‌به‌مرحله: hinted handoff بعداً تغییر را می‌رساند؛ read repair اختلاف در خواندن را اصلاح می‌کند؛ anti-entropy دوره‌ای کلیدهای خوانده‌نشده را هم مقایسه می‌کند.

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

Read repair: in a parallel read, one answer's staleness shows and the fresh value is written back to that node (great for hot keys). Hinted handoff: an absent node's writes are kept in its place (as hints) and delivered later. Anti-entropy: a background process copying differences, with no order at all — it may run late and is hard to use for health measurement too.

۲۰. version vector چیست و چه تفاوتی با vector clock دارد؟20. What is a version vector, and how does it differ from a vector clock?

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

مثال فنی: تشخیصِ concurrent با نسخه

وضعیت اولیه: دو نسخه بردارهای {A:2,B:1} و {A:1,B:2} دارند.

اجرای مرحله‌به‌مرحله: هیچ‌کدام در همهٔ مؤلفه‌ها دیگری را پوشش نمی‌دهد؛ دو تغییر concurrent‌اند. نسخهٔ {A:2,B:2} می‌تواند پس از ادغام هر دو ساخته شود.

نتیجه و دلیل: این مقایسه وابستگی علّی را می‌گوید، نه اینکه ساعت دیواری کدام ماشین جلوتر است.

Instead of one version number, one counter per key per replica; each replica advances its own counter on write and keeps the counters seen from the rest. Reads return them and writes must send them back; so the server knows which values it may overwrite (under the vector) and which are siblings (concurrent). The vector-clock difference is subtle: the version vector is the right structure for comparing “replica states”. Riak uses dotted version vectors (calling them causal context).

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

  • گفتنِ «تکثیر یعنی کپی داده» و غافل شدن از اینکه تمامِ سختی در تغییرات و هم‌زمانی است.
  • یکی دانستنِ تکثیر و بکاپ — یا فراموش کردنِ اینکه حذفِ اشتباهی هم تکثیر می‌شود.
  • تلقیِ «eventual consistency یعنی بعد از چند ثانیه درست می‌شود»؛ «eventually» حد بالا ندارد.
  • فراموش کردنِ اینکه read scaling فقط با تکثیرِ ناهمزمان عملی است (وگرنه یک خرابی سیستم را می‌بندد).
  • در failover، فقط گفتنِ «follower تازه leader می‌شود» و ندیدنِ سه خطر: نوشته‌های گم‌شده، split brain و timeout.
  • گفتنِ «LWW یعنی آخرین نوشته برنده است» بدونِ توضیحِ اینکه برای نوشته‌های هم‌زمان، «آخرین» تعریف‌نشده و برنده تصادفی است.
  • پیش‌فرض گرفتنِ «حفظِ ترتیبِ نوشته‌ها» در تکثیرِ all-to-all یا بدون‌رهبر؛ ترتیبِ پیام‌های شبکه قابل‌اعتماد نیست.
  • درست تعریف نکردنِ هم‌زمانی (concurrency): هم‌زمان یعنی «هیچ‌کدام از دیگری خبر نداشته»، نه «در یک لحظهٔ فیزیکی».
  • گفتنِ «quorum یعنی اکثریت»؛ quorum یعنی هم‌پوشانیِ مجموعه‌های خواندن و نوشتن (w + r > n)، اکثریت فقط یک انتخابِ رایج است.
  • فراموش کردنِ اینکه حتی با quorum هم خواندنِ کهنه ممکن است (بازسازی از replica قدیمی، rebalancing، نوشته‌های ناموفقِ rollback‌نشده).
  • گفتنِ «CRDT همه‌چیز را حل می‌کند»؛ قیدهای ساختاری (مثل «حداکثر ۵ آیتم») با نوشته‌های هم‌زمان ذاتاً ناسازگارند.
  • ادغامِ مجموعه‌ایِ ساده بدونِ ردیابیِ حذف‌ها (تلهٔ سبد خرید).
  • یکی دانستنِ version vector و vector clock، یا فراموش کردنِ بازگرداندنِ vector هنگامِ نوشتنِ بعدی.
  • توصیهٔ multi-leader برای هر سناریویی؛ در یک region معمولاً پیچیدگی‌اش به صرفه نیست و قیدهای سراسری را هم نمی‌شود تضمین کرد.
  • Saying “replication means copying data” while missing that all the pain is in change and concurrency.
  • Conflating replication with backup — or forgetting that a mistaken deletion replicates too.
  • Reading “eventual consistency” as “eventually right after a few seconds”; “eventually” has no upper bound.
  • Forgetting that read scaling only works with async replication (otherwise one failure closes the system).
  • In failover, only saying “a fresh follower becomes leader” and missing the three risks: lost writes, split brain and timeouts.
  • Saying “LWW means the last write wins” without explaining that for concurrent writes “last” is undefined and the winner is random.
  • Assuming “write order is preserved” in all-to-all or leaderless replication; network message order is untrustworthy.
  • Defining concurrency wrong: concurrent means “neither knew of the other”, not “at one physical instant”.
  • Saying “quorum means majority”; quorum means read/write set overlap (w + r > n) — majority is just a common pick.
  • Forgetting that even with a quorum, stale reads are possible (rebuilding from an old replica, rebalancing, un-rolled-back failed writes).
  • Saying “CRDTs solve everything”; structural constraints (like “at most 5 items”) are inherently incompatible with concurrent writes.
  • Naive set-union merging with no deletion tracking (the shopping-cart trap).
  • Conflating version vectors with vector clocks, or forgetting to return the vector on the next write.
  • Recommending multi-leader for every scenario; within one region its complexity rarely pays, and global constraints can't be guaranteed either.

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

  1. سه خانوادهٔ تکثیر + یک جمله مزیت و یک جمله بهای هرکدام.
  2. تفاوتِ sync / semisync / async و اینکه «تأییدشده» در async یعنی چه.
  3. قصهٔ failover را در سه گام و سه خطر تعریف کن (split brain و fencing را حتماً بگو).
  4. سه ناهنجاریِ lag + نامِ تضمینِ مقابلِ هرکدام + یک راه‌حلِ عملیِ هرکدام.
  5. چهار راهبردِ حلِ تعارض: avoidance / LWW / manual / automatic — و تلهٔ سبد خرید.
  6. تعریفِ happens-before و هم‌زمانی + نقشِ version vector در تشخیصِ overwrite از تعارض.
  7. ریاضیِ quorum (n، w، r، شرطِ w + r > n) و سه مکانیزمِ ترمیم (read repair، hinted handoff، anti-entropy).
  8. دو سه جمله دربارهٔ sloppy quorum، request hedging و gray failure — نشانِ اینکه عملیات را می‌فهمی.
  1. The three replication families + one advantage and one price sentence each.
  2. Sync / semisync / async differences, and what “acknowledged” means in async.
  3. Tell the failover story in three steps and three risks (split brain and fencing are a must).
  4. The three lag anomalies + the guarantee facing each + one practical fix each.
  5. Four conflict strategies: avoidance / LWW / manual / automatic — and the cart trap.
  6. The happens-before and concurrency definition + the version vector's role in telling overwrite from conflict.
  7. Quorum math (n, w, r, the w + r > n condition) and three repair mechanisms (read repair, hinted handoff, anti-entropy).
  8. Two or three sentences on sloppy quorum, request hedging and gray failure — proof you understand operations.

۷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

تمرین ۱ — lag را به دام بیندازExercise 1 — trap the lag

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

replica lag و consistency تجربهٔ کاربر مستقیماً به طراحی مرتبط‌اند؛ نصب replica را به مسیر آزمایشگاهی منتقل می‌کنیم.A useful consistency discussion; replica setup belongs in the lab.

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

بعد از ویرایش پروفایل، یک read replica مقدار قدیمی برمی‌گرداند. read-your-writes، monotonic reads و causal consistency را با history کوتاه نشان بده. routing به primary، session stickiness و version/LSN barrier را مقایسه کن و برای lag بی‌حد fallback تعریف کن.Show read-your-writes, monotonic-read and causal anomalies after profile changes. Compare primary routing, stickiness and version/LSN barriers, including fallback when lag has no known bound.

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

  • زمان ثابت sleep را تضمین consistency نمی‌داندNo fixed sleep guarantee
  • دو replica را با version دنبال می‌کندVersion-aware routing
  • قیمت تازگی را در latency و availability می‌بیندFreshness/availability trade-off

پیگیری مصاحبه:Interview follow-up: اگر primary بعد از ACK خراب شود، read-your-writes چه وابستگی‌ای به replication durability دارد؟If the primary fails after ACK, how does read-your-writes depend on replication durability?

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

یک اسکریپتِ ساده بنویس که به یک پایگاه دادهِ تک‌رهبرِ ناهمزمان (مثلاً PostgreSQL با یک read replica) نوشته و بلافاصله از follower بخواند. lag را اندازه بگیر و بعد هر سه ناهنجاری را عمداً بازتولید کن: خواندنِ نوشتهٔ خودت، عقب‌گردِ زمان (دو خواندن از دو replica)، و جابه‌جاییِ ترتیبِ علّی. برای هرکدام یک راه‌حلِ عملی پیاده کن و نشان بده ناهنجاری رفع شده است.

Write a small script that writes to an async single-leader database (e.g. PostgreSQL with one read replica) and immediately reads from the follower. Measure the lag, then deliberately reproduce all three anomalies: reading your own writes, time moving backward (two reads from two replicas), and swapped causal order. Implement one practical fix for each and show the anomaly gone.

برای یادگیریِ عمیق است؛ انجام کاملش در زمان بحثِ مصاحبه انتظار نمی‌رود.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 · Atlassian · engineering interview handbook

تمرین ۲ — سبد خریدِ هم‌زمانExercise 2 — the concurrent shopping cart

L2 · SeniorL2 · Seniorوابسته به نقشRole-specific35 دقیقه بحث35 min discussion

هر دو دستگاه ابتدا milk:t1 و bread:t2 را دیده‌اند؛ فاز اول add تازه ندارد. حذف tag مشاهده‌شده با حذف یک add هم‌زمان فرق دارد.Both devices initially observed milk:t1 and bread:t2; phase one has no fresh adds. Removing observed tags differs from removing a concurrent new add.

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

دو دستگاه سبد مشترک را آفلاین ویرایش می‌کنند: هر کدام یک آیتم شناخته‌شده را حذف می‌کند و هیچ add تازه‌ای وجود ندارد. LWW، union سادهٔ siblings و OR-Set با tag/tombstone را مقایسه کن. سپس add هم‌زمان با remove را اضافه کن و semantics مطلوب محصول را انتخاب کن.Start with two offline devices removing different known cart items and no new adds. Compare LWW, sibling union and an OR-Set with tagged adds/removals. Then introduce concurrent add/remove and choose product semantics.

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

  • LWW را با clock skew و lost update تحلیل می‌کندClock skew and lost updates
  • remove مشاهده‌شده با concurrent add تازه فرق داردObserved removals versus fresh concurrent adds
  • GC tombstone را به causal stability وصل می‌کندSafe tombstone collection

پیگیری مصاحبه:Interview follow-up: دستگاهی یک سال آفلاین بوده؛ tombstone را چه زمانی می‌توان پاک کرد؟A device has been offline for a year. When is it safe to collect tombstones?

آزمایشگاه تکمیلی · زمان تقریبی ۲–۴ ساعتExtended hands-on laboratory · estimated 2–4 hours

دو دستگاه سبد {milk:t1, bread:t2} را دیده‌اند و آفلاین می‌شوند: A شیر و B نان را حذف می‌کند و هیچ add جدیدی نداریم. LWW کل سبد، نگه‌داشتن sibling با سیاست union صریح، و OR-Set با tag یکتای هر add و حذف tagهای مشاهده‌شده را مقایسه کن. نتیجه را در هر ترتیب ادغام و با پیام تکراری بررسی کن. سپس milk:t3 را هم‌زمان با حذف روی دستگاه دیگر اضافه کن: برای add-wins و remove-wins قرارداد جدا بنویس. برای tombstone GC، causal stability یا محدودیت معتبر عضویت/آفلاین‌بودن را مشخص کن و ورود دوبارهٔ replica قدیمی را بیازما؛ صرف انقضای تایمر دلیل امنی برای GC نیست.

Both devices observe {milk:t1, bread:t2}, then go offline: A removes milk and B removes bread, with no new adds. Compare whole-cart LWW, siblings with an explicit union policy, and an OR-Set with unique add tags and observed-tag removals. Check every merge order and duplicate delivery. Then add milk:t3 concurrently with another device's remove; specify separate add-wins and remove-wins contracts. Justify tombstone GC using causal stability or an enforced membership/offline bound, and test an old replica rejoining; a timer alone does not establish safe GC.

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

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Preguiça, Baquero, Shapiro · CRDTs · 2018 · Atlassian · engineering interview handbook

تمرین ۳ — مرز تضمین‌های quorumExercise 3 — quorum guarantee boundaries

L3 · Staff / PrincipalL3 · Staff / Principalوابسته به نقشRole-specific45 دقیقه بحث45 min discussion

برای membership ثابت، هر write quorum سه‌عضوی با هر read quorum سه‌عضوی از پنج عضو اشتراک دارد؛ تازگی به نسخه‌ها، دوام و عضویت نیز وابسته است.With fixed membership, every three-node write quorum intersects every three-node read quorum among five nodes; freshness also needs version, durability and membership assumptions.

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

با n=5,w=3,r=3 نشان بده چه تضمینی از تقاطع مجموعه‌ها داریم و چه تضمینی نداریم. partial write، lost ACK، stale replacement و تغییر membership را تحلیل کن. مانیتور staleness را با version، probe و divergence طراحی کن؛ failure را با rollback یکی ندان.For n=5,w=3,r=3, explain quorum intersection and its limits. Analyze partial writes, lost ACKs, stale replacement and changing membership; define useful version probes and divergence monitoring.

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

  • membership و durable ACK را از نامساوی جدا می‌کندMembership and durability assumptions
  • نوشتن ناموفق می‌تواند قابل مشاهده باشدVisibility of partial writes
  • بدون ترتیب لاگ، واحد سنجش تازگی را تعریف می‌کندMeaningful staleness metrics

پیگیری مصاحبه:Interview follow-up: در rebalancing هم‌زمان، quorum قدیم و جدید چگونه باید هم‌پوشانی داشته باشند؟How should old and new quorums overlap during concurrent rebalancing?

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

پنج replica با w=3 و r=3 و نسخهٔ مرتب در این مدل آموزشی بساز. ابتدا با شمارش مجموعه‌ها ثابت کن w+r>n اشتراک می‌دهد. پاسخ گمشده را از ذخیره‌نشدن جدا ثبت کن؛ ACKها باید به نوشتن بادوام مربوط باشند. سپس بازسازی ناقص replica، تغییر membership بدون انتقال امن و نوشتن جزئیِ اعلام‌شده به‌عنوان ناموفق را تزریق کن. برای هر history، زمان ارسال، دوام، ACK و مقدار read را نشان بده و بگو کدام فرض نقض شده؛ خود نامساوی تغییر نمی‌کند. مانیتور version divergence و probe خواندن پس از نوشتن طراحی کن. فاصلهٔ عدد نسخه فقط در مدل نسخهٔ شمارنده معنا دارد؛ اندازه‌گیری زمانی به timestamp/ساعت مرجع و عدم‌قطعیت مشخص نیاز دارد.

Simulate five replicas with w=3, r=3 and ordered versions in this teaching model. First prove quorum intersection by enumerating sets. Distinguish lost responses from missing storage, and tie ACKs to durable writes. Inject stale replica restoration, unsafe membership changes and partially applied failed writes. Record send, persistence, ACK and read results for each history, identifying the failed premise while the inequality stays true. Design version-divergence monitoring and read-after-write probes. Numeric version distance is meaningful only for a counter model; time-based staleness needs a timestamp/reference clock and stated uncertainty.

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

ارزیابی، سطح و شواهد این تمرینAssessment, level and evidence · حل آموزشی این تمرینWorked solution · Attiya, Bar-Noy, Dolev · atomic registers · 1995 · Jepsen · consistency models