5 মিনিট

CRUD অ্যাপে ডুপ্লিকেট রেকর্ড প্রতিরোধ: ব্যবহারিক প্রতিরক্ষা

CRUD অ্যাপে ডুপ্লিকেট রেকর্ড প্রতিরোধ করতে অনেক স্তর লাগে: ডাটাবেস ইউনিক কনস্ট্রেইন্ট, idempotency কী, এবং ডবল সাবমিট রোধকারী UI স্টেট।

CRUD অ্যাপে ডুপ্লিকেট রেকর্ড প্রতিরোধ: ব্যবহারিক প্রতিরক্ষা

ডুপ্লিকেট রেকর্ড কী এবং কেন ঘটে\n\nডুপ্লিকেট রেকর্ড হল যখন আপনার অ্যাপ একই জিনিস দুইবার সেভ করে ফেলে। সেটা হতে পারে একই চেকআউটের জন্য দুইটি অর্ডার, একই ডিটেইলস নিয়ে দুইটি সাপোর্ট টিকিট, অথবা একই সাইনআপ ফ্লো থেকে দুইটি অ্যাকাউন্ট। CRUD অ্যাপে ডুপ্লিকেট সাধারণত আলাদা রো হিসেবে থাকে, কিন্তু ডেটাকে সামগ্রিকভাবে দেখলে ঠিক নয়।\n\nঅধিকাংশ ডুপ্লিকেট শুরু হয় সাধারণ আচরণ থেকেই। কেউ Create দুইবার ক্লিক করে কারণ পেজ ধীর মনে হচ্ছে। মোবাইলে ডবল-ট্যাপ খোঁজেই মিস হওয়া সহজ। এমনকি যত্নশীল ব্যবহারকারীরাও আবার চেষ্টা করবে যদি বাটন এখনও সক্রিয় দেখায় এবং স্পষ্ট কিছু ঘটছে বলে না দেয়।\n\nতারপর আছে নোংরা মাঝামাঝি: নেটওয়ার্ক এবং সার্ভার। একটি রিকোয়েস্ট টাইমআউট হতে পারে এবং স্বয়ংক্রিয়ভাবে রিট্রাই হতে পারে। ক্লায়েন্ট লাইব্রেরি যদি মনে করে প্রথম চেষ্টা ব্যর্থ হয়েছে, তাহলে এটি একটি POST পুনরায় পাঠাতে পারে। প্রথম রিকোয়েস্ট সফল হতে পারে, কিন্তু রেসপন্স লস্ট হয়ে গেলে ব্যবহারকারী আবার চেষ্টা করে এবং দ্বিতীয় কপি তৈরি হয়।\n\nআপনি এক লেয়ারে এটা সমাধান করতে পারবেন না কারণ প্রতিটি স্তর কেবল গল্পের একটা অংশই দেখে। UI দুর্ঘটনাজনিত ডবল সাবমিট কমাতে পারে, কিন্তু খারাপ কনেকশনের পুনরায় পাঠ থামাতে পারে না। সার্ভার রিপিট সনাক্ত করতে পারে, কিন্তু এটি চিহ্নিত করার জন্য নির্ভরযোগ্য উপায় চায় যে "এটা আবার একই create।" ডাটাবেস নিয়ম আরোপ করতে পারে, কিন্তু সেই ক্ষেত্রে আপনাকে নির্দিষ্ট করতে হবে "একই জিনিস" মানে কি।\n\nলক্ষ্য সহজ: একই রিকোয়েস্ট দুইবার ঘটলেও creates নিরাপদ রাখুন। দ্বিতীয় চেষ্টা নো-অপ হওয়া উচিত, একটি পরিষ্কার “আগেই তৈরি হয়েছে” রেসপন্স হওয়া উচিত, অথবা একটা নিয়ন্ত্রিত কনফ্লিক্ট — না যেন দ্বিতীয় রো।\n\n## কোথায় ডুপ্লিকেট হয় (ক্লায়েন্ট, নেটওয়ার্ক, সার্ভার)\n\nঅনেক দল ডুপ্লিকেটকে ডাটাবেস সমস্যা হিসেবেই দেখে। বাস্তবে, ডুপ্লিকেট প্রায়ই আগেই জন্ম নেয়, যখন একই create অ্যাকশন একাধিকবার ট্রিগার হয়।\n\n### ক্লায়েন্ট‑সাইড: ব্যবহারকারী দুইবার সাবমিট করে\n\nএকজন ব্যবহারকারী Create ক্লিক করে এবং কিছুই ঘটছে না মনে হলে আবার ক্লিক করে। অথবা তারা Enter চাপে, তারপর দ্রুত বাটনে ক্লিক করে। মোবাইলে দুইটি দ্রুত ট্যাপ, ওভারল্যাপিং টাচ ও ক্লিক ইভেন্ট, বা একটি জেসচারের ফলে ডাবল রেজিস্ট্রেশন হতে পারে।\n\n### নেটওয়ার্ক ও ট্রান্সপোর্ট: রিকোয়েস্ট পুনরায় পাঠানো হয়\n\nব্যবহারকারী শুধু একবার সাবমিট করলেও, নেটওয়ার্ক রিকোয়েস্টকে পুনরায় পাঠাতে পারে। টাইমআউট রিট্রাই ট্রিগার করতে পারে। একটি অফলাইন অ্যাপ "Save" কিউ করে পুনরায় সংযোগে পাঠাতে পারে। কিছু HTTP লাইব্রেরি নির্দিষ্ট এররগুলোতে স্বয়ংক্রিয়ভাবে পুনরায় চেষ্টা করে, এবং আপনি সেটা কেবল ডুপ্লিকেট রো দেখলে টের পাবেন।\n\n### সার্ভার‑সাইড: রিট্রাই, webhook, এবং ব্যাকগ্রাউন্ড ওয়ার্কার\n\nসার্ভারগুলো ইচ্ছাকৃতভাবে কাজ পুনরায় করে। জব কিউগুলো ফেইল হওয়া জবগুলো রিট্রাই করে। webhook প্রদানকারীরা একই ইভেন্ট একাধিকবার ডেলিভার করে, বিশেষ করে যদি আপনার endpoint ধীর বা non-2xx স্টেটাস রিটার্ন করে। যদি আপনার create লজিক এসব ইভেন্ট দ্বারা ট্রিগার হয়, ধরে নিন ডুপ্লিকেট ঘটবেই।\n\n### কনকারেন্সি: দুইটি রিকোয়েস্ট রেসে জিতছে\n\nকনকারেন্সি সবচেয়ে ধূর্ত ডুপ্লিকেট তৈরি করে। দুইটি ট্যাব একই ফর্ম মিলিসেকেন্ডের মধ্যে সাবমিট করতে পারে। যদি সার্ভার "আছে কি না দেখে" তারপর ইনসার্ট করে, দুইটি রিকোয়েস্টই চেক পাস করে ফেলতে পারে আগে কোনো ইনসার্ট হয়।\n\nক্লায়েন্ট, নেটওয়ার্ক, এবং সার্ভারকে আলাদা উৎস হিসেবে বিবেচনা করুন। আপনাকে তিনটাতেই প্রতিরোধ স্থাপন করতে হবে।\n\n## ডাটাবেস ইউনিক কনস্ট্রেইন্ট দিয়ে শুরু করুন (চূড়ান্ত প্রতিরোধ)

