পাঠ ৫০ · ৫১-এর মধ্যে · মডিউল ১১
Home / Courses / System Design / ডিস্ট্রিবিউটেড ফাইল স্টোরেজ

কেস স্টাডি: ডিস্ট্রিবিউটেড ফাইল স্টোরেজ ডিজাইন করা

Case study: designing distributed file storage
১১ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

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

  • কেন বড় ফাইল চাংকে ভেঙে বিতরণ করা হয়, এবং এতে সমান্তরাল আপলোড/ডাউনলোডের সুবিধা কীভাবে আসে
  • রেপ্লিকেশনের (L16) মাধ্যমে ডিউরেবিলিটি এবং মেটাডেটা সার্ভিসের ভূমিকা
  • ইরেজার কোডিং কীভাবে রেপ্লিকেশনের একটি স্টোরেজ-সাশ্রয়ী বিকল্প (উচ্চ-স্তরে, কোনো এনকোডিং গণিত ছাড়া)
  • Python দিয়ে চাংকিং + রেপ্লিকেশন বাস্তবায়ন করে দেখানো — একটি রেপ্লিকা ডাউন থাকলেও ফাইল সম্পূর্ণ পুনর্গঠিত হয়

১ · রিকোয়ারমেন্ট

ফাংশনাল
যেকোনো আকারের ফাইল/অবজেক্ট নির্ভরযোগ্যভাবে সংরক্ষণ ও পুনরুদ্ধার করা ("S3-এর মতো")।
নন-ফাংশনাল
চরম ডিউরেবিলিটি (ডেটা কখনো হারানো যাবে না), বিশাল স্কেল, উচ্চ অ্যাভেইলেবিলিটি।

২ · ডিজাইন — চাংকিং ও রেপ্লিকেশন

বড় ফাইল (কল্পনা করুন কয়েক গিগাবাইট) একক নোডে সংরক্ষণ করলে সেই নোডই একটি সিঙ্গেল পয়েন্ট অফ ফেইলিওর হয়ে যায়, এবং সম্পূর্ণ ফাইল একটি ডিস্কে আঁটতেও নাও পারে। সমাধান — ফাইলকে ফিক্সড-সাইজ চাংকChunkএকটি বড় ফাইলকে ভাগ করা ফিক্সড-সাইজ অংশ (যেমন ৬৪ MB) — স্বাধীনভাবে বিভিন্ন স্টোরেজ নোডে বিতরণ ও রেপ্লিকেট করা যায়। -এ (বাস্তবে যেমন ৬৪ MB) ভেঙে অনেকগুলো স্টোরেজ নোডে বিতরণ করা — এতে সমান্তরাল আপলোড/ডাউনলোড সম্ভব হয় এবং কোনো একক নোডের সাইজ-সীমা সমস্যা হয় না।

ডিউরেবিলিটির জন্য প্রতিটি চাংক একাধিক (সাধারণত ৩টি) নোডে রেপ্লিকেট করা হয় (L16-এর রেপ্লিকেশন নীতি), আদর্শভাবে ভিন্ন ফেইলিওর ডোমেইনে (ভিন্ন র‍্যাক/ডেটা সেন্টার) — যাতে একটি নোড, র‍্যাক বা এমনকি পুরো ডেটা সেন্টার হারালেও কোনো চাংক সম্পূর্ণভাবে হারিয়ে না যায়। একটি আলাদা, ছোট মেটাডেটা সার্ভিস ট্র্যাক রাখে কোন ফাইলের কোন চাংক কোন কোন নোডে আছে (GFS-এর NameNode বা HDFS-এর মেটাডেটা-সার্ভারের মতো) — এই মেটাডেটা নিজেই তুলনামূলক ছোট, তাই আলাদাভাবে স্কেল করা হয়।

