CRUD অ্যাপে রেস কন্ডিশন দেখতে কেমন\n\nরেস কন্ডিশন ঘটে যখন দুই (বা বেশি) অনুরোধ প্রায় এক সময়ে একই ডেটা আপডেট করে, এবং চূড়ান্ত ফলাফল টiming‑এর ওপর নির্ভরশীল হয়ে পড়ে। প্রতিটি অনুরোধ আলাদাভাবে সঠিক মনে হয়, কিন্তু একসাথে তারা ভুল আউটপুট দেয়।\n\nসহজ একটি উদাহরণ: দুই মানুষ একই কাস্টমার রেকর্ডে এক সেকেন্ডের মধ্যে Save চাপেন। একজন ইমেইল আপডেট করে, অন্যজন ফোন নম্বর। যদি উভয় অনুরোধ পুরো রেকর্ড পাঠায়, দ্বিতীয়টি প্রথমটির উপরই লিখে দিতে পারে, এবং এক পরিবর্তন অদৃশ্য হয়ে যায় কোনো ত্রুটি ছাড়াই।\n\nদ্রুত অ্যাপে এটা বেশি দেখা যায় কারণ ব্যবহারকারীরা প্রতি মিনিটে বেশি ক্রিয়া ট্রিগার করতে পারে। ফ্ল্যাশ সেল, মাস শেষে রিপোর্টিং, বড় ইমেইল ক্যাম্পেইন বা যখন অনুরোধের ব্যাকলগ একই সারিগুলোর ওপর আঘাত করে — তখন স্পাইক বাড়ে।\n\nব্যবহারকারীরা সাধারণত “রেস কন্ডিশন” বলে রিপোর্ট করে না। তারা লক্ষণগুলো রিপোর্ট করে: ডুপ্লিকেট অর্ডার বা কমেন্ট, মিসিং আপডেট ("আমি সেভ করেছিলাম, কিন্তু ফিরে গেল"), অদ্ভুত টোটাল (ইনভেন্টরি নেগেটিভ হয়ে যাওয়া, কাউন্টার পিছনে লাফ), বা স্ট্যাটাস যা আচমকাই উল্টে যায় (approved, তারপর আবার pending)।\n\nরিট্রাই এটাকে খারাপ করে দেয়। মানুষ ডাবল‑ক্লিক করে, উত্তর ধীর হলে রিফ্রেশ করে, দুইটি ট্যাব থেকে সাবমিট করে, বা দুর্বল নেটওয়ার্কে ব্রাউজার/মোবাইল অ্যাপ রিকোয়েস্ট পুনরায় পাঠায়। সার্ভার যদি প্রতিটি অনুরোধকে নতুন লিখন হিসেবে বিবেচনা করে, আপনি দুইটি create, দুইটি payment, বা এমন দুটি স্ট্যাটাস পরিবর্তন পেতে পারেন যা একবারই হওয়ার কথা ছিল।\n\n## রেস কন্ডিশন কেন আপনি ভাবার চেয়েও বেশি ঘটে\n\nবেশি CRUD অ্যাপ সাধারণ মনে হয়: একটি রো পড়ুন, একটি ফিল্ড বদলান, সেভ করুন। সমস্যা হল—আপনার অ্যাপ টাইমিং নিয়ন্ত্রণ করে না। ডাটাবেস, নেটওয়ার্ক, রিট্রাই, ব্যাকগ্রাউন্ড কাজ এবং ব্যবহারকারীর আচরণ সব মিলেই ওভারল্যাপ করে।\n\nএকটি সাধারণ ট্রিগার হলো একই রেকর্ড একাধিক মানুষ এডিট করা। উভয়েই একই "বর্তমান" মান লোড করে, উভয়েই বৈধ পরিবর্তন করে, এবং শেষের সেভ চুপচাপ প্রথমটিকে ওভাররাইট করে দেয়। কেউও ভুল করেনি, কিন্তু আপডেটটি হারিয়ে যায়।\n\nএটি এক ব্যক্তির সঙ্গেও ঘটে। Save বোতামে ডাবল‑ক্লিক, ধীর সংযোগে আবার সাবমিট করা—এসব একই লিখন দুবার পাঠাতে পারে। যদি endpoint আইডেমপটেন্ট না হয়, আপনি ডুপ্লিকেট, দ্বিগুণ চার্জ, বা স্ট্যাটাস দ্বিগুণ স্টেপ এগিয়ে যাওয়া পেতে পারেন।\n\nআধুনিক ব্যবহারে আরও বেশি ওভারল্যাপ আসে। একই একাউন্টে একাধিক ট্যাব বা ডিভাইস কনফ্লিক্টিং আপডেট পাঠাতে পারে। ব্যাকগ্রাউন্ড জব (ইমেইল, বিলিং, সিঙ্ক, ক্লিনআপ) ওয়েব অনুরোধের সঙ্গে একই সারিগুলো স্পর্শ করতে পারে। ক্লায়েন্ট, লোড ব্যালান্সার, বা জব রাননার‑এ স্বয়ংক্রিয় রিট্রাই ইতিমধ্যে সফল অনুরোধ পুনরায় চালাতে পারে।\n\nআপনি যদি দ্রুত ফিচার শিপ করেন, একই রেকর্ড প্রায়ই এমন অনেক জায়গা থেকে আপডেট হয় যা আর কেউ মনে রাখে না। যদি আপনি Koder.ai মতো চ্যাট‑ড্রিভেন বিল্ডার ব্যবহার করেন, অ্যাপ আরও দ্রুত বেড়ে যায়—তাই কনকারেন্সিকে একটি সাভাবিক আচরণ হিসেবে বিবেচনা করা উচিত, এক ধরনের edge‑case হিসেবে নয়।\n\n## মনিটর করার সাধারণ সংঘর্ষ‑স্থলগুলো\n\nরেস কন্ডিশন সচরাচর "রেকর্ড তৈরি" ডেমোতে দেখায় না। এগুলো আসে যেখানে দুই অনুরোধ প্রায় একই সময়ে একই সত্যের টুকরো স্পর্শ করে। সাধারণ হটস্পটগুলো জানা থাকলে আপনি প্রথম দিন থেকেই নিরাপদ লিখন ডিজাইন করতে পারবেন।\n\n### কাউন্টার ও "পরে নম্বর" ফিল্ডসমূহ\n\nযে কিছুকে আপনি "শুধু +1 কর" মনে করেন, সেটাই লোডে ভেঙে যায়: লাইক, ভিউ কাউন্ট, টোটাল, ইনভয়েস নম্বর, টিকিট নম্বর। ঝুঁকিপূর্ণ প্যাটার্নটি হলো মান পড়া, যোগ করা, এবং ফের লেখা। দুই অনুরোধ একই স্টার্টিং মান পড়তে পারে এবং একে অপরকে ওভাররাইট করে দিতে পারে।\n\n### স্ট্যাটাস ট্রানজিশন\n\nDraft -> Submitted -> Approved -> Paid মতো ওয়ার্কফ্লো সহজ মনে হলেও সংঘর্ষ সাধারণ। সমস্যা তখন শুরু হয় যখন একই সময়ে দুটি অ্যাকশন সম্ভব (approve এবং edit, cancel এবং pay)। গার্ড ছাড়া আপনি এমন রেকর্ড পেতে পারেন যা স্টেপ স্কিপ করে, আবার উল্টে যায়, বা ভিন্ন টেবিলে ভিন্ন স্টেট দেখায়।\n\nস্ট্যাটাস পরিবর্তনকে একটি চুক্তি হিসেবে দেখুন: কেবল পরবর্তী বৈধ স্টেপ অনুমোদন করুন এবং বাকি বাতিল করুন।\n\n### ইনভেন্টরি, ক্যাপাসিটি এবং বুকিং স্লট\n\nবাকি সিট, স্টক কাউন্ট, অ্যাপয়েন্টমেন্ট স্লট, এবং "বাকি ক্যাপাসিটি" ফিল্ডগুলো ক্লাসিক ওভারসেল সমস্যা তৈরি করে। দুই ক্রেতা একসাথে চেকআউট করে, উভয়ই অ্যাভেইলেবিলিটি দেখে সফল হয়, এবং যদি ডাটাবেস শেষ সিদ্ধান্ত না হয়, আপনি শেষ পর্যন্ত আপনার চেয়ে বেশি বিক্রি করে ফেলবেন।\n\n### ইউনিকনেস এবং "শুধুমাত্র একটাই অ্যাকটিভ" নিয়ম\n\nকিছু নিয়ম একেবারেই কঠোর: এক অ্যাকাউন্টে এক ইমেইল, প্রতিটি ইউজারের জন্য এক অ্যাকটিভ সাবস্ক্রিপশন, এক ইউজারের জন্য এক ওপেন কার্ট। এগুলো প্রায়ই ব্যর্থ হয় যখন আপনি আগে চেক করেন ("কিছু আছে কি?") এবং তারপর insert করেন। কনকারেন্সির সময় দুই অনুরোধই চেক পাস করতে পারে।\n\nআপনি যদি CRUD ফ্লো দ্রুত জেনারেট করেন (উদাহরণস্বরূপ, Koder.ai‑তে চ্যাট করে), এই হটস্পটগুলো আগে থেকেই নোট করুন এবং UI চেক নয়—কনস্ট্রেন্ট ও সেফ রাইট দিয়ে ব্যাক করুন।\n\n## ডাবল সাবমিট ও UI থেকে পুনরাবৃত্ত অনুরোধ\n\nঅনেক রেস কন্ডিশনের শুরুটা একটা সাধারণ ঘটনা: একই অ্যাকশন দুইবার পাঠানো। ব্যবহারকারী ডাবল‑ক্লিক করে। নেটওয়ার্ক ধীর হলে তারা আবার ক্লিক করে। ফোন দুইবার ট্যাপ রেজিস্টার করতে পারে। কখনও কখনও ইচ্ছাকৃতও নয়: POST‑এর পরে পেজ রিফ্রেশ হলে ব্রাউজার ফর্মটি আবার সাবমিট করার অফার করে।\n\nযখন এমন হয়, আপনার ব্যাকএন্ড দুইটি create বা update প্যারালাল চালাতে পারে। উভয়ই সফল হলে আপনি ডুপ্লিকেট, ভুল টোটাল, বা স্ট্যাটাস পরিবর্তন দুবার পাবেন (যেমন approve তারপর আবার approve)। এটা র্যান্ডম মনে হবে কারণ এটা টাইমিং‑এর ওপর নির্ভর করে।\n\nসবচেয়ে নিরাপদ পন্থা হলো ডিফেন্স ইন ডেপ্থ। UI ঠিক করুন, কিন্তু ধরে নিন UI ব্যর্থ হবে।\n\nবহু রাইট ফ্লোতে প্রয়োগ করার ব্যবহারিক পরিবর্তনগুলো:\n\n- প্রথম ক্লিকেই সাবমিট নিষ্ক্রিয় করুন এবং স্পষ্ট একটি "saving" স্টেট দেখান।\n- সফল POST‑এর পরে redirect করুন যাতে রিফ্রেশ একই ফর্ম আবার না পাঠায়।\n- একটি idempotency key ব্যবহার করুন: প্রতিটি ব্যবহারকারীর অ্যাকশনের জন্য একটি অনন্য টোকেন যা সার্ভার সংরক্ষণ করে এবং কেবল একবার গ্রহণ করে।\n- এটাকে ডাটাবেস ইউনিকনেস রুল দিয়ে ব্যাক করুন যাতে দুইটি অভিন্ন ইনসার্ট দুটোই জিততে না পারে।\n\nউদাহরণ: একজন ব্যবহারকারী মোবাইলে "Pay invoice" দুইবার ট্যাপ করে। UI‑কে দ্বিতীয় ট্যাপ ব্লক করা উচিত। সার্ভারও একই idempotency key দেখলে দ্বিতীয় অনুরোধ প্রত্যাখ্যান করা উচিত এবং প্রথম সফল ফলাফলটাই ফেরত দেয় যাতে দ্বিগুণ চার্জ না হয়।\n\n## স্ট্যাটাস ট্রানজিশন যা সিঙ্ক থেকে বের হয়ে যায়\n\nস্ট্যাটাস ফিল্ডগুলো সহজ মনে হলেও দুইটি জিনিস একই সময়ে এগুলো বদলে ফেললে সমস্যা শুরু হয়। একজন ব্যবহারকারী Approve চাপছে আর অটোমেটেড জব একই রেকর্ডকে Expired চিহ্নিত করছে, বা দুই জন টিম মেম্বার ভিন্ন ট্যাবে একই আইটেমে কাজ করছে—উভয় আপডেট সফল হতে পারে, কিন্তু চূড়ান্ত স্ট্যাটাস আপনার নিয়ম অনুযায়ী নয়, টাইমিং অনুযায়ী ঠিক হয়।\n\nস্ট্যাটাসকে একটি ছোট state machine হিসেবে বিবেচনা করুন। একটি সংক্ষিপ্ত টেবিল রাখুন অনুমোদিত মুভগুলোর (উদাহরণ: Draft -> Submitted -> Approved, এবং Submitted -> Rejected)। তারপর প্রতিটি রাইট চেক করুন: "এই মুভটি বর্তমান স্ট্যাটাস থেকে অনুমোদিত কি?" যদি না হয়, সেভ করার বদলে প্রত্যাখ্যান করুন।\n\nOptimistic locking আপনাকে stale আপডেট ধরতে সাহায্য করে ব্লক না করে। একটি ভার্সন নম্বর (বা updated_at) যোগ করুন এবং সেভ করার সময় এটি মিলতে হবে। কেউ আপনার লোড করার পরে যদি রোটা বদলে দেয়, আপনার আপডেট শূন্য রোতে প্রয়োগ হবে এবং আপনি একটি পরিষ্কার বার্তা দেখাতে পারেন যেমন: "এই আইটেমটা বদলেছে, রিফ্রেশ করে আবার চেষ্টা করুন।"\n\nস্ট্যাটাস আপডেটের জন্য একটি সহজ প্যাটার্ন:\n\n- বর্তমান স্ট্যাটাস ও ভার্সন পড়ুন\n- ট্রানজিশন ভ্যালিডেট করুন\n- একটি গার্ড দিয়ে আপডেট করুন (WHERE status = ? AND version = ?)\n- ভার্সন বাড়ান\n\nআরো ভালো হচ্ছে স্ট্যাটাস চেঞ্জগুলো এক জায়গায় রাখুন। যদি আপডেটগুলো স্ক্রিন, ব্যাকগ্রাউন্ড জব, এবং webhook-এ ছড়িয়ে থাকে, আপনি নিয়মগুলো মিস করবেন। একটিমাত্র ফাংশন বা endpoint‑এর পেছনে এসব রাখুন যাতে প্রতিবার একই ট্রানজিশন চেক প্রয়োগ হয়।\n\n## সময়ের সাথে ভ্রম হাওয়া কাউন্টার ও টোটাল\n\nসবচেয়ে সাধারণ কাউন্টার বাগটি দেখতেও হালকা মনে হয়: অ্যাপ একটি মান পড়ে, +1 করে, তারপর আবার লিখে। লোডে দুই অনুরোধ একই নম্বর পড়ে এবং উভয়ই একই নতুন নম্বর লিখে দেয়, ফলে একটি ইনক্রিমেন্ট হারিয়ে যায়। এটা টেস্টিং‑এ সাধারণত কাজ করে বলে সহজে মিস করা যায়।\n\n### পড়া-তারপর-লিখার বদলে অ্যাটমিক আপডেট পছন্দ করুন\n\nযদি একটি মান শুধু ইনক্রিমেন্ট বা ডিক্রিমেন্ট হচ্ছে, ডাটাবেসকে এক স্টেটমেন্টে এটা করার দিন। তাহলে অনেক অনুরোধ একসাথে আঘাত করলেও ডাটাবেস পরিবর্তনগুলো নিরাপদভাবে প্রয়োগ করবে।\n\n```sql