\nযদি আপনি ডুপ্লিকেট বন্ধ করার জন্য একটি নির্ভরযোগ্য জায়গা চান, নিয়মটি ডাটাবেসে রাখুন। UI ফিক্স এবং সার্ভার চেক সাহায্য করে, কিন্তু রিট্রাই, ল্যাগ, বা একসাথে দুইজন ব্যবহারকারীর কাণ্ডে তারা ব্যর্থ হতে পারে। একটি ডাটাবেস ইউনিক কনস্ট্রেইন্ট হল চূড়ান্ত কর্তৃপক্ষ।\n\nপ্রথমে এমন একটি বাস্তব-বিশ্ব ইউনিকনেস রুল বেছে নিন যা মানুষ কিভাবে রেকর্ডটি দেখে সেই সাথেই মেলে। সাধারণ উদাহরণগুলো:\n\n- একজন ব্যবহারকারীর জন্য ইমেল ঠিকানা\n- একটি ক্রয়ের জন্য অর্ডার নম্বর\n- একটি পেমেন্ট প্রদানকারীর external_id\n\nআবশ্যক নয় এমন ফিল্ডগুলো নিয়ে সতর্ক থাকুন, যেমন পুরো নাম।\n\nরুল ঠিক হলে, একটি unique constraint (বা unique index) দিয়ে এটি এনফোর্স করুন। এতে ডাটাবেস একই মুহূর্তে আসা দুইটি ইনসার্টকেও প্রত্যাখ্যান করবে যদি সেটা রুল ভঙ্গ করে।\n\nকনস্ট্রেইন্ট ট্রিগার হলে ব্যবহারকারীর অভিজ্ঞতা কী হবে সেটা ঠিক করুন। যদি ডুপ্লিকেট তৈরি করা সবসময় ভুল হয়, তাহলে একটি পরিষ্কার বার্তা দেখান ("এই ইমেলটি ইতিমধ্যে ব্যবহৃত হয়েছে")। যদি রিট্রাই সাধারণ হয় এবং রেকর্ড আগেই আছে, তবে প্রায়ই রিট্রাইকে সফল ধরা এবং বিদ্যমান রেকর্ড ফেরত দেওয়াই ভাল ("আপনার অর্ডার ইতিমধ্যে তৈরি হয়েছে")।\n\n### উপযুক্ত হলে সেফ রাইট প্যাটার্ন ব্যবহার করুন\n\nআপনার create প্রকৃতপক্ষে "create বা reuse" হলে, upsert সবচেয়ে পরিষ্কার প্যাটার্ন হতে পারে। উদাহরণ: "ইমেলের মাধ্যমে কাস্টমার তৈরি" একটি নতুন রো ইনসার্ট করতে পারে বা বিদ্যমানটি ফিরিয়ে দিতে পারে। এটা কেবল তখনই ব্যবহার করুন যখন এটি ব্যবসায়িক মানেই মিলে। যদি একই কী-র জন্য মাঝে মাঝে ভিন্ন পেইলোড আসে, তখন সিদ্ধান্ত নিন কোন ফিল্ডগুলো আপডেট করা যাবে এবং কোনগুলো অপরিবর্তিত থাকবে।\n\nইউনিক কনস্ট্রেইন্ট idempotency কী বা ভালো UI স্টেটকে বদলায় না, কিন্তু এগুলোকে নির্ভর করতে দেওয়ার মতো একটি কঠিন বন্ধনী দেয়।\n\n## Idempotency কী: পুনরাবৃত্তি-নিরাপদ create করুন\n\nএকটি idempotency কী হল একটি ইউনিক টোকেন যা একটি ব্যবহারকারীর ইচ্ছাকে প্রতিনিধিত্ব করে, যেমন "এই অর্ডারটি একবার তৈরি করুন"। একই রিকোয়েস্ট আবার পাঠালে (ডবল ক্লিক, নেটওয়ার্ক রিট্রাই, মোবাইল রিসিউম), সার্ভার এটিকে একটি রিট্রাই হিসেবে বিবেচনা করে, নতুন create হিসেবে নয়।\n\nএটি এমন একটি অত্যন্ত ব্যবহারিক টুল যা ক্লায়েন্ট প্রথম প্রচেষ্টাটি সফল হয়েছে কি না তা বুঝাতে না পারলে create এন্ডপয়েন্টগুলোকে নিরাপদ করে তোলে।\n\nযে এন্ডপয়েন্টগুলো সবচেয়ে উপকৃত হয় সেগুলো হল যেখানে ডুপ্লিকেট ব্যয়বহুল বা বিভ্রান্তিকর, যেমন অর্ডার, ইনভয়েস, পেমেন্ট, ইনভাইট, সাবস্ক্রিপশন, এবং এমন ফর্ম যা ইমেল বা webhook ট্রিগার করে।\n\nরিট্রাই হলে, সার্ভার প্রথম সফল প্রচেষ্টার মূল রেজাল্ট রিটার্ন করা উচিত, একই তৈরি করা রেকর্ড ID এবং স্ট্যাটাস কোডসহ। এর জন্য একটি ছোট idempotency রেকর্ড সংরক্ষণ করুন যা (user বা account) + endpoint + idempotency কী দ্বারা কীড করা আছে। ফলাফল (রেকর্ড ID, রেসপন্স বডি) এবং একটি "in progress" স্টেট সংরক্ষণ করুন যাতে দুইটি কাছাকাছি অনুরোধ দুটি রো না তৈরি করে।\n\nবাস্তব রিট্রাইলার সময় পর্যন্ত idempotency রেকর্ড রাখুন। সাধারণ বেসলাইন হল 24 ঘন্টা। পেমেন্টের জন্য অনেক দল 48–72 ঘন্টা রাখে। একটি TTL স্টোরেজ সীমিত রাখে এবং কতদিন রিট্রাই সম্ভব তা মিলে।\n\nযদি আপনি Koder.ai-এর মতো একটি চ্যাট-চালিত বিল্ডার দিয়ে API জেনারেট করেন, তবুও idempotency স্পষ্ট রাখুন: ক্লায়েন্ট-প্রেরিত কী গ্রহণ করুন (হেডার বা ফিল্ড) এবং সার্ভারে "একই কী, একই ফল" এনফোর্স করুন।\n\n## ধাপে ধাপে: একটি create এন্ডপয়েন্টে idempotency যোগ করা\n\nIdempotency একটি create রিকোয়েস্টকে পুনরাবৃত্তির জন্য নিরাপদ করে। যদি ক্লায়েন্ট টাইমআউটের কারণে রিট্রাই করে (বা ব্যবহারকারী দুইবার ক্লিক করে), সার্ভার একই ফলাফল দেয় নতুন রো তৈরি না করে।\n\n### একটি ব্যবহারিক ৫-ধাপ রেসিপি\n\n1. প্রতিটি ব্যবহারকারীর ইচ্ছার জন্য একটি idempotency কী তৈরি করুন (উদাহরণ: যখন তারা “Create invoice” চাপে)। ক্লায়েন্টেই কী জেনারেট করুন এবং ঐ একচেটিয়া ক্রিয়ার জন্য এটি স্থিতিশীল রাখুন।\n2. সেই কীটি প্রতিবার রিকোয়েস্টের সঙ্গে পাঠান যখনই আপনি সেই create চেষ্টা করেন। একটি হেডার ভালো কাজ করে (যেমন Idempotency-Key), কিন্তু JSON বডিতেও পাঠানো যায়।\n3. সার্ভারে, কীকে একটি স্পষ্ট স্কোপের সাথে সংরক্ষণ করুন: কে কল করছে (user বা account), কোন এন্ডপয়েন্টে প্রযোজ্য, এবং কোন create-কে প্রতিনিধিত্ব করছে। ফাইনাল রেসপন্স পে-লোডও সংরক্ষণ করুন।\n4. যদি একই কী একই স্কোপের জন্য আবার আসে, কিছুই তৈরি না করে সংরক্ষিত রেসপন্স (মূল রেকর্ড id সহ) ফিরিয়ে দিন।\n5. দ্রুত ক্লিক, ক্লায়েন্ট রিট্রাই, এবং নেটওয়ার্ক টাইমআউট নকল করে স্ট্রেস টেস্ট করুন। নিশ্চিত করুন ঠিক একটিই নতুন রেকর্ড হচ্ছে।\n\n### সার্ভার লজিক কেমন দেখায়\n\nকী বিষয়টি হলো—“চেক + স্টোর” concurrency-র অধীনে নিরাপদ হতে হবে। বাস্তবে, আপনি idempotency রেকর্ডকে (scope, key) উপর ইউনিক কনস্ট্রেইন্ট দিয়ে সংরক্ষণ করবেন এবং কনফ্লিক্টকে পুনরায় ব্যবহার করার সিগন্যাল হিসাবে বर्तবি করবেন।\n\n```text if idempotency_record exists for (user_id, key): return saved_response else: begin transaction create the row save response under (user_id, key) commit return response

