Part 03 · High Level Design

Consensus, Leader Election and Distributed Locks

ConsensusRaftLeader electionDistributed locksFencing tokensClocks

অনেক machine জুড়ে concurrency: clock কেন মিথ্যা বলে, leader election, Raft দিয়ে consensus, আর fencing token দিয়ে নিরাপদ distributed lock।

⏱ 3-4 ঘণ্টা · Station 28 / 80

যে job দুইবার চলল

একটা payroll service চলে তিনটা server এ। মাসে একবার, রাত ১২টায়, ঠিক একটা server কে bank এ বেতন পাঠাতে হয়। একটাই যেন পাঠায় সেজন্য team Redis এ একটা lock ব্যবহার করে: যে আগে payroll-lock key টা set করবে সে job চালাবে, আর server crash করলে আটকে না থাকে সেজন্য key টা 30 সেকেন্ডে expire হয়।

এক মাসে server A lock নিয়ে শুরু করল। মাঝপথে A এর Java process একটা garbage-collection pause এ 40 সেকেন্ড থেমে থাকল। Lock expire হয়ে গেল। Server B দেখল lock ফাঁকা, নিয়ে নিল, আর একই job শুরু করল। তারপর A জেগে উঠল, এখনো ভাবছে lock তার কাছে, আর কাজ চালিয়ে গেল। কিছু কর্মী দুইবার বেতন পেল।

কিছুই crash করেনি। প্রতিটা অংশ যেভাবে বানানো সেভাবেই কাজ করেছে। Bug টা একটা চুপচাপ ধরে নেওয়া কথায়: একটা process জানে lock এখনো তার কাছে আছে কিনা, আর এক machine এর সময় আরেক machine এর সময়ের সমান। Distributed system এ এর কোনোটাই সত্যি না। এই chapter হলো machine গুলো তবুও কীভাবে একমত হয়: clock কেন মিথ্যা বলে, একটা দল কীভাবে একজন leader বাছে (Raft), consensus কী দেয়, আর fencing token দিয়ে lock কীভাবে নিরাপদ করা যায়।

সমস্যা: যে machine রা সময়কে বা একে অপরকে ভরসা করতে পারে না

কী হয়কেন
পুরনো write নতুনটাকে overwrite করেভিন্ন ভিন্ন clock এর machine এর দেয়ালঘড়ির timestamp দিয়ে "সবশেষ" ঠিক করা
দুইটা server ই ভাবে সে leaderNetwork এর একটা ঝাঁকুনিতে পুরনো leader কে মৃত মনে হয়েছে, অথচ সে বেঁচে ছিল
Lock থাকার পরও job দুইবার চলেPause এর সময় lock expire হয়েছে; পুরনো holder জানতেই পারেনি
Cluster একদমই এগোতে পারে নাMajority বানানোর মতো যথেষ্ট node বেঁচে নেই
Election বারবার হতেই থাকেNode গুলো একই মুহূর্তে timeout হয়ে vote ভাগ করে ফেলে
"Highly available" lock service failover এ lock হারায়সে asynchronously replicate করত; নতুন primary lock টা কখনো দেখেনি

চেয়ারম্যান ছাড়া একটা কমিটির কথা ভাবো

পাঁচজন কমিটি সদস্য আলাদা আলাদা শহরে থাকেন, কথা বলেন শুধু চিঠিতে। চিঠি দেরিতে আসতে পারে, হারাতে পারে, উল্টো ক্রমে পৌঁছাতে পারে। তাদের একসাথে সিদ্ধান্ত নিতে হয়: অফিসের চাবি কার কাছে থাকবে, কোন প্রস্তাব পাস হলো।

