পেস্ট সার্ভিস ডিজাইন
সমস্যাটি বোঝা (Understanding the Problem)
পেস্টবিন (Pastebin), গিটহাব গিস্ট (GitHub Gist) এবং হেস্টবিনের (Hastebin) মতো পেস্ট সার্ভিসগুলো ইউজারদের যেকোনো টেক্সট স্নিপেট (snippet) সেভ করা এবং একটি ছোট ইউআরএল বা শর্ট লিঙ্কের (Short URL) মাধ্যমে সেগুলো শেয়ার করার সুযোগ দেয়। চলুন শুরু থেকে এমন একটি সিস্টেম ডিজাইন করি।
প্রথমে, প্রয়োজনীয়তাগুলো পরিষ্কার করে নেওয়া যাক:
ফাংশনাল প্রয়োজনীয়তা (Functional Requirements):
- ইউজাররা টেক্সট জমা দিয়ে একটি পেস্ট (Paste) তৈরি করতে পারবেন এবং একটি ইউনিক URL পাবেন।
- ইউজাররা ইউনিক URL-টি ভিজিট করে পুরোনো কোনো পেস্ট রিট্রিভ (Retrieve) করতে পারবেন।
- পেস্টের একটি এক্সপায়ারেশন টাইম বা মেয়াদ (Expiration Time) সেট করা থাকতে পারে (যেমন: ১০ মিনিট, ১ ঘণ্টা, বা অনন্তকাল)।
- ঐচ্ছিক (Optional): জনপ্রিয় প্রোগ্রামিং ল্যাঙ্গুয়েজের জন্য সিনট্যাক্স হাইলাইটিং (Syntax highlighting)।
- ঐচ্ছিক (Optional): কাস্টম শর্ট URL বা এলিয়াস (Aliases)।
নন-ফাংশনাল প্রয়োজনীয়তা (Non-Functional Requirements):
- রিড-হেভি (Read-heavy): লেখার চেয়ে পড়ার পরিমাণ বেশি। আনুমানিক রেশিও ~৫:১।
- নিম্ন ল্যাটেন্সি (Low latency): পেস্ট রিট্রিভাল দ্রুত হওয়া উচিত (ক্যাশ করা কনটেন্টের জন্য < ৫০ মিলিসেকেন্ড)।
- উচ্চ প্রাপ্যতা (High availability): সার্ভিসটি অত্যন্ত এভেইলেবল হতে হবে — শেয়ার করা লিঙ্কগুলো সব সময় কাজ করতে হবে।
- ডিউরাবিলিটি (Durability): একবার তৈরি করা হলে, মেয়াদ শেষ না হওয়া পর্যন্ত কোনো পেস্ট হারানো যাবে না।
- সাইজ লিমিট (Size limits): অযাচিত ব্যবহার বা অ্যাবিউস (Abuse) রোধ করতে প্রতিটি পেস্ট প্রায় ১০ MB পর্যন্ত সীমাবদ্ধ রাখা যেতে পারে।
অ্যাস্টিমেশন (Estimation)
চলুন এই সিস্টেমের সাইজ বা ক্যাপাসিটি পরিমাপ করি:
- মাসে ৫ মিলিয়ন নতুন পেস্ট (রাইট বা Write): ~২ পেস্ট/সেকেন্ড
- রিড:রাইট (Read:Write) রেশিও ৫:১: ~১০ রিড/সেকেন্ড, পিকে (Peak) ~৩০/সেকেন্ড
- গড় পেস্ট সাইজ: ~১০ KB (বেশিরভাগ পেস্টই ছোট কোড স্নিপেট বা কনফিগ ফাইল)
- প্রতি মাসে স্টোরেজ: ৫ মিলিয়ন × ১০ KB = ৫০ GB/মাস
- ৫ বছরের স্টোরেজ: ৫০ GB × ৬০ মাস = ৩ TB
- প্রতি পেস্ট মেটাডেটা (Metadata): ~৫০০ বাইটস (কী (Key), টাইটেল (Title), ভাষা, টাইমস্ট্যাম্প, ব্যবহারকারীর আইডি)
- মেটাডেটা স্টোরেজ (৫ বছর): ৩০০ মিলিয়ন পেস্ট × ৫০০ বাইটস = ১৫০ GB
- ক্যাশ (Cache): সবচেয়ে জনপ্রিয় (হট) ২০% পেস্ট ক্যাশ করা হবে। ২০% × ৩ TB = ~৬০০ GB (অথবা শুধুমাত্র মেটাডেটা ক্যাশ করা হলে: ~৩০ GB)
পেস্টের কনটেন্ট বা লেখার মূল অংশ স্টোর করাটাই সবচেয়ে বড় খরচ। একটি রিলেশনাল ডেটাবেসের চেয়ে একটি অবজেক্ট স্টোরেজে (যেমন: S3) এটি সংরক্ষণ করা অনেক সাশ্রয়ী। মেটাডেটাগুলো একটি ট্র্যাডিশনাল ডেটাবেসেই থাকবে।
এপিআই ডিজাইন (API Design)
একটি সিম্পল REST API:
ক্রিয়েট পেস্ট (Create Paste)
| Endpoint | POST /api/v1/pastes |
| Request | {"content": "print('hello')", "title": "My Snippet", "language": "python", "expires_in": 3600} |
| Response | {"key": "abc123", "url": "https://paste.ly/abc123", "created_at": "...", "expires_at": "..."} |
| Status | 201 Created |
গেট পেস্ট (Get Paste)
| Endpoint | GET /api/v1/pastes/:key |
| Response | {"key": "abc123", "content": "print('hello')", "title": "My Snippet", "language": "python", "created_at": "...", "expires_at": "...", "views": 42} |
| Status | 200 OK অথবা 404 Not Found (মেয়াদ শেষ বা মুছে ফেলা হলে) |
কনটেন্ট এবং মেটাডেটার বিভাজন (Content vs Metadata separation): API মেটাডেটা এবং কনটেন্ট উভয়ই একসাথে রিটার্ন করে, কিন্তু ইন্টারনাল স্তরে এগুলো আলাদাভাবে সংরক্ষিত হয়। দ্রুত কোয়েরির জন্য মেটাডেটা (নাম, ভাষা, টাইমস্ট্যাম্প) ডেটাবেসে অবস্থান করে। আর সাশ্রয়ী ও টেকসই স্টোরেজের জন্য আসল পেস্ট কনটেন্ট অবজেক্ট স্টোরে (যেমন: S3) অবস্থান করে।
স্টোরেজ ডিজাইন (Storage Design)
মূল বিষয়: কনটেন্ট থেকে মেটাডেটাকে আলাদা রাখুন।
মেটাডেটা (SQL বা NoSQL ডেটাবেস):
paste_key(VARCHAR 8, PRIMARY KEY) — অনন্য ছোট কী (Unique short key)title(VARCHAR 255) — ঐচ্ছিক শিরোনামlanguage(VARCHAR 32) — সিনট্যাক্স হাইলাইটিংয়ের জন্য ভাষাs3_path(VARCHAR 255) — অবজেক্ট স্টোরে কনটেন্টের পাথcontent_size(INT) — বাইট হিসেবে আকারuser_id(BIGINT) — পেস্ট ক্রিয়েটর (যাদের একাউন্ট নেই তাদের জন্য null)created_at(TIMESTAMP)expires_at(TIMESTAMP, nullable) — Null মানে কখনও মেয়াদ শেষ হবে fixview_count(BIGINT) — কতবার পড়া হয়েছে তার সংখ্যা
কনটেন্ট (Object Store — S3, GCS, বা MinIO):
paste_keyদ্বারা প্লেইন টেক্সট ফাইল হিসেবে সংরক্ষিত হয়- অবজেক্ট স্টোরগুলো বড় আকারের (Large) ব্লব (BLOB) স্টোরেজের জন্য অসাধারন পারফর্ম করে — যা ডেটাবেসের চেয়ে প্রতি গিগাবাইটে (GB) অনেক বেশি সাশ্রয়ী
- ইন-বিল্ট (Built-in) ডিউরাবিলিটি (S3 এর জন্য ১১ নাইনস), রিপ্লিকেশন এবং CDN ইন্টিগ্রেশন পাওয়া যায়
কেন ডেটাবেসে কনটেন্ট স্টোর করা হবে না? গড় ১০ KB পেস্ট সাইজ এবং ৩০০ মিলিয়ন পেস্টের জন্য ৫ বছরে আপনার ডেটাবেসে ৩ TB ব্লব ডেটা জমা হবে। এটি ডেটাবেসকে বিশাল ও ভারি করে তোলে, ব্যাকআপকে ধীর করে এবং অবজেক্ট স্টোরেজের চেয়ে এটি ১০-৫০ গুণ বেশি ব্যয়বহুল। ডেটাবেস শুধু ছোট আকারের স্ট্রাকচারড মেটাডেটা পরিচালনা করা উচিত।
এক্সপায়ারেশন এবং ক্লিনআপ (Expiration & Cleanup)
মেয়াদ শেষ বা এক্সপায়ারিং (Expiration): যখন expires_in হিসেবে একটি টাইম দেয়া হয়, তখন একটি পেস্ট তৈরি হয়, যা expires_at সময় ক্যালকুলেট করে মেটাডেটাতে স্টোর করে রাখে। যখন ইউজার পেস্টের জন্য রিকোয়েস্ট পাঠায় (রিড করার জন্য), তখন এপিআই (API) প্রথমে expires_at চেক করে। যদি মেয়াদ উত্তীর্ণ হয়ে যায়, তবে এটি ৪০৪ (404) রিটার্ন করে। এটি একটি অলস ডিলিশন (Lazy deletion) পদ্ধতি।
অ্যাক্টিভ ক্লিনআপ (Active cleanup): নির্দিষ্ট সময়ে (যেমন, প্রতি ঘণ্টায়) একটি অ্যাসিনক্রোনাস ক্লিনআপ ওয়ার্কার (Async cleanup worker) চালান। এটি ডেটাবেসে মেয়াদ উত্তীর্ণ পেস্টগুলো খুঁজে বের করে, S3 থেকে তাদের কনটেন্ট মুছে ফেলে এবং মেটাডেটা সারিগুলোও ডেটাবেস থেকে সরিয়ে ফেলার ব্যবস্থা করে। মুছে ফেলা পেস্টের কী (Key) পরবর্তীতে পুনর্গঠনের জন্য Key Generation Service (KGS)-এ পাঠানো যেতে পারে।
রেট লিমিটিং (Rate Limiting): অপব্যবহার বা অ্যাবিউজ রোধ করার জন্য একটি আইপি-ভিত্তিক এবং ইউজার-ভিত্তিক রেট লিমিট প্রয়োগ করুন। এর জন্য টোকেন বাকেট (Token bucket) বা স্লাইডিং উইন্ডো (Sliding window) ব্যবহার করা যেতে পারে। সাধারণ লিমিট: রেজিস্টারড ইউজারদের জন্য মিনিটে ৬০টি পেস্ট, বেনামী (অ্যাকাউন্ট নেই এমন) ইউজারদের জন্য মিনিটে ১০টি পেস্ট। এছাড়া, এপিআই গেটওয়ে (API gateway)-তে পেস্টের ফাইলের সাইজ যেমন ১০ MB পর্যন্ত সীমাবদ্ধ করার ব্যবস্থা করতে হবে।