\nউদাহরণ: একজন কাস্টমার “Create invoice” চাপছে, অ্যাপ কী পাঠায় `abc123`, এবং সার্ভার invoice `inv_1007` তৈরি করে। ফোন সিগন্যাল হারালে এবং পুনরায় পাঠালে, সার্ভার একই `inv_1007` রেসপন্স দেয়, `inv_1008` নয়।\n\nটেস্ট করার সময় শুধু “ডবল ক্লিক” এ আটকে থাকবেন না। এমন একটি অনুরোধ নকল করুন যা ক্লায়েন্টে টাইমআউট হয় কিন্তু সার্ভারে সম্পন্ন হয়, তারপর একই কী দিয়ে রিট্রাই করুন।\n\n## দুর্ঘটনাপূর্ণ ডবল সাবমিট বাধা দেওয়ার UI উপায়সমূহ\n\nসার্ভার‑সাইড প্রতিরোধ গুরুত্বপূর্ণ, কিন্তু অনেক ডুপ্লিকেট এখনও মানুষের সাধারণ আচরণ থেকে শুরু হয়। ভাল UI নিরাপদ পথটাকে স্পষ্ট করে তোলে।\n\nইউজার সাবমিট করার সঙ্গে সঙ্গেই সাবমিট বাটন ডিজেবল করুন। প্রথম ক্লিকেই করুন—ভ্যালিডেশনের পরে বা রিকোয়েস্ট শুরু হওয়ার পরে নয়। যদি ফর্মটি একাধিক কন্ট্রোলে সাবমিট করতে পারে (একটি বাটন এবং Enter), পুরো ফর্ম স্টেট লক করুন, কেবল একটি বাটন নয়।\n\nএকটি স্পষ্ট প্রগ্রেস স্টেট দেখান যা একটাই প্রশ্নের উত্তর দেয়: এটা কাজ করছে কি? একটি সাধারণ “Saving...” লেবেল বা স্পিনার যথেষ্ট। লেআউট স্থিতিশীল রাখুন যাতে বাটন চারপাশে ঝাঁপিয়ে না পড়ে এবং দ্বিতীয় ক্লিকের প্রলোভন না তৈরি করে।\n\nকিছু সহজ নিয়ম বেশিরভাগ ডবল সাবমিট আটকায়: সাবমিট হ্যান্ডলার শুরুতেই একটি `isSubmitting` ফ্ল্যাগ সেট করুন, এটি সত্য থাকা অবস্থায় নতুন সাবমিটগুলো উপেক্ষা করুন (ক্লিক এবং Enter‑এর জন্য), এবং বাস্তব রেসপন্স পাওয়া না পর্যন্ত এটিকে ক্লিয়ার করবেন না।\n\nধীর রেসপন্সই অনেক অ্যাপ পেছনে ফেলে। যদি আপনি বাটন একটি নির্দিষ্ট টাইমারের পরে পুনরায় সক্রিয় করেন (যেমন 2 সেকেন্ড পরে), ব্যবহারকারীরা তখনও আবার সাবমিট করতে পারে যখন প্রথম রিকোয়েস্ট এখনো ফ্লাইটে আছে। কেবল যখন প্রচেষ্টা সম্পন্ন হয় তখনই পুনরায় সক্রিয় করুন।\n\nসফলতার পরে, পুনরায় সাবমিশন সম্ভবত কম রাখুন। ন্যাভিগেট করে দিন (নতুন রেকর্ড পেজ বা লিস্টে) বা একটি স্পষ্ট সফল স্টেট দেখান যেখানে তৈরি হওয়া রেকর্ড দৃশ্যমান। একই ভর্তি ফর্ম স্ক্রিন রেখে বাটন সক্রিয় রেখে দেবেন না।\n\n## পরিকল্পনা করা এজ কেসগুলো (ট্যাব, রিফ্রেশ, মোবাইল)\n\nদৃঢ় ডুপ্লিকেট বাগগুলো দৈনন্দিন “অদ্ভুত কিন্তু সাধারণ” আচরণ থেকে আসে: দুইটি ট্যাব, রিফ্রেশ, বা ফোন সিগন্যাল গেলা।\n\nপ্রথমে ইউনিকনেস সঠিকভাবে স্কোপ করুন। “ইউনিক” বিরলভাবে মানে ডাটাবেস জুড়ে ইউনিক। এটি হতে পারে প্রতি ব্যবহারকারী একক, প্রতি ওয়ার্কস্পেস একক, বা প্রতি টেন্যান্ট একক। আপনি যদি কোনো বাহ্যিক সিস্টেমের সঙ্গে সিঙ্ক করেন, তাহলে হয়তো বাহ্যিক সোর্স এবং তার external ID অনুযায়ী ইউনিকনেস লাগবে। নিরাপদ পদ্ধতি হল আপনি ঠিক কোন বাক্যটি বোঝাতে চান সেটা লেখে রাখুন (উদাহরণ: “প্রতি টেন্যান্ট প্রতি বছরে একটি ইনভয়েস নম্বর”), তারপর তা এনফোর্স করুন।\n\nমাল্টি‑ট্যাব আচরণ ক্লাসিক ফাঁদ। এক ট্যাবে UI লোডিং স্টেট সহায়ক হলেও তা ট্যাব-আসন্ন অন্য ট্যাবকে কভার করবে না। এই ক্ষেত্রে সার্ভার‑সাইড ডিফেন্সই শেষ ধারা বজায় রাখে।\n\nBack বাটন এবং রিফ্রেশ দুর্ঘটনাজনিত পুনরায় সাবমিট ট্রিগার করতে পারে। সফল create-এর পরে ব্যবহারকারীরা প্রায়ই রিফ্রেশ করে “চেক” করেন, বা Back চাপলে এমন ফর্ম ফিরে পেতে পারেন যা এখনও সম্পাদনাযোগ্য দেখায়। একটি তৈরি ভিউ প্রাধান্য দিন ফর্মটিকে রেখে দেওয়ার বদলে, এবং সার্ভারকে নিরাপদ রিপ্লে হ্যান্ডল করতে দিন।\n\nমোবাইল টি ব্যাঘাত জুড়ে বাড়ায়: ব্যাকগ্রাউন্ডে যাওয়া, দুর্বল নেটওয়ার্ক, এবং স্বয়ংক্রিয় রিট্রাই। একটি রিকোয়েস্ট সফল হতে পারে, কিন্তু অ্যাপ রেসপন্স পায় না, তাই resume-এ আবার চেষ্টা করে।\n\n## সাধারণ ভুল ও ফাঁদগুলো\n\nসর্বাধিক সাধারণ ব্যর্থতা মোড হল UI-কে কেবলমাত্র গার্ডরেল হিসেবে দেখা। ডিসেবল করা বাটন এবং স্পিনার সাহায্য করে, কিন্তু রিফ্রেশ, খারাপ মোবাইল নেটওয়ার্ক, ব্যবহারকারী দ্বিতীয় ট্যাব খোলা, বা ক্লায়েন্ট বাগ কভার করে না। সার্ভার এবং ডাটাবেসটিকে এখনও বলার ক্ষমতা থাকতে হবে "এই create ইতিমধ্যেই হয়েছে"।\n\nআরেকটি ফাঁদ হল ভুল ফিল্ডকে ইউনিক ধরা। যদি আপনি এমন কিছুতে ইউনিক কনস্ট্রেইন্ট সেট করেন যা প্রকৃতপক্ষে ইউনিক নয় (যেমন একটি শেষ নাম, রাউন্ড করা টাইমস্ট্যাম্প, বা ফ্রি-ফর্ম টাইটেল), আপনি বৈধ রেকর্ড ব্লক করে দেবেন। বরং একটি বাস্তব আইডেন্টিফায়ার ব্যবহার করুন (যেমন একটি external provider ID) অথবা একটি স্কোপড রুল (প্রতি ব্যবহারকারী, প্রতি দিন, বা প্রতি প্যারেন্ট রেকর্ড)।\n\nIdempotency কীও ভুলভাবে বাস্তবায়ন করা সহজ। যদি ক্লায়েন্ট প্রত্যেক রিট্রাইতে নতুন কী জেনারেট করে, আপনি প্রতিবারই নতুন ক্রিয়েট পাবেন। একই কী পুরো ব্যবহারকারীর ইচ্ছার জন্য প্রথম ক্লিক থেকে যেকোন রিট্রাই পর্যন্ত অপরিবর্তিত রাখুন।\n\nপুনরায় চেষ্টা করলে আপনি কি রিটার্ন করছেন তাও লক্ষ্য করুন। যদি প্রথম অনুরোধটি রেকর্ড তৈরি করে, রিট্রাই-এ একই ফল (অন্তত একই রেকর্ড ID) ফেরত দিন, কনফিউজিং ত্রুটি নয় যা ব্যবহারকারীকে আবার চেষ্টা করায়।\n\nযদি একটি ইউনিক কনস্ট্রেইন্ট ডুপ্লিকেট ব্লক করে, সেটিকে “কিছু ভুল হয়েছে” দিয়ে আড়াল করবেন না। কী ঘটেছে তা সাধারণ ভাষায় বলুন: “এই ইনভয়েস নম্বর ইতিমধ্যে আছে। আমরা মূলটি রাখেছি এবং দ্বিতীয়টি তৈরি করিনি।”\n\n## রিলিজের আগে দ্রুত চেকলিস্ট\n\nরিলিজের আগে ডুপ্লিকেট ক্রিয়েশন পাথগুলোর জন্য একটি দ্রুত পাস করুন। সবচেয়ে ভালো ফলাফল আসে বহুস্তরীয় প্রতিরোধ থেকে, যাতে একটি চুচুনি ক্লিক, রিট্রাই, বা ধীর নেটওয়ার্কও দুইটি রো তৈরি করতে না পারে।\n\nতিনটি জিনিস নিশ্চিত করুন:\n\n- ডাটাবেস নিয়ম বাস্তবতার সঙ্গে মিলে (উত্পাদনে ইউনিক কনস্ট্রেইন্ট আছে এবং সঠিকভাবে স্কোপ করা)।\n- গুরুত্বপূর্ণ create এন্ডপয়েন্টগুলো পুনরাবৃত্তির জন্য নিরাপদ (idempotency কী একই আউটকাম দেয়)।\n- UI স্টেট স্পষ্ট (ডিসেবল করা সাবমিট, পরিষ্কার প্রগ্রেস, এবং সফলতার পর স্পষ্ট ভিউ)।\n\nএকটি ব্যবহারিক গাট চেক: ফর্ম খুলুন, দ্রুত দুইবার সাবমিট করুন, তারপর মিড‑সাবমিটে রিফ্রেশ করে আবার চেষ্টা করুন। যদি আপনি দুটি রেকর্ড তৈরি করতে পান, ব্যবহারকারীরাও পাবে।\n\n## উদাহরণ: ইনভয়েস তৈরি করলেই ডুপ্লিকেট নেই\n\nএকটি ছোট ইনভয়েসিং অ্যাপ কল্পনা করুন। একজন ব্যবহারকারী নতুন ইনভয়েস ভর্তি করে Create ট্যাপ করে। নেটওয়ার্ক ধীর, স্ক্রিন তৎক্ষণাৎ বদলে না, এবং তারা আবার Create ট্যাপ করে।\n\nশুধু UI প্রটেকশনে, আপনি বাটন ডিসেবল করে স্পিনার দেখাতে পারেন। সেটা সাহায্য করে, কিন্তু যথেষ্ট নয়। কিছু ডিভাইসে ডাবল ট্যাপ এখনও ফাঁস হতে পারে, টাইমআউটের পরে রিট্রাই হতে পারে, অথবা ব্যবহারকারী দুইটি ট্যাব থেকে সাবমিট করতে পারে।\n\nশুধু ডাটাবেস ইউনিক কনস্ট্রেইন্ট থাকলে আপনি সঠিকভাবে ডুপ্লিকেট বন্ধ করতে পারেন, কিন্তু UX খারাপ হতে পারে। প্রথম অনুরোধ সফল হয়, দ্বিতীয়টি কনস্ট্রেইন্টে ধাক্কা খায়, এবং ব্যবহারকারী ত্রুটি দেখে যদিও ইনভয়েস তৈরি হয়ে গেছে।\n\nপরিষ্কার ফলাফল হল idempotency প্লাস ইউনিক কনস্ট্রেইন্ট:\n\n- ক্লায়েন্ট create অ্যাকশনের জন্য একটি idempotency কী জেনারেট করে এবং রিট্রাইতে পুনরায় ব্যবহার করে।\n- সার্ভার কী এবং create রেজাল্ট সংরক্ষণ করে এবং রিপীট হলে একই ইনভয়েস ID ফেরত দেয়।\n- ইউনিক কনস্ট্রেইন্ট আপনাকে রক্ষা করে যদি দুটি ভিন্ন কী একই ইনভয়েস নম্বর তৈরি করার চেষ্টা করে।\n\nদ্বিতীয় ট্যাপে একটি সহজ UI বার্তা: “Invoice তৈরি হয়েছে — আমরা ডুপ্লিকেট সাবমিশন উপেক্ষা করেছি এবং আপনার প্রথম অনুরোধ রক্ষা করেছি।”\n\n## পরের ধাপ: অ্যাপ বড় হলে নির্ভরযোগ্য রাখুন\n\nবেসলাইন বসানোর পরে, পরবর্তী জিতগুলো দৃশ্যমানতা, ক্লিনআপ, এবং সামঞ্জস্য সম্পর্কে।\n\nক্রিয়েট পাথের চারপাশে হালকা লগিং যোগ করুন যাতে আপনি আসল ব্যবহারকারীর অ্যাকশন এবং রিট্রাই আলাদাভাবে চিনতে পারেন। idempotency কী, জড়িত ইউনিক ফিল্ডগুলো, এবং আউটকাম (created vs returned existing vs rejected) লগ করুন। শুরুতে আপনাকে ভারী টুলিং দরকার নেই।\n\nযদি ডুপ্লিকেট ইতিমধ্যেই থাকে, একটি স্পষ্ট রুল এবং অডিট ট্রেইল দিয়ে সেগুলো ক্লিন করুন। উদাহরণ: পুরনো রেকর্ডকে “জয়ী” রাখুন, সম্পর্কিত রো (পেমেন্ট, লাইন আইটেম)গুলো পুনরায় অনুমোদন করুন, এবং বাকি গুলোকে merged হিসেবে মার্ক করুন মুছে ফেলার বদলে। এতে সাপোর্ট ও রিপোর্টিং সহজ হয়।\n\nআপনার ইউনিকনেস ও idempotency রুলগুলো এক জায়গায় লিখে রাখুন: কী ইউনিক এবং কোন স্কোপে, idempotency কী কতক্ষণ বাস করবে, ত্রুটিগুলো কেমন দেখাবে, এবং UI রিট্রাই-এ কিভাবে আচরণ করবে—এতে নতুন এন্ডপয়েন্টগুলো ভালো নিরাপত্তা ছাড়া তৈরি হওয়া বন্ধ হবে।\n\nআপনি যদি দ্রুত CRUD স্ক্রিন Koder.ai (koder.ai) নিয়ে বানান, তবে এগুলোকে আপনার ডিফল্ট টেমপ্লেটের অংশ করা মূল্যবান: স্কিমায় ইউনিক কনস্ট্রেইন্ট, API-তে idempotent create এন্ডপয়েন্ট, এবং UI-তে পরিষ্কার লোডিং স্টেট। এভাবে গতি বিশৃঙ্খলার দামে আসে না।