তারা দুইটা নিয়মে রাজি হন। প্রথমত, একটা সিদ্ধান্ত গোনা হবে শুধু যদি majority (5 এর 3) রাজি থাকে। দুইটা আলাদা majority তে সবসময় অন্তত একজন সদস্য মিলবেন, তাই দুইটা উল্টো সিদ্ধান্ত একসাথে পাস হতে পারে না। দ্বিতীয়ত, তারা একটা নম্বর দেওয়া term এর জন্য একজন চেয়ারম্যান বাছেন, আর শুধু চেয়ারম্যান সিদ্ধান্ত প্রস্তাব করেন। কিছুদিন চেয়ারম্যানের কোনো খবর না পেলে কেউ একজন পরের term এর জন্য নিজের নাম প্রস্তাব করে vote চান। প্রত্যেক সদস্য প্রতি term এ একবার vote দেন।

আর অফিসের চাবি ধার দেওয়ার সময় প্রতিটা ধারের একটা নম্বর থাকে যেটা শুধু বাড়ে: ধার #33, ধার #34। অফিসের দরজা একটা চাবি মানে শুধু যদি তার নম্বর এ পর্যন্ত দেখা সবচেয়ে বড় নম্বরের সমান বা বেশি হয়। ধার #33 এর মালিক এক মাস গায়েব থেকে ফিরে এলে, দরজা আগেই #34 চেনে, তাই তাকে ঢুকতে দেয় না। এটাই fencing token।

N1leader, term 1N2followerN3followerN4followerN5follower♥♥♥♥votevotevotevotevotethe leader sends heartbeats: 'I'm alive, term 1'N1 dies; the heartbeats stopterm 2: N3 and N5 time out at once, each asks for votes2 votes each, no majority of 5: nobody leadsterm 3: after a random wait N5 asks first and gets 4 of 5one vote per node per term; a leader needs a majority
Play চাপো। Leader N1 heartbeat পাঠায়, তারপর মরে যায়। Term 2 এ N3 আর N5 একই মুহূর্তে timeout হয়ে vote 2 আর 2 এ ভাগ করে ফেলে, তাই কেউ জেতে না। একটা random অপেক্ষার পর term 3 এ N5 আগে চায় আর 5 এর 4টা vote পায়।

তবে একটা জায়গায় মেলে না। এক মাস পর ফেরা কমিটি সদস্য অন্তত জানেন যে এক মাস পেরিয়েছে। থেমে থাকা একটা process জানে না: তার কাছে কোনো সময়ই পার হয়নি। এজন্যই সুরক্ষা টা থাকতে হয় resource এর কাছে (দরজা, storage), holder এর নিজের সময়ের ধারণায় না।

আগে অনুমান করো

একটা cluster এ 5টা node, majority voting চলে। কয়টা node fail করলেও cluster leader বাছতে আর write নিতে পারবে?