ক্লায়েন্ট Client মেটাডেটা সার্ভিস Metadata Service চাংক স্প্লিটার Chunk + Replicate নোড A (রেপ্লিকা) নোড B (রেপ্লিকা) নোড C (রেপ্লিকা)
মেটাডেটা সার্ভিস জানে কোন চাংক কোন নোডে আছে; প্রতিটি চাংক ৩টি ভিন্ন নোডে রেপ্লিকেটেড।

৩ · ডিপ ডাইভ — চাংকিং, রেপ্লিকেশন ও পুনর্গঠন

নিচের কোড একটি ফাইল (একটি স্ট্রিং, বাইট ডেটার প্রতিনিধিত্ব হিসেবে) ফিক্সড-সাইজ চাংকে ভাগ করে, প্রতিটি চাংক একটি সিমুলেটেড ৬-নোড পুলের ৩টি (ডিটারমিনিস্টিক, র‍্যান্ডম নয়) নোডে বরাদ্দ করে, এবং একটি চাংকের একটি রেপ্লিকা "ডাউন" থাকা অবস্থায়ও reconstruct() ফাইলটি সঠিকভাবে পুনর্গঠন করে দেখায়।

Python
CHUNK_SIZE = 12
NUM_REPLICAS = 3
node_pool = [f"node-{i}" for i in range(6)]   # ৬টি সিমুলেটেড স্টোরেজ নোড

original_file = "ABCL-TECH-DISTRIBUTED-FILE-STORAGE-DEMO-0123456789-END"
chunks = [original_file[i:i + CHUNK_SIZE] for i in range(0, len(original_file), CHUNK_SIZE)]

def replicas_for_chunk(chunk_index, num_replicas=NUM_REPLICAS):
    # ডিটারমিনিস্টিক অ্যাসাইনমেন্ট — একই চাংক-ইনডেক্স সবসময় একই নোডে যায়
    n = len(node_pool)
    return [node_pool[(chunk_index + offset) % n] for offset in range(num_replicas)]

chunk_assignment = {i: replicas_for_chunk(i) for i in range(len(chunks))}

# প্রতিটি নোডের স্টোরেজ সিমুলেট করা: node -> {chunk_index: chunk_data}
storage = {node: {} for node in node_pool}
for i, chunk in enumerate(chunks):
    for node in chunk_assignment[i]:
        storage[node][i] = chunk

print(f"মোট চাংক সংখ্যা: {len(chunks)}")
for i in range(len(chunks)):
    print(f"  চাংক {i} = {chunks[i]!r}  -> রেপ্লিকা: {chunk_assignment[i]}")

def reconstruct(down_node=None, down_chunk_index=None):
    parts = []
    for i in range(len(chunks)):
        data = None
        for node in chunk_assignment[i]:
            if node == down_node and i == down_chunk_index:
                continue   # এই নির্দিষ্ট চাংকের জন্য এই রেপ্লিকাটি "ডাউন"
            if i in storage[node]:
                data = storage[node][i]
                break
        parts.append(data)
    return "".join(parts)

# সিমুলেশন: চাংক ০-এর প্রথম রেপ্লিকা নোড ডাউন
failed_node = chunk_assignment[0][0]
reconstructed = reconstruct(down_node=failed_node, down_chunk_index=0)

print(f"\nচাংক ০-এর রেপ্লিকা '{failed_node}' ডাউন সিমুলেট করা হলো।")
print(f"মূল ফাইল       : {original_file!r}")
print(f"পুনর্গঠিত ফাইল : {reconstructed!r}")
print("পুনর্গঠন সঠিক:", reconstructed == original_file)
assert reconstructed == original_file, "পুনর্গঠন ব্যর্থ!"

    
কোডটি চালিয়ে দেখুন — চাংক ০-এর একটি রেপ্লিকা নোড ডাউন থাকা সত্ত্বেও, reconstruct() সেই চাংকের বাকি ২টি জীবিত রেপ্লিকার একটি থেকে ডেটা আনে, ফলে পুনর্গঠিত ফাইল মূল ফাইলের সাথে হুবহু মিলে যায়। এটিই রেপ্লিকেশনের মূল প্রতিশ্রুতি — যতক্ষণ প্রতিটি চাংকের অন্তত একটি রেপ্লিকা বেঁচে থাকে, ডেটা হারায় না।