সাধারণ প্রশ্ন

একটি CRUD অ্যাপে ডুপ্লিকেট রেকর্ড ঠিক কীভাবে গণ্য হবে?

একই বাস্তব জিনিস দুটি বার স্টোর করা হলে সেটাই ডুপ্লিকেট রেকর্ড — উদাহরণস্বরূপ একই চেকআউটের জন্য দুইটি অর্ডার বা একই সমস্যার জন্য দুইটি টিকিট। এটি সাধারণত ঘটে যখন একই “create” অ্যাকশন বারবার চলে: ব্যবহারকারীর ডবল সাবমিট, রিট্রাই, বা কনকারেন্ট রিকোয়েস্টের কারণে।

কেন ব্যবহারকারী কেবল একবার ক্লিক করলেও ডুপ্লিকেট হয়?

কারণ অনেক সময় ব্যবহারকারী শুধু একবারই ক্লিক করে, কিন্তু অন্য কোনো উপায়ে দ্বিতীয়বার ক্রিয়েট ট্রিগার হতে পারে — যেমন মোবাইলের ডবল-ট্যাপ বা Enter চাপার পরে বাটনে ক্লিক করা। এছাড়া ক্লায়েন্ট, নেটওয়ার্ক, বা সার্ভার টাইমআউটের পরে রিকোয়েস্ট রিট্রাই করতে পারে, তাই সার্ভার ধরে নিতে পারে না যে "POST মানে একবারই"।