একমত হওয়ার পেছনের নিয়মগুলো

  1. Clock সরে যায় আর লাফ দেয়। প্রতিটা machine এর clock একটু দ্রুত বা ধীরে চলে, আর NTP সেটা ঠিক করে, কখনো পেছনে লাফিয়ে। Machine জুড়ে দেয়ালঘড়ির সময় কয়েক millisecond, খারাপ দিনে কয়েক সেকেন্ডও আলাদা হতে পারে। ভিন্ন machine এর দেয়ালঘড়ির timestamp মিলিয়ে কখনো "কোন write নতুন" ঠিক করবে না, আর ধরে নেবে না যে এক machine এর timeout আরেকটার সমান।
  2. Logical clock event সাজায়। Lamport clock একটা counter: প্রতিটা event এ বাড়াও, প্রতিটা message এর সাথে পাঠাও, আর message পেলে নিজেরটা বানাও max(নিজের, পাওয়া) + 1। A যদি B এর আগে ঘটে থাকে (A থেকে B হয়েছে), A এর সংখ্যা ছোট। Version vector আরেক ধাপ এগিয়ে ধরে ফেলে কখন দুইটা write একসাথে ঘটেছে।
  3. Consensus মানে একদল node একটা value (বা value এর একটা ক্রম, একটা replicated log) নিয়ে এমনভাবে একমত হওয়া, যাতে কিছু node crash করলে বা message হারালেও সেটা একমতই থাকে। এগোতে majority লাগে, আর কখনো দুইটা আলাদা জিনিস ঠিক করে না।
  4. সংক্ষেপে Raft। Node রা follower, candidate বা leader। Leader heartbeat পাঠায়। কোনো follower একটা random election timeout (ধরো 150 থেকে 300 ms) ধরে কিছু না শুনলে candidate হয়ে যায়, term বাড়ায়, নিজেকে vote দেয় আর বাকিদের কাছে চায়। প্রতিটা node প্রতি term এ একবার vote দেয়, আর শুধু এমন candidate কে যার log অন্তত তার মতো update। Majority পেলে leader। Random timeout split vote কে বিরল আর ছোট রাখে।
  5. Leader একটা log replicate করে। Client write পাঠায় leader এর কাছে; সে নিজের log এ যোগ করে follower দের পাঠায়; majority একটা entry রেখে দিলে সেটা committed হয় আর apply হয়। নতুন leader এর কাছে সবসময় প্রতিটা committed entry থাকে, তাই committed data কখনো হারায় না।
  6. Consensus system ব্যবহার করো, নিজে লিখো না। etcd, ZooKeeper আর Consul (ভেতরে Raft বা Zab) দেয় leader election, distributed lock, configuration আর service membership। CockroachDB, TiDB আর Spanner এর মতো database প্রতিটা data range এ consensus ব্যবহার করে। নিজে লেখা সূক্ষ্ম bug এর পুরনো উৎস।
  7. Lease হলো expiry সহ একটা lock। Crash করা client এর কাছে lock চিরকাল আটকে থাকা এড়ায়। কিন্তু থেমে থাকা বা slow client বুঝতে পারে না তার lease শেষ, তাই শুধু lease দিয়ে পুরনো holder কে কাজ করা থেকে থামানো যায় না।
  8. Fencing token lock কে নিরাপদ বানায়। প্রতিবার lock দেওয়ার সময় lock service একটা সংখ্যা দেয় যেটা শুধু বাড়ে (etcd revision, ZooKeeper zxid)। Client প্রতিটা write এর সাথে সেটা পাঠায়; resource এ পর্যন্ত দেখা সবচেয়ে বড় token মনে রাখে আর ছোট কিছু এলে ফিরিয়ে দেয়। পুরনো holder দরজাতেই আটকায়।
  9. পারলে lock এর দরকারই রাখো না। Idempotent কাজ, unique constraint, compare-and-set ("version = 7 হলেই শুধু update করো") আর প্রতি partition এ একজন মালিক প্রায়ই distributed lock এর প্রয়োজন পুরোপুরি সরিয়ে দেয়, আর এগুলো ঠিকমতো করা সহজ।
Client AGC pauseClient BLock servicelease 5 sStoragehighest seen: 34Storagerejects 33token 33token 34write, 34write, 33A gets the lock with token 33, then freezesthe 5 s lease runs out; B gets token 34 and writesA wakes up, still thinks it holds the lock, and writes33 < 34: the storage refuses the stale writer
Play চাপো। Client A token 33 সহ lock নেয়, তারপর থেমে যায়। 5 সেকেন্ডের lease শেষ হয়, client B token 34 পেয়ে লেখে। A জেগে উঠে, এখনো ভাবছে lock তার কাছে, token 33 দিয়ে লেখে। Storage আগেই 34 দেখেছে, তাই ফিরিয়ে দেয়।

ধাপে ধাপে একটা leader failover

ধাপে ধাপে দেখো

5 node এর Raft cluster এর leader মরলে কী হয়

Leader N1Follower রাCandidate N5MajorityClient রা

ধাপ 1/5 N1 term 1 এর leader

প্রতি 50 ms এ heartbeat পাঠায় আর প্রতিটা write follower দের কাছে replicate করে। 5 এর 3 জনের কাছে পৌঁছালে write committed।

term
1
leader
N1
committed index
1,204