৪ · ট্রেড-অফ

৩x রেপ্লিকেশন সরল ও দ্রুত পুনরুদ্ধার দেয়, কিন্তু স্টোরেজ খরচ আসল ডেটার ৩ গুণ। একটি বিকল্প — ইরেজার কোডিংErasure Codingডেটাকে রিডানডেন্সিসহ একাধিক ফ্র্যাগমেন্টে ভাগ করার কৌশল, যেখানে মূল ডেটা মোট ফ্র্যাগমেন্টের একটি উপসেট থেকেই পুনর্গঠন করা যায় — পূর্ণ রেপ্লিকেশনের তুলনায় কম স্টোরেজ ওভারহেডে একই রকম ডিউরেবিলিটি দেয়। — যা পূর্ণ রেপ্লিকেশনের কাছাকাছি ডিউরেবিলিটি অনেক কম স্টোরেজ ওভারহেডে দেয়, কিন্তু পুনর্গঠনের সময় বাড়তি কম্পিউটেশন লাগে (এনকোডিং গণিতে না গিয়ে শুধু ধারণা হিসেবে উল্লেখযোগ্য)। এছাড়া মেটাডেটা সার্ভিস নিজেই একটি গুরুত্বপূর্ণ সিঙ্গেল পয়েন্ট — এটি ছোট হলেও এর নিজস্ব হাই-অ্যাভেইলেবিলিটি ডিজাইন (L37-এর ফেইলওভার নীতি) দরকার, কারণ মেটাডেটা হারালে চাংক জীবিত থাকলেও কোন চাংক কোথায় তা জানা অসম্ভব হয়ে যায়।

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

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

প্র ০১ কোডে চাংক-নোড অ্যাসাইনমেন্ট ডিটারমিনিস্টিক (চাংক-ইনডেক্স-ভিত্তিক), সম্পূর্ণ র‍্যান্ডম নয়। বাস্তব সিস্টেমে এই ডিটারমিনিজম কেন গুরুত্বপূর্ণ?

একটি চাংক কোথায় আছে তা জানতে হলে মেটাডেটা সার্ভিসের নিজে থেকেই নির্ভরযোগ্যভাবে হিসাব করতে বা লুকআপ করতে পারা দরকার। সম্পূর্ণ র‍্যান্ডম অ্যাসাইনমেন্ট হলে প্রতিবার একই ফলাফল পুনরুৎপাদনযোগ্য হবে না, তাই প্রতিটি অ্যাসাইনমেন্ট স্পষ্টভাবে মেটাডেটাতে সংরক্ষণ করতে হতো। ডিটারমিনিস্টিক অ্যাসাইনমেন্ট (বা এমনকি L13-এর কনসিস্টেন্ট হ্যাশিং) ব্যবহার করলে অনেক ক্ষেত্রে অ্যাসাইনমেন্ট পুনরায় গণনা করা যায়, মেটাডেটা লুকআপের চাপ কমে যায়।

প্র ০২ যদি একই চাংকের ৩টি রেপ্লিকাই একসাথে ডাউন হয়ে যায় (উদাহরণস্বরূপ, তিনটিই একই ডেটা সেন্টারে ছিল এবং সেই ডেটা সেন্টার সম্পূর্ণ বিদ্যুৎ হারালো), কী ঘটবে — এবং কীভাবে এটি প্রতিরোধ করা যায়?