ডিসেবল করা সাবমিট বাটন কি ডুপ্লিকেট প্রতিরোধের জন্য যথেষ্ট?

নিশ্চিতভাবে নয়। বাটন ডিসেবল করা এবং “Saving…” দেখানো দুর্ঘটনামূলক ডবল সাবমিট কমায়, কিন্তু খারাপ নেটওয়ার্ক, রিফ্রেশ, মাল্টি-ট্যাব, ব্যাকগ্রাউন্ড ওয়ার্কার বা webhook পুনরায় ডেলিভারি থেকে আসা রিট্রাই আটকাতে সক্ষম নয়। সার্ভার এবং ডাটাবেস লেভেলের প্রতিরোধও দরকার।

ডাটাবেসে আমি কী ফিল্ডকে ইউনিক কনস্ট্রেইন্ট হিসেবে রাখব?

ডাটাবেসে ইউনিক কনস্ট্রেইন্ট হল শেষ লাইন অফ ডিফেন্স — এটি একই সময়ে দুইটি ইনসার্ট এলে ওগুলোকে ব্লক করে। সাধারণত এমন একটি বাস্তব-বিশ্ব ইউনিকনেস রুল বেছে নিন এবং সেটা ডাটাবেসে এনফোর্স করুন (প্রায়শই স্কোপ করা হয়, যেমন প্রতি tenant বা workspace)।