ধাপে ধাপে machine জুড়ে সমন্বয়

  1. আগে জিজ্ঞেস করো সমন্বয় আদৌ লাগবে কিনা। কাজটা কি idempotent করা যায়, unique constraint দিয়ে পাহারা দেওয়া যায়, বা compare-and-set বানানো যায়? একটা partition কি সবসময় একজন worker এর হাতে রাখা যায়?
  2. একটা node কেই একা কাজ করতে হলে (একটা scheduler, payroll job), etcd, ZooKeeper, Consul বা তোমার database এর leader election ব্যবহার করো, ঘরে বানানো timer না।
  3. Lock লাগলে consensus এর উপর দাঁড়ানো service থেকে lease সহ নাও, আর তার fencing token প্রতিটা resource এ পাঠাও যেখানে লেখো।
  4. Resource কে token check করাও (একটা version column, একটা conditional write), কারণ পুরনো holder কে থামাতে পারে শুধু resource।
  5. ক্রম যখন সঠিকতার জন্য জরুরি, event সাজাও logical version দিয়ে, দেয়ালঘড়ি দিয়ে না।
  6. Failure এর হিসাব করে cluster এর size ঠিক করো: 3টা node 1টা failure সহ্য করে, 5টা 2টা। Zone জুড়ে ছড়িয়ে দাও।
  7. Majority না থাকলে কী হবে ঠিক করো: system এর উচিত write ফিরিয়ে দেওয়া, আন্দাজ করা না।

নিজে বলো: সমন্বয়টা নিরাপদ নাকি না?

প্রতিটা case চালিয়ে দেখো

এটা কি দুইটা worker কে ধাক্কা খাওয়া থেকে বাঁচাবে?

থেমে থাকা worker জেগে উঠল; পুরনো token সহ তার write ফেরত
কাজ করে: ঠিক আছে। Holder মরে গেলে lease lock ছেড়ে দেয়, আর token এমন holder কে থামায় যে জানে না সে lock হারিয়েছে।

চালিয়ে দেখার মতো clock, vote আর fence

তিনটা experiment। 2 সেকেন্ডের clock পার্থক্যের দুইটা server ঠিক করে কোন দামের write নতুন, একবার দেয়ালঘড়ি দিয়ে, একবার Lamport clock দিয়ে। N1 মরার পর পাঁচটা node leader বাছে: term 2 এ split vote, তারপর term 3 এ একজন জেতে। আর 5 সেকেন্ডের lease এর একটা lock, যার holder 10 সেকেন্ড থেমে থাকে, storage এ fencing check সহ আর ছাড়া। ছয়টা version একই নিয়ম মানে, একই লাইন print করে।

package main

import (
	"fmt"
	"strings"
)

// 1. Clocks. Server B's clock runs 2 seconds fast. B writes price=90 first; A reads it and
// then writes price=100. The later write is A's.
func clocks() {
	type write struct {
		server  string
		price   int
		wall    int // what the server's own clock said, in ms
		lamport int
	}
	// Real times: B at 500 ms, A at 1000 ms. B's clock adds 2000 ms.
	b := write{"B", 90, 500 + 2000, 1}
	// A received B's write (carrying Lamport time 1) before writing, so A's counter becomes
	// max(own 0, seen 1) + 1 = 2.
	a := write{"A", 100, 1000, max(0, b.lamport) + 1}

	pick := func(by func(w write) int) write {
		if by(a) > by(b) {
			return a
		}
		return b
	}
	byWall := pick(func(w write) int { return w.wall })
	byLamport := pick(func(w write) int { return w.lamport })
	fmt.Println("clocks: B writes price=90, A sees it and writes price=100; B's clock is 2 s fast")
	fmt.Printf("  last write wins by wall clock: price=%d (B's stamp %d ms beats A's %d ms)\n", byWall.price, b.wall, a.wall)
	fmt.Printf("  last write wins by Lamport clock: price=%d (A's counter %d beats B's %d)\n", byLamport.price, a.lamport, b.lamport)
}