সেই চাংক স্থায়ীভাবে হারিয়ে যাবে এবং পুরো ফাইলটি পুনর্গঠন করা অসম্ভব হয়ে পড়বে — রেপ্লিকেশনের প্রতিশ্রুতি তখনই ভাঙে যখন সবকটি রেপ্লিকা একই "ফেইলিওর ডোমেইনে" থাকে এবং সেই ডোমেইনটাই ব্যর্থ হয়। প্রতিরোধের উপায় হলো রেপ্লিকা নির্বাচনের সময় ইচ্ছাকৃতভাবে ভিন্ন ফেইলিওর ডোমেইন (ভিন্ন র‍্যাক, ভিন্ন ডেটা সেন্টার, এমনকি ভিন্ন অঞ্চল) বেছে নেওয়া — যাতে একটি একক অবকাঠামোগত ব্যর্থতা কখনো একই চাংকের সবকটি কপি একসাথে না নেয়।

প্র ০৩ এই কোডে চাংকগুলো স্বাধীনভাবে রেপ্লিকেট করা হয়েছে। এই "চাংকিং + স্বাধীন বিতরণ" পদ্ধতি কীভাবে একটি ফাইল আপলোড/ডাউনলোডকে সমান্তরাল (parallel) করতে সাহায্য করে?

যেহেতু প্রতিটি চাংক স্বাধীন এবং সম্ভাব্যত ভিন্ন ভিন্ন নোডে থাকে, ক্লায়েন্ট একসাথে একাধিক চাংক আলাদা নোড থেকে সমান্তরালভাবে ডাউনলোড করতে পারে (একটির জন্য অপেক্ষা না করে) — মোট ডাউনলোড সময় প্রায় "সবচেয়ে ধীর একক চাংক আনার সময়"-এর কাছাকাছি নেমে আসে, পুরো ফাইল সিরিয়ালি আনার চেয়ে অনেক দ্রুত। আপলোডেও একই যুক্তি প্রযোজ্য — একাধিক চাংক সমান্তরালে বিভিন্ন নোডে পাঠানো যায়।

অনুশীলন

  1. পরিবর্তন করুন: কোড সেলে reconstruct() কল করার সময় down_chunk_index-কে এমন একটি মান দিন যা len(chunks)-এর সমান বা বেশি (যেমন ৯৯)। ফলাফলে কী পরিবর্তন হবে এবং কেন?

    কোনো পরিবর্তন হবে না — যেহেতু লুপে i == down_chunk_index শর্তটি কখনো সত্য হবে না (যেহেতু i সবসময় 0 থেকে len(chunks)-1 পর্যন্ত), কোনো রেপ্লিকাকেই "ডাউন" হিসেবে গণ্য করা হবে না, এবং প্রতিটি চাংক তার প্রথম রেপ্লিকা থেকেই স্বাভাবিকভাবে পাওয়া যাবে — পুনর্গঠন আগের মতোই সঠিক হবে।

  2. চিন্তা করুন: কোডে reconstruct() শুধু একটি চাংকের একটি রেপ্লিকা ডাউন সিমুলেট করে। যদি একই সাথে দুটি ভিন্ন চাংকের দুটি ভিন্ন রেপ্লিকা ডাউন থাকে (কিন্তু কোনো চাংকের সবকটি রেপ্লিকা নয়), তাহলে কি পুনর্গঠন এখনও সফল হবে? যুক্তি দিন।

    হ্যাঁ, সফল হবে — কারণ reconstruct()-এর লুপ প্রতিটি চাংকের জন্য স্বাধীনভাবে তার রেপ্লিকা তালিকা ক্রমানুসারে চেক করে এবং প্রথম উপলব্ধ (ডাউন নয়) রেপ্লিকা থেকে ডেটা নেয়। যতক্ষণ প্রতিটি পৃথক চাংকের অন্তত একটি রেপ্লিকা জীবিত থাকে — তা যত ভিন্ন চাংকেই ভিন্ন রেপ্লিকা ডাউন থাকুক না কেন — পুরো ফাইল সফলভাবে পুনর্গঠিত হবে। শুধুমাত্র একই চাংকের সবকটি রেপ্লিকা একসাথে ডাউন হলেই পুনর্গঠন ব্যর্থ হয়।

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

পূর্ববর্তী পাঠ
কেস স্টাডি: সার্চ অটোকমপ্লিট সিস্টেম ডিজাইন করা