পাঠ ৩১ · ৫১-এর মধ্যে · মডিউল ৮
Home / Courses / System Design / কোরাম রিড/রাইট

কোরাম রিড/রাইট

Quorum reads & writes
৮ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • N, W, R-এর সংজ্ঞা এবং তারা কীভাবে রেপ্লিকেটেড সিস্টেমে consistency টিউন করার একটি নব হিসেবে কাজ করে
  • $R+W>N$ গ্যারান্টি কেন pigeonhole principle থেকে সরাসরি আসে
  • W=N,R=1 বনাম W=1,R=N বনাম ব্যালেন্সড কনফিগারেশনের ট্রেড-অফ
  • Python দিয়ে N=3 রেপ্লিকা সিমুলেট করে W=2,R=2 বনাম W=1,R=1-এর বাস্তব পার্থক্য দেখা

১ · N, W, R — কোরামের বেসিক সংজ্ঞা

L16-এ আমরা রেপ্লিকেশন দেখেছি — একটি ডেটা একাধিক নোডে কপি রাখা হয় availability ও read-scaling-এর জন্য। এখন প্রশ্ন হলো — একটি লেখা কতগুলো রেপ্লিকায় নিশ্চিত হওয়ার পরই সফল বলে ধরা হবে, এবং একটি পড়া কতগুলো রেপ্লিকা থেকে জিজ্ঞাসা করা হবে? QuorumQuorum (N, W, R)N = মোট রেপ্লিকার সংখ্যা, W = একটি write সফল হওয়ার আগে যত রেপ্লিকা acknowledge করতে হবে, R = একটি read-এর জন্য যত রেপ্লিকা জিজ্ঞাসা করা হবে — এই তিনটি সংখ্যা মিলিয়ে সিস্টেমের consistency/availability/latency ট্রেড-অফ ঠিক হয়। -ভিত্তিক সিস্টেমে এই দুটি সংখ্যা (W ও R) কনফিগারযোগ্য — এগুলোই ঠিক করে দেয় সিস্টেম কতটা কড়া বা শিথিল consistency মেনে চলবে।

২ · কোরাম কন্ডিশন — $R + W > N$

যদি $R + W > N$ হয়, তাহলে গণিতগতভাবেই প্রতিটি সেট (W-টি রেপ্লিকা যেখানে সর্বশেষ লেখা গেছে, এবং R-টি রেপ্লিকা যা পড়া হবে) অন্তত একটি কমন রেপ্লিকা শেয়ার করতে বাধ্য — নাহলে তাদের যোগফল N-এর বেশি হতে পারত না অথচ তারা N-টি স্লটে বিচ্ছিন্নভাবে বসতে চেষ্টা করছে। এটি ঠিক discrete-math কোর্সের pigeonhole principlePigeonhole Principleযদি n+1টি বস্তু n-টি বাক্সে রাখা হয়, অন্তত একটি বাক্সে একাধিক বস্তু পড়বেই — এখানে "বাক্স" হলো রেপ্লিকা, আর দুটি "গ্রুপ" (write-সেট ও read-সেট) একসাথে N-এর বেশি রেপ্লিকা কভার করলে তারা কোথাও না কোথাও ওভারল্যাপ করতে বাধ্য। -এর সরাসরি প্রয়োগ — দুটি গ্রুপ একসাথে উপলব্ধ স্লটের চেয়ে বেশি জায়গা দাবি করলে তাদের ওভারল্যাপ করতেই হবে।

এর মানে কী

যেই read-সেট বেছে নেওয়া হোক না কেন (যতক্ষণ আকার R), তার মধ্যে অন্তত একটি রেপ্লিকা এমন থাকবে যা সর্বশেষ লেখাটি পেয়েছিল। প্রতিটি লেখার সাথে একটি ভার্সন/টাইমস্ট্যাম্প যুক্ত থাকলে, read-এর সময় প্রাপ্ত মানগুলোর মধ্যে সর্বোচ্চ ভার্সনটি বেছে নিলেই সর্বশেষ লেখাটি নিশ্চিতভাবে পাওয়া যায় — এটাই strong consistency-এর গ্যারান্টি দেয়, কোনো একক "primary" নোডের প্রয়োজন ছাড়াই।