আমাকে কি ইউনিক কনস্ট্রেইন্ট এবং idempotency কী—দুটি-ই রাখতেই হবে?

উভয়ই দরকার। ইউনিক কনস্ট্রেইন্টগুলি ফিল্ড-ভিত্তিক duplicate ব্লক করে (যেমন ইনভয়েস নম্বর), আর idempotency কী একটি নির্দিষ্ট create চেষ্টা পুনরাবৃত্তির জন্য নিরাপদ করে (একই কী হলে একই রেজাল্ট ফিরে দেয়)। উভয় ব্যবহার করলে আপনি নিরাপত্তা পাবেন এবং রিট্রাই হলে ব্যবহারকারীর অভিজ্ঞতাও ভালো থাকবে।

ক্লায়েন্ট কিভাবে idempotency কী জেনারেট ও পুনরায় ব্যবহার করবে?

প্রতিটি ব্যবহারকারীর ইচ্ছে (উদাহরণ: “Create” চাপা) জন্য একটি কী জেনারেট করুন, প্রথম ক্লিক থেকে যে একই ইচ্ছার উপর যে কোন রিট্রাই জোরে ততক্ষণ পর্যন্ত একই কী পুনর্ব্যবহার করুন, এবং প্রতিবার রিকোয়েস্টের সঙ্গে কী পাঠান। কীটি টাইমআউট বা অ্যাপ রিজিউম হওয়ার পরও স্থিতিশীল থাকা উচিত, কিন্তু আলাদা কোনো নতুন create-এর জন্য পুনরায় ব্যবহার না করা উচিত।

