Pat Helland-এর 'বাইরের বনাম ভেতরের' ডেটা ধারণা শিখুন—স্পষ্ট সীমানা, আইডেম্পোটেন্ট কল ডিজাইন করা এবং নেটওয়ার্ক ব্যর্থ হলে স্টেট মিলিয়ে নেওয়ার কৌশল।

যখন আপনি একটি অ্যাপ তৈরি করেন, সহজে ভাবা যায় যে অনুরোধগুলো সুন্দরভাবে, একটির পর একটা সঠিক ক্রমে আসবে। বাস্তব নেটওয়ার্কগুলো ওইভাবে আচরণ করে না। ব্যবহারকারী স্ক্রিন freeze হওয়ায় “Pay” দুবার ট্যাপ করতে পারে। একটি মোবাইল সংযোগ বোতাম চাপার ঠিক পরেই বিচ্ছিন্ন হয়ে যেতে পারে। একটি webhook দেরিতে আসতে পারে, বা দুবার আসতে পারে। কখনও কখনও সেটা একেবারেই আসে না।
Pat Helland-এর বাইরে বনাম ভিতরে ডেটা ধারণা এই বিশৃঙ্খলতার জন্য একটি পরিষ্কার চিন্তার রীতি দেয়।
"বাইরে" হল সেই সব কিছু যা আপনার সিস্টেম নিয়ন্ত্রণ করে না। এটি হলো যেখানে আপনি অন্য মানুষ ও সিস্টেমের সঙ্গে কথা বলেন, এবং যেখানে ডেলিভারির নির্ভরযোগ্যতা অনিশ্চিত: ব্রাউজার ও মোবাইল অ্যাপ থেকে HTTP রিকুয়েস্ট, কিউ থেকে মেসেজ, তৃতীয়‑পক্ষ webhook (পেমেন্ট, ইমেল, শিপিং), এবং ক্লায়েন্ট, প্রক্সি বা ব্যাকগ্রাউন্ড জব দ্বারা ট্রিগার হওয়া রিট্রাই।
বাইরে, ধরে নিন বার্তাগুলো বিলম্বিত হতে পারে, সদৃশ হতে পারে বা ভুল ক্রমে আসতে পারে। এমনকি যদি কিছু "সাধারণত নির্ভরযোগ্য" হয়, তবুও সেই দিনের জন্য ডিজাইন করুন যখন তা নির্ভরযোগ্য থাকবেনা।
"ভিতরে" হল যা আপনার সিস্টেমকে আপনি নির্ভরযোগ্য করতে পারেন। এটি সেই টেকসই স্টেট যা আপনি সংরক্ষণ করেন, আপনি যেসব নিয়ম প্রয়োগ করেন, এবং যেসব তথ্য পরে আপনি প্রমাণ করতে পারেন:
ভিতরে আপনি ইনভারিয়েন্টগুলো রক্ষা করেন। যদি আপনি "একটি অর্ডারের জন্য একবার পেমেন্ট" প্রতিশ্রুতি দেন, সেই প্রতিশ্রুতিটি ভিতরেই জোরদার করতে হবে, কারণ বাইরেকে বিশ্বাস করা যায় না।
মনোভাবের সরল পরিবর্তন হলো: নিখুঁত ডেলিভারি বা নিখুঁত টাইমিং ধরে নেবেন না। প্রতিটি বাইরের ইন্টারঅ্যাকশনকে একটি অনিশ্চিত পরামর্শ হিসেবে বিবেচনা করুন যা পুনরাবৃত্ত হতে পারে, এবং ভিতরকে নিরাপদভাবে সাড়া দিতে ডিজাইন করুন।
এটি ছোট টিম এবং সরল অ্যাপের ক্ষেত্রেও গুরুত্বপূর্ণ। প্রথমবার একটি নেটওয়ার্ক ত্রুটি ডুপ্লিকেট চার্জ বা আটকে থাকা অর্ডার তৈরি করলে সেটি তাত্ত্বিক না থেকে রিফান্ড, সাপোর্ট টিকিট এবং বিশ্বাসের ক্ষতিতে পরিণত হয়।
একটি বাস্তব উদাহরণ: ব্যবহারকারী "Place order" ট্যাপ করে, অ্যাপ একটি রিকুয়েস্ট পাঠায়, এবং সংযোগ বিচ্ছিন্ন হয়ে যায়। ব্যবহারকারী আবার চেষ্টা করে। যদি আপনার ভিতরে সেই চেষ্টা "একই চেষ্টা" চিহ্নিত করার উপায় না থাকে, আপনি দুটি অর্ডার তৈরি করতে পারেন, ইনভেন্টরি দ্বিগুণ রিজার্ভ করতে পারেন, বা দুবার কনফার্মেশন ইমেল পাঠাতে পারেন।
Helland-এর দৃষ্টান্ত সোজা: বাইরের বিশ্ব অনিশ্চিত, কিন্তু আপনার সিস্টেমের ভিতরকে ধারাবাহিক রাখতে হবে। নেটওয়ার্ক প্যাকেট হারায়, ফোন সিগন্যাল নষ্ট হয়, ক্লক ডিফট করে, এবং ব্যবহারকারী refresh ট্যাপে দেয়। আপনার অ্যাপ এসব কিছু নিয়ন্ত্রণ করতে পারে না। যা এটি নিয়ন্ত্রণ করতে পারে সেটা হলো স্পষ্ট সীমানা পেরিয়ে যখন ডেটা "সত্য" হিসেবে গ্রহণ করা হবে—সেই সিদ্ধান্ত।
ভাবুন কেউ খারাপ Wi‑Fi থাকা একটি ভবনে হাঁটতে হাঁটতে ফোনে কফি অর্ডার করছে। তারা "Pay" ট্যাপ করে। স্পিনার চলে। নেটওয়ার্ক কেটে যায়। তারা আবার ট্যাপ করে।
হয়তো প্রথম অনুরোধ আপনার সার্ভারে পৌঁছেছে, কিন্তু রেসপন্স ফিরতে পারেনি। অথবা হয়তো কোনো অনুরোধই পৌঁছায়নি। ব্যবহারকারীর দৃষ্টিকোণ থেকে উভয় সম্ভাবনাই একই দেখায়।
এটাই সময় এবং অনিশ্চয়তা: আপনি এখনও জানেন না কী হয়েছে, এবং পরে জানতে পারেন। আপনার সিস্টেমকে অপেক্ষাকালীনও সংবেদনশীলভাবে আচরণ করতে হবে।
একবার আপনি গ্রহণ করেন যে বাইরে অনির্ভরযোগ্য, কয়েকটি "অদ্ভুত" আচরণ স্বাভাবিক হয়ে ওঠে:
বাইরের ডেটা একটি দাবি; বাস্তবতা নয়। "আমি পেমেন্ট করেছি" কেবল একটি বিবৃতি যা অনির্ভরযোগ্য চ্যানেলে পাঠানো হয়েছে। এটি একটি বাস্তবতা হয় কেবলমাত্র আপনি তা ভিতরে টেকসই ও সঙ্গতিপূর্ণভাবে রেকর্ড করলে।
এটি আপনাকে তিনটি ব্যবহারিক অভ্যাসের দিকে ঠেলবে: স্পষ্ট সীমানা নির্ধারণ, রিট্রাইকেও নিরাপদ রাখতে আইডেম্পোটেন্সি, এবং বাস্তবতা মিলেনি এমন পরিস্থিতির জন্য রিকনসিলিয়েশন পরিকল্পনা।
"বাইরে বনাম ভিতরে" ধারণাটি একটি ব্যবহারিক প্রশ্ন দিয়ে শুরু করে: আপনার সিস্টেমের সত্য কোথায় শুরু এবং শেষ হয়?
সীমার ভেতরে আপনি শক্তিশালী গ্যারান্টি দিতে পারেন কারণ আপনি ডেটা ও নিয়মগুলো নিয়ন্ত্রণ করেন। সীমার বাইরে আপনি সেরা প্রয়াস করেন এবং ধরে নেবেন বার্তা হারিয়ে যেতে পারে, সদৃশ হতে পারে, বিলম্বিত হতে পারে বা ভুল ক্রমে আসতে পারে।
বাস্তব অ্যাপে সেই সীমা প্রায়ই নিম্ন জায়গাগুলোতে দেখা যায়:
একবার আপনি সেই লাইন অঙ্কন করলে, ঠিক করুন কোন ইনভারিয়েন্টগুলো অমোঘ ভিতরে। উদাহরণ:
সীমাটির জন্য "আমরা কোথায় আছি" পরিষ্কার ভাষা দরকার। অনেক ব্যর্থতা থাকে “আমরা আপনাকে শুনেছি” এবং “আমরা শেষ করেছি” এর শূন্যস্থানেই। একটি সহায়ক প্যাটার্ন হলো তিনটি আলাদা অর্থ আলাদা করা:
যখন টিমগুলো এটি বাদ দেয়, তারা লোডের সময় বা আংশিক আউটেজের সময় ঘটতো এমন বাগ পায়। একটি সিস্টেম “paid” বলতে অর্থ করতে পারে টাকা ক্যাপচার করা; অন্যটি বলতে পারে পেমেন্ট চেষ্টা শুরু হয়েছে। সেই অমিল ডুপ্লিকেট, আটকে থাকা অর্ডার এবং এমন সাপোর্ট টিকিকেট তৈরি করে যেগুলো কেউ পুনরুত্পাদন করতে পারে না।
আইডেম্পোটেন্সি মানে: একই অনুরোধ যদি দুবার পাঠানো হয়, সিস্টেম সেটিকে এক অনুরোধের মত বিবেচনা করে এবং একই আউটকাম রিটার্ন করে।
রিট্রাই স্বাভাবিক। টাইমআউট ঘটে। ক্লায়েন্ট নিজেকে পুনরাবৃত্তি করে। যদি বাইরে পুনরাবৃত্তি করতে পারে, আপনার ভিতর এটিকে স্থিতিশীল স্টেট পরিবর্তনে পরিণত করতে হবে।
সরল উদাহরণ: একটি মোবাইল অ্যাপ "pay $20" পাঠায় এবং সংযোগ শেষ হয়ে যায়। অ্যাপ রিট্রাই করে। আইডেম্পোটেন্সি ছাড়া গ্রাহক দুবার চার্জ হতে পারে। আইডেম্পোটেন্সি থাকলে দ্বিতীয় অনুরোধ প্রথম চার্জের ফলই ফেরত দেয়।
বেশিরভাগ টিম এই প্যাটার্নগুলোর এক বা মিশ্রণ ব্যবহার করে:
Idempotency-Key: ক্লায়েন্ট প্রতিটি উদ্দেশ্যের জন্য একটি অনন্য কী পাঠায় (উদাহরণ: Idempotency-Key: ...)। সার্ভার কীগুলো এবং চূড়ান্ত রেসপন্স রেকর্ড করে।(client_id, key) বা (order_id, operation) দিয়ে কী-ভিত্তিক একটি সারি সংরক্ষণ করে দ্বিতীয় পাশ-এফেক্ট অস্বীকার করা।যখন একটি ডুপ্লিকেট আসে, সাধারণত সেরা আচরণ হল "409 conflict" বা সাধারণ ত্রুটি না দেখিয়ে, একই রেসপন্স ফেরত দেওয়া — একই রিসোর্স ID এবং স্ট্যাটাস সহ। এটাই ক্লায়েন্ট এবং ব্যাকগ্রাউন্ড জবগুলোর জন্য রিট্রাইকে নিরাপদ করে।
আইডেম্পোটেন্সি রেকর্ড অবশ্যই আপনার সীমানার ভিতরে টেকসই স্টোরেজে থাকতে হবে, মেমরিতে নয়। যদি API রিস্টার্ট করে এবং ভুলে যায়, সুরক্ষা গ্যারান্টি উধাও হয়ে যাবে।
বাস্তব রিট্রাই এবং বিলম্বিত ডেলিভারির কভার করার জন্য রেকর্ড যথেষ্ট দীর্ঘ রাখুন। উইন্ডো ব্যবসায়িক ঝুঁকির উপর নির্ভর করে: কম-ঝুঁকির ক্রিয়াগুলোর জন্য মিনিট থেকে ঘন্টা, পেমেন্ট/ইমেল/শিপমেন্টের মতো খরচী ক্ষেত্রের জন্য দিনগুলো এবং যদি পার্টনার দীর্ঘসময় রিট্রাই করতে পারে তবে আরও বেশি।
বিতরণকৃত ট্রানজ্যাকশন আরামদায়ক শোনায়: সার্ভিস, কিউ এবং ডাটাবেস জুড়ে একটি বড় কমিট। বাস্তবে এগুলো প্রায়ই অনুপলব্ধ, ধীর বা অতিরিক্ত ভঙ্গুর হয়। একবার নেটওয়ার্ক হপ জড়িত হয়ে গেলে আপনি ধরতে পারবেন না সবকিছু একসাথে কমিট হবে।
একটি সাধারণ ফাঁদ হলো এমন একটি ওয়ার্কফ্লো তৈরি করা যা তখনই কাজ করে যদি প্রতিটি ধাপ তখনই সফল হয়: অর্ডার সেভ, কার্ড চার্জ, ইনভেন্টরি রিজার্ভ, কনফার্মেশন পাঠান। যদি ধাপ 3 টাইমআউট করে, তাহলে এটা ব্যর্থ না সফল—কী করবেন? যদি আপনি রিট্রাই করেন, কি আপনি ডাবল-চার্জ বা ডাবল-রিজার্ভ করবেন?
দুইটি ব্যবহারিক পদ্ধতি এড়াতে সহায়ক:
প্রতিটি ওয়ার্কফ্লোর জন্য একটি স্টাইল বেছে নিন এবং তার সাথে থাকুন। "কখনও কখনও আমরা আউটবক্স করি" আর "কখনও কখনও আমরা সিঙ্ক্রোনাস সাফল্য ধরে নিই" মিশালে এমন এজকেসগুলো তৈরি হয় যা টেস্ট করা কঠিন।
একটি সহজ নিয়ম: যদি আপনি সীমার ভেতরে অ্যাটোমিকালি কমিট করতে না পারেন, রিট্রাই, ডুপ্লিকেট এবং বিলম্বের জন্য ডিজাইন করুন।
রিকনসিলিয়েশন একটি মৌলিক সত্য স্বীকার করে: যখন আপনার অ্যাপ নেটওয়ার্কের মাধ্যমে অন্য সিস্টেমের সঙ্গে কথা বলে, তখন মাঝে মাঝে আপনি কী হয়েছে সে নিয়ে অসম্মতি থাকবে। রিকুয়েস্ট টাইমআউট করে, কলব্যাক দেরিতে আসে, এবং মানুষ অ্যাকশন পুনরায় করে। রিকনসিলিয়েশন হল আপনি কিভাবে মিলবৈষম্য শনাক্ত করে সময়ের সাথে তা ঠিক করবেন।
বাইরের সিস্টেমগুলোকে স্বাধীন সত্যের উৎস হিসেবে বিবেচনা করুন। আপনার অ্যাপের নিজের অভ্যন্তরীণ রেকর্ড আছে, কিন্তু এটি পার্টনার, প্রদানকারী এবং ব্যবহারকারীর সাথে মিলিয়ে তুলতে একটি উপায় চাই।
বেশিরভাগ টিম ব্যবহার করে কিছু সাধারণ কারণের টুল (সহজ হলেও ভালো): একটি worker যা পেন্ডিং অ্যাকশনগুলো পুনরায় ট্রাই করে এবং বহির্ভূত স্ট্যাটাস রি‑চেক করে, অনিয়মিততার জন্য শিডিউলড স্ক্যান, এবং সাপোর্টের জন্য ছোট একটি অ্যাডমিন রিপেয়ার অ্যাকশন (রিট্রাই, বাতিল, বা রিভিউ হিসেবে চিহ্নিত)।
রিকনসিলিয়েশন কাজ করবে যদি আপনি জানেন কি তুলনা করবেন: অভ্যন্তরীণ লেজার বনাম প্রদানকারী লেজার (পেমেন্ট), অর্ডার স্টেট বনাম শিপমেন্ট স্টেট (ফুলফিলমেন্ট), সাবস্ক্রিপশন স্টেট বনাম বিলিং স্টেট।
স্টেটগুলোকে রেপেয়ারযোগ্য রাখুন। সরাসরি “created” থেকে “completed” তে যাওয়ার পরিবর্তে হোল্ডিং স্টেট যেমন pending, on hold, বা needs review ব্যবহার করুন। এটি বলতে নিরাপদ করে "আমরা নিশ্চিত না" এবং রিকনসিলিয়েশনের একটি স্পষ্ট ল্যান্ডিং জায়গা দেয়।
গুরুত্বপূর্ণ পরিবর্তনগুলোর উপর একটি ছোট অডিট ট্রেইল ধরুন:
উদাহরণ: যদি আপনার অ্যাপ শিপমেন্ট লেবেল অনুরোধ করে এবং নেটওয়ার্ক কেটে যায়, আপনি অভ্যন্তরে "কোন লেবেল নেই" পেতে পারেন যখন ক্যারিয়ার বাস্তবে একটি লেবেল তৈরি করেছে। একটি recon worker correlation ID দিয়ে খুঁজে বের করতে পারে যে লেবেল আছে এবং অর্ডারকে এগিয়ে নিয়ে যেতে পারে (অথবা যদি ডিটেইল মেল না খায়, রিভিউর জন্য চিহ্নিত করবে)।
একবার আপনি ধরে নিলেন নেটওয়ার্ক ব্যর্থ হবে, লক্ষ্য বদলে যায়। প্রতিটি ধাপ একবারেই সফল করার চেষ্টা নয়; বরং প্রতিটি ধাপকে পুনরাবৃত্তি করা নিরাপদ ও মেরামত করা সহজ করা।
একটি এক-লাইন সীমানা বিবৃতি লিখুন। আপনি স্পষ্টভাবে কি আপনার সিস্টেম owns (অর্থাৎ truth), কি এটি mirrors করে, এবং কি শুধুমাত্র অন্যদের কাছে অনুরোধ করে তা উল্লেখ করুন।
সুখী পথ আগে নয়—ব্যর্থতা মোডগুলো তালিকাভুক্ত করুন। কমপক্ষে: টাইমআউট (আপনি জানেন না এটা কাজ করেছে কিনা), ডুপ্লিকেট রিকুয়েস্ট, আংশিক সাফল্য (এক ধাপ হয়েছে, পরেরটা হয়নি), এবং ভুল ক্রমে ইভেন্ট।
প্রতিটি ইনপুটের জন্য একটি আইডেম্পোটেন্সি কৌশল চয়ন করুন। synchronous API এর জন্য প্রায়শই idempotency key + সংরক্ষিত ফলাফল; মেসেজ/ইভেন্টের জন্য একটি অনন্য মেসেজ ID এবং "আমি কি এটি প্রসেস করেছি?" রেকর্ড।
উদ্দেশ্য স্থায়ীভাবে সংরক্ষণ করুন, তারপর কাজ করুন। প্রথমে কিছু টেকসই সংরক্ষণ করুন যেমন PaymentAttempt: pending বা ShipmentRequest: queued, তারপর বহির্ভূত কল করুন, পরে আউটকাম সংরক্ষণ করুন। একটি স্থাবর রেফারেন্স ID রিটার্ন করুন যাতে রিট্রাই একই উদ্দেশ্যের দিকে পয়েন্ট করে নতুন একটি তৈরি না করে।
রিকনসিলিয়েশন এবং মেরামতের পথ তৈরি করুন, এবং সেগুলো দৃশ্যমান করুন। রিকনসিলিয়েশন হতে পারে এমন একটি জব যা "অতিরিক্ত সময় পেন্ডিং" রেকর্ডগুলি স্ক্যান করে এবং স্ট্যাটাস পুনরায় চেক করে। মেরামত পথটি হতে পারে একটি নিরাপদ অ্যাডমিন অ্যাকশন যেমন "retry", "cancel" বা "mark resolved" একটি অডিট নোট সহ। মৌলিক অবজার্ভেবিলিটি যোগ করুন: correlation ID, পরিষ্কার স্ট্যাটাস ফিল্ড, এবং কিছু কনটিং (pending, retries, failures)।
উদাহরণ: যদি চেকআউট টাইমআউট করে ঠিক পরে আপনি পেমেন্ট প্রদানকারীকে কল করেছেন, আন্দাজ করবেন না। চেষ্টা সংরক্ষণ করুন, attempt ID রিটার্ন করুন, এবং ব্যবহারকারীকে একই idempotency কী দিয়ে পুনরায় চেষ্টা করার সুযোগ দিন। পরে রিকনসিলিয়েশন নিশ্চিত করবে প্রদানকারী চার্জ করেছে কি না এবং ডাবল-চার্জ না করে attempt আপডেট করবে।
একটি গ্রাহক "Place order" ট্যাপ করে। আপনার সার্ভিস একটি পেমেন্ট অনুরোধ প্রদানকারীকে পাঠায়, কিন্তু নেটওয়ার্ক অনিচ্ছিন্ন। প্রদানকারীর নিজেদের একটি সত্য আছে, এবং আপনার ডাটাবেসের নিজের। যদি আপনি ডিজাইন না করেন তবে এরা বিচ্ছিন্ন হবে।
আপনার দৃষ্টিকোণ থেকে বাইরে একটি স্ট্রিম বার্তা যা দেরিতে, পুনরাবৃত্ত বা অনুপস্থিত হতে পারে:
এই ধাপগুলো কোনোটাই "একবারই"-এর গ্যারান্টি দেয় না। সেগুলো মাত্র "সম্ভব" বলে গ্যারান্টি দেয়।
আপনার সীমানার ভিতরে, টেকসই তথ্য ও বাহ্যিক ইভেন্টগুলোকে ঐ তথ্যের সঙ্গে যুক্ত করার জন্য নূন্যতম দরকারি ডেটা সংরক্ষণ করুন।
গ্রাহক প্রথমবার অর্ডার করলে একটি order রেকর্ড তৈরি করুন একটি স্পষ্ট স্টেটে যেমন pending_payment। একই সাথে একটি payment_attempt রেকর্ড তৈরি করুন যা একটি ইউনিক প্রদানকারী রেফারেন্স এবং idempotency_key ধারণ করবে যা গ্রাহকের কর্মকান্ডের সাথে связанных।
যদি ক্লায়েন্ট টাইমআউট করে এবং পুনরায় চেষ্টা করে, আপনার API-কে দ্বিতীয় অর্ডার তৈরি করা উচিত নয়। এটি idempotency_key দেখে একই order_id এবং বর্তমান স্টেট রিটার্ন করা উচিত। ওই সিদ্ধান্তই নেটওয়ার্ক ব্যর্থ হলে ডুপ্লিকেট প্রতিরোধ করে।
এখন webhook দুটি বার আসে। প্রথম কলব্যাক payment_attempt কে authorized করে আপডেট করে এবং অর্ডারকে paid এ নিয়ে যায়। দ্বিতীয় কলব্যাক একই হ্যান্ডলারকে আঘাত করে, কিন্তু আপনি দেখতে পাবেন যে আপনি ইতিমধ্যেই সেই প্রদানকারী ইভেন্টটি প্রসেস করেছেন (প্রদানকারী ইভেন্ট ID সংরক্ষণ করে, বা বর্তমান স্টেট চেক করে) এবং কিছুই করবেন না। আপনি এখনও 200 OK রেসপন্স পাঠাতে পারেন, কারণ রেজাল্টটি ইতিমধ্যেই সত্য।
শেষমেষ, রিকনসিলিয়েশন মিশ্র কেসগুলো সামলে নেয়। যদি একটি অর্ডার একটি বিলম্বিত সময় পরে এখনও pending_payment থাকে, একটি ব্যাকগ্রাউন্ড জব সংরক্ষিত রেফারেন্স ব্যবহার করে প্রদানকারীকে জিজ্ঞাসা করবে। যদি প্রদানকারী বলে "authorized" কিন্তু আপনি webhook মিস করেছেন, আপনি আপনার রেকর্ড আপডেট করবেন। যদি প্রদানকারী বলে "failed" কিন্তু আপনি ইতিমধ্যেই paid চিহ্নিত করেছেন, আপনি সেটি রিভিউর জন্য ফ্ল্যাগ করবেন বা ক্ষতিপূরণমূলক ক্রিয়া (যেমন রিফান্ড) ট্রিগার করবেন।
অধিকাংশ ডুপ্লিকেট রেকর্ড এবং "আটকে থাকা" ওয়ার্কফ্লো আসে বাইরের ঘটনাকে (একটি রিকুয়েস্ট এসেছে, একটি বার্তা প্রাপ্ত হয়েছে) এবং আপনি যা নিরাপদে আপনার ভিতরে কমিট করেছেন সেটাকে মিশিয়ে ফেলার কারণে।
একটি ক্লাসিক ব্যর্থতা: ক্লায়েন্ট "place order" পাঠায়, আপনার সার্ভার কাজ শুরু করে, নেটওয়ার্ক ড্রপ করে, এবং ক্লায়েন্ট পুনরায় চেষ্টা করে। যদি আপনি প্রতিটি রিট্রাইকে নতুন সত্য মনে করেন, আপনি ডাবল চার্জ, ডুপ্লিকেট অর্ডার, বা একাধিক ইমেল তৈরি করবেন।
সাধারণ কারণগুলো:
একটি সমস্যা সবকিছুকে খারাপ করে: কোনও অডিট ট্রেইল নেই। যদি আপনি ফিল্ডগুলো ওভাররাইট করে কেবল সাম্প্রতিক স্টেট রাখেন, আপনি পরে রিকনসিলিয়েশনের জন্য প্রয়োজনীয় প্রমাণ হারিয়ে ফেলেন।
একটি ভাল সম্প্রীতি পরীক্ষা: "আমি যদি এই হ্যান্ডলারটি দুবার চালাই, কি একই ফল পাব?" যদি উত্তর না হয়, ডুপ্লিকেট রেয়ার এজকেস নয়—এগুলো গ্যারান্টিত।
যদি আপনি একটাই মনে রাখেন: আপনার অ্যাপকে সঠিক থাকতে হবে এমনকি যখন বার্তাগুলো দেরিতে আসে, দুবার আসে বা একেবারেই না আসে।
এই চেকলিস্টটি দুর্বল পয়েন্টগুলো চিহ্নিত করতে ব্যবহার করুন আগে তারা ডুপ্লিকেট রেকর্ড, অনুপস্থিত আপডেট, বা আটকে থাকা ওয়ার্কফ্লো তৈরি করে:
আপনি যদি এগুলোর কোনটির দ্রুত উত্তর না দিতে পারেন, এটি সাধারণত মানে একটি সীমা অস্পষ্ট বা একটি স্টেট ট্রানজিশন অনুপস্থিত।
ব্যবহারিক পরবর্তী ধাপ:
প্রথমে সীমানা এবং স্টেটগুলো আঁকুন। প্রতিটি ওয়ার্কফ্লোর জন্য ছোট একটি স্টেট সেট সংজ্ঞায়িত করুন (উদাহরণ: Created, PaymentPending, Paid, FulfillmentPending, Completed, Failed)।
সবচেয়ে ঝুঁকিপূর্ণ লিখনে আইডেম্পোটেন্সি যোগ করুন। শুরু করুন: create order, capture payment, issue refund। PostgreSQL-এ ইউনিক কনস্ট্রেইন্টসহ idempotency কী সংরক্ষণ করুন যাতে ডুপ্লিকেট নিরাপদে প্রত্যাখ্যাত হয়।
রিকনসিলিয়েশনকে একটি সাধারণ ফিচার হিসেবে বিবেচনা করুন। একটি জব শিডিউল করুন যা "অতিরিক্ত সময় পেন্ডিং" রেকর্ডগুলো খোঁজে, বহির্ভূত সিস্টেম চেক করে, এবং লোকাল স্টেট মেরামত করে।
নিরাপদে পুনরাবৃত্তি করুন। ট্রানজিশন এবং রিট্রাই নিয়ম ঠিক করুন, তারপর একই অনুরোধ পুনরায় পাঠিয়ে এবং একই ইভেন্ট পুনরায় প্রসেস করে পরীক্ষা করুন।
আপনি যদি দ্রুতভাবে Koder.ai (koder.ai) মত একটি চ্যাট-চালিত প্ল্যাটফর্মে তৈরি করছেন, তখনও এগুলো প্রথম থেকেই আপনার জেনারেটেড সার্ভিসে সংযুক্ত করা মূল্যবান: গতি অটোমেশনের থেকে আসে, কিন্তু নির্ভরযোগ্যতা আসে স্পষ্ট সীমানা, আইডেম্পোটেন্ট হ্যান্ডলার এবং রিকনসিলিয়েশন থেকে।
"বাইরে" হল এমন সবকিছু যা আপনি নিয়ন্ত্রণ করেন না: ব্রাউজার, মোবাইল নেটওয়ার্ক, কিউ, তৃতীয়-পক্ষের webhook, রিট্রাই এবং টাইমআউট। বার্তা বিলম্বিত, সদৃশ, হারিয়ে যেতে পারে বা সঠিক ক্রমে না আসতে পারে এমনটি ধরে নিন.
"ভিতরে" হল আপনি যা নিয়ন্ত্রণ করেন: আপনার সংরক্ষিত স্টেট, আপনার নিয়মগুলো এবং পরে প্রমাণ করার মতো তথ্য (সাধারণত আপনার ডেটাবেসে)।
কারণ নেটওয়ার্ক আপনাকে মিথ্যা বলে।
ক্লায়েন্ট টাইমআউট হওয়া মানেই আপনার সার্ভার রিকুয়েস্ট প্রসেস করেনি এমন নয়। একটি webhook দুবার এসে গেলেও সেটি প্রদানকারী কাজটি দুবার করেছে এমন অর্থ নাও বহন করে। যদি আপনি প্রতিটি বার্তাকে "নতুন সত্য" ধরা শুরু করেন, তাহলে ডুপ্লিকেট অর্ডার, ডাবল চার্জ এবং আটকে থাকা ওয়ার্কফ্লো তৈরি হবে।
একটি পরিষ্কার সীমানা হলো যেখানে একটি অনিশ্চিত বার্তা একটি টেকসই সত্যে পরিণত হয়।
সাধারণ সীমান্তগুলো:
একবার ডেটা সীমান্ত পেরিয়ে গেলে আপনি ভিতরে ইনভারিয়েন্টগুলো প্রয়োগ করেন (যেমন "অর্ডার একবারই পেইড হতে পারে").
আইডেম্পোটেন্সি ব্যবহার করুন। মৌলিক ধারণা: একই উদ্দেশ্য যদি একাধিকবার পাঠানো হয়, সিস্টেম সেটিকে একবারই ঘটবে বলে বিবেচনা করবে এবং একই আউটকাম রিটার্ন করবে.
প্র্যাকটিক্যাল প্যাটার্ন:
মনে রাখবেন এটি কেবল মেমরিতে রাখবেন না। এটি আপনার সীমানার ভিতরে (উদাহরণস্বরূপ PostgreSQL) সংরক্ষণ করুন যাতে রিস্টার্টেও সুরক্ষা বজায় থাকে.
সংরক্ষণকাল কেমন হবে তা ঝুঁকির উপর নির্ভর করবে:
এটি যথেষ্ট দীর্ঘ রাখুন যাতে বাস্তবসম্মত রিট্রাই এবং বিলম্বিত ডেলিভারি ঢাকা পড়ে।
অনিশ্চয়তাকে স্বীকার করে এমন স্টেট ব্যবহার করুন।
সরল এবং ব্যবহারিক একটি সেট:
pending_* (আমরা উদ্দেশ্যটি গ্রহণ করেছি কিন্তু ফলাফল জানি না)succeeded / failed (চূড়ান্ত আউটকাম রেকর্ড করা হয়েছে)needs_review (মিলমেল দেখা গেছে যা একজন মানুষের হস্তক্ষেপ বা বিশেষ জব চায়)কারণ আপনি নেটওয়ার্ক জুড়ে একাধিক সিস্টেমে অ্যাটোমিক কমিট ধরতে পারবেন না।
যদি আপনি "অর্ডার সেভ → কার্ড চার্জ → ইনভেন্টরি রিজার্ভ" সিঙ্ক্রোনাসভাবে করেন এবং ধাপ 2 টাইমআউট করে, তাহলে আপনি জানবেন না সেটা ব্যর্থ হয়েছে নাকি সফল। রিট্রাই করলে ডুপ্লিকেট হবে; না করলে কাজ অসম্পূর্ণ থাকবে.
পার্শিয়াল সাফল্যের জন্য ডিজাইন করুন: প্রথমে উদ্দেশ্যটি টেকসইভাবে সংরক্ষণ করুন, তারপর এক্সটার্নাল অ্যাকশন করুন, পরে আউটকাম রেকর্ড করুন।
outbox/inbox প্যাটার্ন নেটওয়ার্ককে পারফেক্ট মনে না করে ক্রস-সিস্টেম মেসেজিং নির্ভরযোগ্য করে তোলে.
রিকনসিলিয়েশন হলো যখন আপনার রেকর্ড এবং একটি বাহ্যিক সিস্টেমের মধ্যে অমিল দেখা দেয় তখন কিভাবে আপনি পুনরুদ্ধার করবেন।
ভাল ডিফল্ট:
needs_review চিহ্নিত করাপেমেন্ট, ফুলফিলমেন্ট, সাবস্ক্রিপশন বা যেকোনো webhook-যুক্ত কাজের জন্য এটি অপরিহার্য।
হ্যাঁ। দ্রুত তৈরি করা নেটওয়ার্ক ব্যর্থতা বিলোপ করে না—এরা আপনাকে দ্রুতই সমস্যার মুখোমুখি করে।
যদি আপনি Koder.ai (koder.ai)‑এর মতো প্ল্যাটফর্মে সার্ভিস দ্রুত জেনারেট করে থাকেন, এই ডিফল্টগুলো আগে থেকেই অন্তর্ভুক্ত করা মূল্যবান:
এভাবে, রিট্রাই এবং ডুপ্লিকেট কলব্যাক সাধারণ ঝামেলা না হয়ে নির্বিঘ্ন কাজ করবে।
এটি টাইমআউটের সময় জিগার করা থেকে বিরত রাখে এবং রিকনসিলিয়েশন সহজ করে তোলে।