// 2. Leader election, Raft style. Each node votes at most once per term, for the first
// candidate whose request reaches it. A candidate needs a majority of all 5 nodes.
func election(term int, candidates []string, arrival map[string][]string) (string, bool) {
	nodes := []string{"N1", "N2", "N3", "N4", "N5"}
	votes := map[string]int{}
	votedFor := map[string]string{}
	for _, c := range candidates { // a candidate votes for itself
		votedFor[c] = c
		votes[c]++
	}
	for _, n := range nodes {
		if n == "N1" || votedFor[n] != "" { // N1 is down
			continue
		}
		votedFor[n] = arrival[n][0] // the first request to arrive wins this node's vote
		votes[arrival[n][0]]++
	}
	var tally []string
	winner := ""
	for _, c := range candidates {
		tally = append(tally, fmt.Sprintf("%s %d", c, votes[c]))
		if votes[c] >= 3 {
			winner = c
		}
	}
	fmt.Printf("  term %d: candidates %s, votes %s -> ", term, strings.Join(candidates, ", "), strings.Join(tally, ", "))
	if winner == "" {
		fmt.Println("no majority, wait a random time and try again")
		return "", false
	}
	fmt.Printf("%s leads with %d of 5\n", winner, votes[winner])
	return winner, true
}

// 3. A lock with a 5 s lease. Client A gets it, then freezes for 10 s (a long GC pause).
func lockDemo(fencing bool) string {
	token := 32
	highest := 0 // the storage remembers the highest token it has accepted
	file := ""
	write := func(who string, tok int) {
		if fencing && tok < highest {
			return // a stale lock holder: reject
		}
		highest = max(highest, tok)
		file = who
	}
	token++
	aToken := token // t=0: A gets the lock
	token++
	bToken := token // t=6 s: the lease has expired, B gets the lock
	write("B", bToken)
	write("A", aToken) // t=10 s: A wakes up, still believing it holds the lock
	return file
}

func main() {
	clocks()

	fmt.Println("election: leader N1 (term 1) dies; 4 nodes left, a majority is 3 of 5")
	if _, ok := election(2, []string{"N3", "N5"}, map[string][]string{"N2": {"N3", "N5"}, "N4": {"N5", "N3"}}); !ok {
		election(3, []string{"N5"}, map[string][]string{"N2": {"N5"}, "N3": {"N5"}, "N4": {"N5"}})
	}

	fmt.Println("lock with a 5 s lease: A gets token 33, pauses 10 s; B gets token 34 at 6 s and writes; A wakes and writes")
	fmt.Printf("  without fencing: the file says %s (B's write is lost)\n", lockDemo(false))
	fmt.Printf("  with fencing:    the file says %s (token 33 rejected, 34 already seen)\n", lockDemo(true))
}

ছয়টাই হুবহু এটা print করে:

clocks: B writes price=90, A sees it and writes price=100; B's clock is 2 s fast
  last write wins by wall clock: price=90 (B's stamp 2500 ms beats A's 1000 ms)
  last write wins by Lamport clock: price=100 (A's counter 2 beats B's 1)
election: leader N1 (term 1) dies; 4 nodes left, a majority is 3 of 5
  term 2: candidates N3, N5, votes N3 2, N5 2 -> no majority, wait a random time and try again
  term 3: candidates N5, votes N5 4 -> N5 leads with 4 of 5