৩ · ট্রেড-অফ — বিভিন্ন W, R কনফিগারেশন

W=N, R=1
লেখা ধীর (সব রেপ্লিকার অপেক্ষা করতে হয়) কিন্তু পড়া দ্রুত ও সরল — যেকোনো একটি রেপ্লিকাই সর্বশেষ ডেটা দেবে।
W=1, R=N
লেখা দ্রুত (একটি রেপ্লিকা যথেষ্ট) কিন্তু পড়া ধীর — সব রেপ্লিকা চেক করে সর্বোচ্চ ভার্সন বের করতে হয়।
N=3, W=2, R=2
$R+W=4>3$ — উভয়ই মাঝারি লেটেন্সি, তবু strong consistency গ্যারান্টিড। Cassandra/Dynamo-স্টাইল সিস্টেমে সাধারণ ডিফল্ট।
যদি $R+W \le N$ হয় (যেমন N=3-এ W=1,R=1), তাহলে read-সেট ও write-সেট ওভারল্যাপ নাও করতে পারে — একটি read সর্বশেষ লেখা স্পর্শ না করা রেপ্লিকা(গুলো)-তেই গিয়ে পড়তে পারে এবং একটি পুরনো (stale) মান ফেরত দিতে পারে। এটি দুর্বল, eventual consistency (L04) — বিনিময়ে সর্বনিম্ন লেটেন্সি ও সর্বোচ্চ availability পাওয়া যায়, যখন কোনো রেপ্লিকা ডাউন থাকলেও লেখা/পড়া চালিয়ে যাওয়া যায়।
Write (W=2) Replica 1 write ✓ Replica 2 write ✓ / read ✓ Replica 3 read ✓ Read (R=2)
N=3, W=2 (Replica 1,2 তে লেখা), R=2 (Replica 2,3 থেকে পড়া) — Replica 2 উভয় সেটে থাকায় ওভারল্যাপ নিশ্চিত, তাই read সবসময় সর্বশেষ মান পাবে।

৪ · Python দিয়ে যাচাই করা

নিচের কোডে N=3 রেপ্লিকা সিমুলেট করা হয়েছে, প্রতিটি লেখায় একটি ইনক্রিমেন্টিং ভার্সন কাউন্টার যুক্ত। প্রথমে W=2,R=2 দিয়ে একাধিক লেখা ও পড়া চালিয়ে দেখানো হয়েছে read সবসময় সর্বশেষ মান পায় (এমনকি read-সেট প্রতিবার ভিন্ন হলেও) — তারপর W=1,R=1 দিয়ে একটি সুনির্দিষ্ট (deterministic) দৃশ্য বানানো হয়েছে, যেখানে read ইচ্ছাকৃতভাবে সর্বশেষ লেখা স্পর্শ না করা রেপ্লিকাতেই যায়, ফলে stale মান পাওয়া যায়।

Python
version_counter = 0

def write(replicas, key, value, replica_ids):
    global version_counter
    version_counter += 1
    for r in replica_ids:
        replicas[r][key] = (value, version_counter)
    return version_counter

def read(replicas, key, replica_ids):
    candidates = [replicas[r][key] for r in replica_ids if key in replicas[r]]
    if not candidates:
        return None
    # সর্বোচ্চ ভার্সনের মানটিই সর্বশেষ লেখা
    return max(candidates, key=lambda pair: pair[1])


print("=== W=2, R=2  (R+W=4 > N=3) ===")
replicas = [{} for _ in range(3)]
version_counter = 0
write_sets = [[0, 1], [1, 2], [0, 2], [0, 1], [1, 2]]
read_sets  = [[0, 2], [0, 1], [1, 2], [0, 2], [0, 1]]