সার্ভার একই idempotency কী আবার পেলে কী করা উচিত?

স্কোপ (যেমন user বা account), এন্ডপয়েন্ট, এবং idempotency কী দিয়ে একটি idempotency রেকর্ড সংরক্ষণ করুন এবং প্রথম সফল অনুরোধের জন্য যা রেসপন্স দিয়েছিলেন তা সংরক্ষণ করুন। একই কী আবার এলে, নতুন কিছু তৈরি না করে সেই সংরক্ষিত রেসপন্স (এবং একই রেকর্ড ID) ফিরিয়ে দিন।

কনকারেন্সি এবং রেস কন্ডিশনে idempotency কিভাবে নিরাপদ করা যায়?

কনকারেন্সি-সেফ "check + store" প্যাটার্ন ব্যবহার করুন, সাধারণত idempotency রেকর্ড-এ (scope, key) উপর ইউনিক কনস্ট্রেইন্ট লাগিয়ে। এতে করে দুইটি কাছাকাছি সম-সময়ের অনুরোধই একইভাবে প্রথম দাবি করতে পারবে না; একটির ক্ষেত্রে কনফ্লিক্ট হয়ে একটি ফল পুনঃব্যবহার করা হবে।

idempotency রেকর্ড কতক্ষণ রাখব?

বাস্তব রিট্রাই ঢেকে দেয়ার জন্য যথেষ্ট সময় রাখুন; সাধারণত 24 ঘন্টার বেসলাইন হয়, পেমেন্টের মতো ফ্লোতে 48-72 ঘণ্টাও থাকে। একটি TTL রাখলে স্টোরেজ নিয়ন্ত্রিত থাকে এবং কী কতদূর পর্যন্ত প্রাসঙ্গিক তা মেলে।

ডুপ্লিকেট create ধরা পড়লে ব্যবহারকারীকে কি দেখানো উচিত?

যদি duplicate create স্পষ্টভাবে একই ইচ্ছা থাকে, তখন সেটাকে সফল রিট্রাই হিসাবে গণ্য করুন এবং মূল তৈরি করা রেকর্ড (একই ID) ফেরত দিন। যদি কোনো ফিল্ড সত্যিই ইউনিক হওয়া উচিত (যেমন ইমেল), তাহলে পরিষ্কার কনফ্লিক্ট বার্তা দিন এবং ব্যাখ্যা করুন কি আছে এবং পরবর্তীতে কি ঘটেছে।

Related posts