lock with a 5 s lease: A gets token 33, pauses 10 s; B gets token 34 at 6 s and writes; A wakes and writes
  without fencing: the file says A (B's write is lost)
  with fencing:    the file says B (token 33 rejected, 34 already seen)

Output টা পড়ো:

  • wall clock: B এর write আগে হয়েছিল (আসল সময় 500 ms), কিন্তু B এর দ্রুত clock তাতে 2500 ms লিখেছে, তাই A এর পরের write কে হারিয়ে দেয়। ভুল দাম, 90, জেতে।
  • Lamport clock: A B এর write দেখেছিল, তাই A এর counter বড় (2 বনাম 1)। কারণ-ফলের দিক থেকে পরের write, 100, জেতে, clock যাই বলুক।
  • term 2: N3 আর N5 একসাথে দাঁড়িয়ে 2টা করে vote পেল। কারো 5 এর 3 নেই, তাই কেউ leader না, যেটা নিরাপদ।
  • term 3: একটা random অপেক্ষার পর শুধু N5 দাঁড়াল, পেল 4টা vote। একজন leader, majority দিয়ে বাছা।
  • without fencing: lease চলে যাওয়ার পরে A জেগে উঠে B এর data overwrite করল। File এ লেখা A।
  • with fencing: storage আগেই token 34 দেখেছিল, তাই 33 সহ A এর write ফেরত গেল। File এ এখনো B।
  • কোন language এ কী বদলালো: বাইরের variable বদলানো closure। Go, Rust, TypeScript আর C++ এর closure সরাসরি পারে (Rust এ let mut write লাগে)। Java র lambda শুধু effectively final variable ধরতে পারে, তাই fence এর state রাখা হয়েছে এক উপাদানের array তে; Python একই কারণে একটা ছোট dict ব্যবহার করে।

আসল project এ একমত হওয়া

  • etcd (Kubernetes এর উপর চলে), ZooKeeper (Kafka বহু বছর ব্যবহার করেছে) আর Consul হলো চেনা consensus service। ওদের election আর lock এর recipe ব্যবহার করো, আর lease আর revision নিয়ে ওদের docs পড়ো।
  • 3 বা 5টা node চালাও, availability zone জুড়ে ছড়ানো। বেশি voter মানে slow write; read বাড়াতে non-voting replica ব্যবহার করো।
  • Timeout: election timeout হতে হবে স্বাভাবিক network delay এর চেয়ে অনেক বড়, আর leader ছাড়া যতক্ষণ চলতে পারো তার চেয়ে ছোট। Leader বদলানো দেখো; ঘনঘন বদলালে মানে node overloaded বা network নড়বড়ে।
  • বাস্তবে fencing: etcd এর lease আর mod revision, ZooKeeper এর zxid, বা lock এর সাথে রাখা শুধু বাড়তে থাকা একটা epoch। Resource এটা check করে conditional write দিয়ে (WHERE epoch <= :token)।
  • Redis lock: কাজের দক্ষতার জন্য ঠিক আছে (যেখানে দুইবার হলে ক্ষতি নেই সেখানে দুইবার কাজ এড়ানো), সঠিকতার জন্য না, যদি না resource fence করা থাকে। "Redlock" এর দাবি নিয়ে সাবধান; ভরসা করার আগে ওটা নিয়ে বিতর্কটা পড়ে নাও।
  • Clock: সব জায়গায় NTP বা chrony চালাও আর drift এ alert দাও, তবু এমনভাবে design করো যাতে সঠিকতা clock এর মিলের উপর নির্ভর না করে। Spanner এর মতো system জানা ভুলের সীমা সহ বিশেষ hardware clock ব্যবহার করে আর সেই সময়টা অপেক্ষা করে নেয়।

যে design গুলো পরে problem create করে

প্রতিটা case চালিয়ে দেখো

সমন্বয়ের ভুলটা ধরো

network ভাগ হলো: প্রতিটা দিক নিজের leader বেছে নিল
ভাঙে: Majority এর নিয়ম ছাড়া দুই অর্ধেকই কাউকে বেছে নেয়। Consensus ভিত্তিক election ব্যবহার করো।

একটু থামো, ভাবো

তোমার team একটা availability zone এ 3 node এর একটা etcd cluster চালায়। কেউ প্রস্তাব দিল "বাড়তি security এর জন্য" ৪ নম্বর একটা node যোগ করতে। এটা কি বেশি নিরাপদ? এর বদলে তুমি কী করবে?

উত্তর দেখো

আসলে না। 4টা node এ majority হলো 3, তাই cluster এখনো মাত্র 1টা failure সহ্য করে, 3টা node এর মতোই, অথচ প্রতিটা write এখন 2 এর বদলে 3টা acknowledgement এর জন্য অপেক্ষা করে, আর fail করার মতো আরেকটা machine বাড়ল। জোড় সংখ্যা fault tolerance না বাড়িয়ে শুধু খরচ বাড়ায়। এখানে আসল ঝুঁকি হলো 3টা node ই একটা availability zone এ: zone এর একটা outage পুরো cluster ফেলে দেয়। ভালো হয়: 3টা node রাখো আর 3টা zone এ ছড়িয়ে দাও, বা 2টা failure সহ্য করতে হলে 3টা zone জুড়ে 5টা node নাও।

ছোট্ট quiz

1. ভিন্ন server এর দেয়ালঘড়ির timestamp দিয়ে write সাজানো কেন ঠিক না?

উত্তর

Server এর clock সরে যায় আর ঠিক করা হয় (কখনো পেছনে), তাই ভিন্ন machine এর timestamp তুলনা করা যায় না। পরে ঘটা একটা write আগের timestamp নিয়ে এসে হেরে যেতে পারে।

2. Raft এ election timeout কেন random?

উত্তর

যাতে follower রা খুব কমই ঠিক একই মুহূর্তে timeout হয়। হলে কয়েকজন candidate vote ভাগ করে ফেলত আর কেউ জিতত না; random হলে সাধারণত একটা node আগে গিয়ে majority জোগাড় করে।

3. শুধু lease যেটা আটকায় না, fencing token সেটা কী থেকে বাঁচায়?

উত্তর

এমন একজন holder থেকে, যে না জেনেই lease হারিয়েছে (long pause, slow network) আর তারপর কাজ করে। Resource তার write ফিরিয়ে দেয়, কারণ আরো বড় একটা token আগেই ব্যবহার হয়ে গেছে।

4. 5 node এর majority ভিত্তিক cluster কয়টা node failure সহ্য করতে পারে?

উত্তর

দুইটা। তিনটা node থাকে, যেটা এখনো পাঁচের majority। তৃতীয় failure এ write বন্ধ হয়, জেনেবুঝেই।

তোমার challenge: যখন majority নেই

Election কে সবসময় N1 বাদ না দিয়ে down node এর একটা set নিতে দাও। আগের দুইটা call রেখে দাও (শুধু N1 down), তারপর তৃতীয় একটা call যোগ করো: term 4, candidate N5, N1, N2 আর N4 down, আর N3 vote দেয় N5 কে।

  • Down node vote দেয় না, কিন্তু majority এখনো 5 এর 3: cluster এর size বদলায়নি।
  • চালানোর আগে ঠিক করো: N5 কি leader হতে পারবে?

তুমি পেরেছো যখন নতুন line টা হবে term 4: candidates N5, votes N5 2 -> no majority, wait a random time and try again আর বাকি line গুলো বদলাবে না। 5 এর 3টা node down থাকলে বাকি 2টা যত চেষ্টাই করুক, কখনো leader বাছতে পারবে না। এটা bug না: যখন system নিশ্চিত হতে পারছে না যে আর কেউ কাজ করছে না, তখন সে কাজ করতে অস্বীকার করছে।

এক লাইনে

Machine রা clock কে ভরসা করতে পারে না, আর জানে না তারা থেমে ছিল কিনা, তাই একমত হওয়া আসে majority থেকে: একটা consensus system কে (etcd, ZooKeeper, Consul এর Raft) প্রতি term এ একজন leader বাছতে আর log commit করতে দাও, event সাজাও logical clock দিয়ে, lock কে দাও একটা lease আর এমন fencing token যেটা resource check করে, zone জুড়ে বেজোড় (odd) সংখ্যার node চালাও, আর যেখানে idempotency বা compare-and-set দিয়েই চলে সেখানে lock এর দরকারই রেখো না।

Station stamp

Chapter শেষ করো

পড়া শেষ হলে Complete চাপো, progress save হবে। XP শুধু Pro তে — Pro থাকলে এই chapter এ +10 XP পেতে।

Pro নিয়ে XP পাও →