all_matched = True
for i, (ws, rs) in enumerate(zip(write_sets, read_sets), start=1):
    latest_value = f"v{i}"
    ver = write(replicas, "x", latest_value, ws)
    value, seen_ver = read(replicas, "x", rs)
    matched = (value == latest_value)
    all_matched = all_matched and matched
    print(f"write#{i} -> {latest_value} (replicas {ws}, ver {ver}) | "
          f"read (replicas {rs}) -> {value} (ver {seen_ver}) | latest মিলছে: {matched}")

print("সব read সর্বশেষ মান পেয়েছে:", all_matched)

print()
print("=== W=1, R=1  (R+W=2 <= N=3) ===")
replicas = [{} for _ in range(3)]
version_counter = 0
write_sets2 = [[0], [1], [2]]
read_sets2  = [[0], [0], [0]]   # read ইচ্ছাকৃতভাবে সবসময় replica 0

for i, (ws, rs) in enumerate(zip(write_sets2, read_sets2), start=1):
    latest_value = f"v{i}"
    ver = write(replicas, "x", latest_value, ws)
    value, seen_ver = read(replicas, "x", rs)
    matched = (value == latest_value)
    print(f"write#{i} -> {latest_value} (replica {ws}, ver {ver}) | "
          f"read (replica {rs}) -> {value} (ver {seen_ver}) | latest মিলছে: {matched}")

    
কোডটি কী প্রমাণ করছে

W=2,R=2 ব্লকে প্রতিটি write ও read সেট ভিন্ন ২টি রেপ্লিকা বেছে নেয়, তবু প্রতিবার read সর্বশেষ লেখা মিলিয়ে দেয় — কারণ N=3-এ যেকোনো দুটি ২-সদস্যের সেট অন্তত একটি রেপ্লিকায় ওভারল্যাপ করতে বাধ্য (pigeonhole)। W=1,R=1 ব্লকে read সবসময় replica 0 থেকে হয়, কিন্তু ২য় ও ৩য় লেখা যায় replica 1 ও replica 2-তে — তাই ২য় ও ৩য় read replica 0-এর পুরনো মান (v1) ফেরত দেয়, latest মিলছে না। এই সুনির্দিষ্ট, পুনরাবৃত্তিযোগ্য দৃশ্যই দেখায় কেন $R+W \le N$ কনফিগারেশনে stale read সম্ভব — এলোমেলো ঘটনা নয়, বরং একটি কাঠামোগত ঝুঁকি।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ N=5 রেপ্লিকা থাকলে, strong consistency ($R+W>N$) বজায় রেখে সবচেয়ে কম মোট (W+R) লেটেন্সি পেতে কোন কম্বিনেশনটি সবচেয়ে ভালো, এবং কেন?

W=3,R=3 ($R+W=6>5$) — এটি সবচেয়ে ব্যালেন্সড কম্বিনেশন যা শর্তটি সবচেয়ে কম মোট রিসোর্স দিয়ে পূরণ করে ($\lceil (N+1)/2 \rceil$ প্রতিটির জন্য)। W=5,R=1 বা W=1,R=5-এর মতো এক্সট্রিম কম্বিনেশনও শর্ত পূরণ করে, কিন্তু সেগুলোতে একটি অপারেশন (write বা read) সব ৫টি রেপ্লিকার ওপর নির্ভরশীল হয়ে পড়ে — একটি রেপ্লিকা ডাউন হলেই পুরো অপারেশন ব্যর্থ হবে। W=3,R=3 প্রতিটি অপারেশনকে কিছুটা রেপ্লিকা ডাউন থাকা অবস্থাতেও (৫টির মধ্যে ২টি ডাউন হলেও) কাজ চালিয়ে যাওয়ার সুযোগ দেয়, তাই এটি সবচেয়ে ব্যবহারিক পছন্দ।

প্র ০২ একটি রেপ্লিকা সাময়িকভাবে ডাউন থাকলে W বা R কোরাম কীভাবে প্রভাবিত হয়, এবং সিস্টেম কীভাবে এটি সামলায়?

