𝗔 𝗦𝘂𝗰𝗰𝗲𝘀𝘀𝗳𝘂𝗹 𝗣𝗮𝘆𝗺𝗲𝗻𝘁 𝗧𝗵𝗮𝘁 𝗡𝗲𝘃𝗲𝗿 𝗕𝗲𝗰𝗮𝗺𝗲 𝗮 𝗕𝗼𝗼𝗸𝗶𝗻𝗴
একজন গ্রাহক পেমেন্ট করেছেন। Razorpay সফল দেখিয়েছে। Webhook একটি HTTP 200 পাঠিয়েছে। পেমেন্টটি ক্যাপচার করা হয়েছিল।
তবুও বুকিংটি "confirming" অবস্থায় আটকে ছিল।
কোনো ত্রুটি দেখা দেয়নি। কোডে কোনো exception আসেনি। কোনো অ্যালার্ট আসেনি। প্রতিটি মেট্রিক একটি সুস্থ সিস্টেম নির্দেশ করছিল।
কিন্তু গ্রাহক কিছুই পাননি। ক্রিয়েটরের কোনো বুকিং ছিল না।
টাকা গ্রহণ করা সহজ। প্রতিটি পেমেন্ট যেন একটি বুকিংয়ে রূপান্তরিত হয় তা নিশ্চিত করাই হলো আসল চ্যালেঞ্জ।
বেশিরভাগ টিউটোরিয়াল এই ফ্লোটি অনুসরণ করতে বলে:
- Webhook ইভেন্ট গ্রহণ করে
- Webhook বুকিং আপডেট করে
এটি বিপজ্জনক। যদি বিজনেস লজিক Webhook-এর ভেতরে থাকে, তবে আপনি সম্পূর্ণভাবে ডেলিভারি সফল হওয়ার ওপর নির্ভর করেন। Webhook-এর ক্ষেত্রে রিট্রাই (retries), ডুপ্লিকেট (duplicates) এবং আংশিক ব্যর্থতার (partial failures) মতো সমস্যা হতে পারে।
আমরা এই কাজগুলোকে আলাদা করার জন্য আমাদের আর্কিটেকচার পরিবর্তন করেছি। Webhook এখন শুধুমাত্র ইভেন্ট রেকর্ড করে। তারা কোনো বিজনেস লজিক সম্পাদন করে না।
আমরা তিনটি টেবিল বিশিষ্ট একটি ইভেন্ট লেজার (event ledger) চালু করেছি:
- payment_orders: প্রোভাইডারের সত্যতা (The provider truth)
- payment_events: অপরিবর্তনীয় ইভেন্ট লেজার (The immutable event ledger)
- bookings: বিজনেসের সত্যতা (The business truth)
Webhook-এর এখন একটিই কাজ:
- সিগনেচার যাচাই করা
- ইভেন্ট সংরক্ষণ করা
- 200 রিটার্ন করা
এটি সিস্টেমকে সুরক্ষিত রাখে। যদি Webhook ব্যর্থ হয়, তবুও ইভেন্টটি নিরাপদ থাকে।
আমরা আরও শিখেছি যে পেমেন্ট স্টেট এবং বুকিং স্টেট আলাদা। একটি ক্যাপচার করা পেমেন্ট হলো ইনপুট। একটি নিশ্চিত বুকিং হলো ফলাফল। এদের আলাদা রাখলে রিকনসিলিয়েশন (reconciliation) করা সম্ভব হয়।
তদন্ত চলাকালীন আমরা একটি বাগ (bug) খুঁজে পেয়েছি। ইভেন্টগুলো ডাটাবেসে ছিল। প্রসেসরটি ঠিকঠাক কাজ করছিল। Webhook ঠিকঠাক কাজ করছিল।
কিন্তু প্রসেসরটি কখনোই চলেনি। পেন্ডিং ইভেন্টগুলো প্রসেস করার জন্য ফাংশনটিকে কেউ ট্রিগার করছিল না।
ইনজেশন (ingestion) এবং প্রসেসিং (processing)-কে আলাদা করা একটি ভালো ডিজাইন। কিন্তু এটি একটি নতুন প্রয়োজনীয়তা তৈরি করে: প্রসেসিং শুরু করার জন্য কিছু একটা ট্রিগার করতে হবে।
আমরা বেশ কিছু জব চালানোর জন্য একটি শিডিউলার (scheduler) বাস্তবায়ন করেছি:
- পেমেন্ট ইভেন্ট প্রসেস করা
- মিস হওয়া Webhook পুনরুদ্ধার করা
- সিস্টেমের কনসিস্টেন্সি যাচাই করা
রিট্রাই করার সময় ত্রুটি এড়াতে আমরা এই লজিকটি ব্যবহার করি:
- প্রসেস না হওয়া ইভেন্টগুলো নির্বাচন করা
- একাধিক ওয়ার্কারকে (workers) অনুমতি দিতে "SKIP LOCKED" ব্যবহার করা
- ডুপ্লিকেট ডেলিভারি যেন কোনো পরিবর্তন না করে তা নিশ্চিত করা
যে সিস্টেম শুধুমাত্র প্রতিটি Webhook সঠিক সময়ে পৌঁছালে কাজ করে, সেটি একটি ভঙ্গুর (fragile) সিস্টেম। যদি আপনার কিউ (queue) খালি করার মতো কেউ না থাকে, তবে কাজ চিরকাল অপেক্ষায় থাকবে।
নির্ভরযোগ্যতা মানে হলো ব্যর্থতার কথা মাথায় রেখে সিস্টেম তৈরি করা।