যতক্ষণ পর্যাপ্ত রেপ্লিকা (কমপক্ষে W-টি লেখার জন্য, R-টি পড়ার জন্য) জীবিত থাকে, ততক্ষণ অপারেশন স্বাভাবিকভাবে চলতে পারে — একটি ডাউন রেপ্লিকা বাদ দিয়ে বাকিদের মধ্যে থেকে কোরাম গঠন করা হয়। যদি জীবিত রেপ্লিকার সংখ্যা W বা R-এর নিচে নেমে যায়, তাহলে সংশ্লিষ্ট অপারেশন ব্যর্থ হয় (অথবা সিস্টেম temporarily একটি দুর্বল কোরামে "হিন্টেড হ্যান্ডঅফ"-এর মতো কৌশলে চালিয়ে যায়, পরে রেপ্লিকা ফিরে এলে সিঙ্ক করে নেয়) — এটি সরাসরি availability বনাম consistency-এর CAP (L04) ট্রেড-অফ প্রতিফলিত করে।

প্র ০৩ কোরাম রিড/রাইট কি নিশ্চিত করে যে দুটি concurrent লেখা কখনো একে অপরকে ওভাররাইট করবে না?

না — কোরাম শুধু নিশ্চিত করে read-সেট ও write-সেট ওভারল্যাপ করবে, তাই সর্বশেষ ভার্সন খুঁজে পাওয়া যাবে। কিন্তু যদি দুটি লেখা প্রায় একই সময়ে, একে অপরের সম্পর্কে না জেনে ঘটে (concurrent, L30-এর ভাষায়), তাহলে "কোনটি সর্বশেষ" এই প্রশ্নটাই দ্ব্যর্থহীন নয়। এই কারণেই Dynamo-স্টাইল সিস্টেম প্রায়ই কোরাম রিড/রাইটের পাশাপাশি Vector Clock (L30) ব্যবহার করে সত্যিকারের concurrent conflict শনাক্ত করতে — শুধু টাইমস্ট্যাম্প/ভার্সন তুলনা সবসময় যথেষ্ট নয়।

অনুশীলন

  1. কোড বদলান: উপরের W=1,R=1 ব্লকে read_sets2-কে [[0],[1],[2]]-এ বদলে দিন (read সবসময় সবচেয়ে সাম্প্রতিক লেখার রেপ্লিকা অনুসরণ করবে)। প্রতিটি read এখন latest মিলবে কিনা আগে অনুমান করুন, তারপর চালিয়ে যাচাই করুন।

    এবার প্রতিটি read ঠিক সেই রেপ্লিকা থেকেই পড়বে যেখানে সেই মুহূর্তের সর্বশেষ লেখাটি গিয়েছিল (write#1→replica0, read replica0; write#2→replica1, read replica1; ইত্যাদি) — তাই W=1,R=1 হওয়া সত্ত্বেও প্রতিটি read latest মিলে যাবে। এটি গুরুত্বপূর্ণ একটি শিক্ষা — $R+W \le N$ মানে stale read অবশ্যম্ভাবী নয়, বরং সম্ভাব্য (guaranteed নয়) — কোন রেপ্লিকা কোনটি বেছে নেয় তার ওপর নির্ভর করে ফলাফল বদলে যেতে পারে, যেখানে $R+W>N$-এ ফলাফল সবসময় নিশ্চিত।

  2. হিসাব করুন: N=7 রেপ্লিকার একটি সিস্টেমে strong consistency ($R+W>N$) বজায় রাখতে চাইলে সর্বনিম্ন কত হতে হবে W ও R যদি দুটোই সমান রাখতে চান?

    W=R=4 হলে $R+W=8>7$ শর্ত পূরণ হয় এবং এটিই সর্বনিম্ন সমান মান (W=R=3 হলে $3+3=6 \le 7$, শর্ত ব্যর্থ হয়)। সাধারণ সূত্র: $W=R=\lceil (N+1)/2 \rceil$ — অর্থাৎ মোট রেপ্লিকার সংখ্যার সামান্য বেশি অর্ধেক, যা "মেজরিটি কোরাম" নামেও পরিচিত এবং L36-এর leader election-এর মেজরিটি-ভোট ধারণার সাথেও সরাসরি সংযুক্ত।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
ভেক্টর ক্লক ও কজালিটি ট্র্যাকিং