অ্যাডমিন টুলের জন্য অপটিমিস্টিক লকিং: নীরব ওভাররাইট প্রতিরোধ করুন
ভার্সন কলাম বা `updated_at` চেক ব্যবহার করে অ্যাডমিন টুলে অপটিমিস্টিক লকিং শিখুন—সহজ UI প্যাটার্নসহ এডিট কনফ্লিক্টগুলো নীরব ওভাররাইট ছাড়াই কীভাবে হ্যান্ডল করবেন।

সমস্যা: অনেকেই একসঙ্গে এডিট করলে নীরব ওভাররাইট\n\n“নীরব ওভাররাইট” ঘটে যখন দুজনই একই রেকর্ড খুলে আলাদা‑আলাদা পরিবর্তন করে এবং শেষে যিনি সেভ করেন তিনি জিতে যান। প্রথম ব্যক্তির পরিবর্তনগুলি কোনো সতর্কতা ছাড়াই মুছে যায় এবং সাধারণত সেগুলি ফিরিয়ে আনা কঠিন।\n\nব্যস্ত অ্যাডমিন প্যানেলে এটা সারাদিন ঘটতে পারে আর কেউ খেয়ালও রাখে না। মানুষ একাধিক ট্যাব খোলা রাখে, টিকিটের মধ্যে ঝাঁপিয়ে পড়ে, এবং ২০ মিনিট ধরে খোলা একটি ফর্মে ফিরে আসে। যখন তারা সেভ করে, তারা সর্বশেষ ভার্সন আপডেট করছে না—তারা ওভাররাইট করছে।\n\nএটা ব্যাক‑অফিস টুলে বেশি দেখা যায় কারণ কাজটি সহযোগিতামূলক এবং রেকর্ড‑ভিত্তিক। অভ্যন্তরীণ দলগুলো একই কাস্টমার, অর্ডার, প্রোডাক্ট ও রিকোয়েস্ট বারবার সম্পাদনা করে, প্রায়ই স্বল্প বিরতিতে। পাবলিক অ্যাপে সাধারণত “একজন ব্যবহারকারী নিজের কিছুর ওপর কাজ করে,” আর অ্যাডমিন টুলে হয় “অনেক ব্যবহারকারী শেয়ার্ড কিছুর ওপর কাজ করে।”\n\nক্ষতি তৎক্ষণাত বড় না হলেও দ্রুত জমে যায়:\n\n- প্রোমো আপডেটের পরে প্রোডাক্টের দাম পুরোনো মানে ফিরে যেতে পারে।\n- এক এজেন্টের ইন্টারনাল নোট হারিয়ে যায়, ফলে পরের এজেন্ট একই ট্রাবলশুটিং আবার করে।\n- অর্ডার স্ট্যাটাস পিছনে যায় (যেমন “Shipped” থেকে “Packed”), ভুল ফলো‑আপ ট্রিগার করে।\n- কাস্টমারের ফোন নম্বর বা ঠিকানা পুরোনো তথ্য দিয়ে রিপ্লেস হয়ে যায়।\n\nনীরব ওভাররাইট ক্ষতিকর কারণ সবাই ভাবছে সিস্টেম সেভ ঠিক করেছে। কোনো স্পষ্ট “কিছু ভুল হয়েছে” মুহূর্ত নেই, শুধু পরে রিপোর্ট অথবা সহকর্মীর প্রশ্ন “এইটা কে বদলিয়েছে?”—যা বিভ্রান্তি তৈরি করে।\n\nএকমাত্র উদ্দেশ্য হলো দুইজনকে সম্পাদন বন্ধ করা নয়, বরং চিহ্নিত করা যে কেউ এডিট করার সময় রেকর্ড বদলেছে এবং সেই মুহূর্তটি নিরাপদভাবে হ্যান্ডল করা।\n\nআপনি যদি AppMaster-এর মতো একটি নো‑কোড প্ল্যাটফর্মে ইন্টারনাল টুল বানান, তবে শুরু থেকেই এটি পরিকল্পনা করা উচিত। অ্যাডমিন টুল দ্রুত বাড়ে, এবং একবার দল নির্ভর করতে শুরু করলে মাঝেমধ্যে ডাটা হারানো অভ্যস্ত হওয়া নির্ভরযোগ্যতাকে ক্ষুণ্ণ করে।\n\n## অপটিমিস্টিক লকিং সহজভাবে\n\nযখন দুইজন একই রেকর্ড খুলে এবং দুজনেই সেভ করে, তখন আপনার কাছে concurrency আছে। প্রত্যেকেই একটি পুরোনো স্ন্যাপশট থেকে শুরু করেছে, কিন্তু কেবল একজনই সেভ হওয়ার সময় “সর্বশেষ” হতে পারে।\n\nসুরক্ষার ব্যতীত, শেষ সেভ জিতবে—এটাই নীরব ওভাররাইটের কারণ: দ্বিতীয় সেভ চুপচাপ প্রথমজনের পরিবর্তনগুলো প্রতিস্থাপন করে।\n\nঅপটিমিস্টিক লকিং একটি সহজ নিয়ম: “আমি আমার পরিবর্তন তখনই সেভ করব যখন রেকর্ডটা ঠিক সেই অবস্থায় আছে যেমনটা আমি এডিট শুরু করার সময় দেখেছিলাম।” যদি রেকর্ড মাঝখানে বদলে যায়, সেভটি প্রত্যাখ্যাত হবে এবং ব্যবহারকারীকে কনফ্লিক্ট দেখানো হবে।\n\nএটা পেসিমিস্টিক লকিং থেকে আলাদা, যার অর্থ হচ্ছে “আমি এটা এডিট করছি, তো আর কেউ পারবে না।” পেসিমিস্টিক লকিং সাধারণত হার্ড লক, টাইমআউট এবং ব্লকিং নিয়ে আসে। এই ধরনের লকিং নির্দিষ্ট কেসে দরকার হতে পারে (যেমন এক অ্যাকাউন্ট থেকে আরেকটায় টাকা স্থানান্তর), কিন্তু ব্যস্ত অ্যাডমিন টুলে এটি অপ্রত্যাশিতভাবে বিরক্তিকর হয়ে ওঠে।\n\nঅপটিমিস্টিক লকিং সাধারণত ভাল ডিফল্ট কারণ এটি কাজের প্রবাহ বজায় রাখে। মানুষ পাশাপাশি এডিট করতে পারে, আর সিস্টেম কেবল তখনই হস্তক্ষেপ করে যখন বাস্তব সংঘর্ষ ঘটে।\n\nএটা সবচেয়ে ভাল কাজ করে যখন:\n\n- কনফ্লিক্ট সম্ভাব্য কিন্তু ঘন ঘন নয়।\n- এডিটগুলো দ্রুত (কয়েকটি ফিল্ড, ছোট ফর্ম)।\n- অন্যদের ব্লক করা দলের গতি ধীর করে দেবে।\n- আপনি স্পষ্টভাবে “কেউ এটি আপডেট করেছে” মেসেজ দেখাতে পারেন।\n- আপনার API প্রতিটি আপডেটে একটি ভার্সন (বা টাইমস্ট্যাম্প) চেক করতে পারে।\n\nএটা “চুপচাপ ওভাররাইট” সমস্যা প্রতিরোধ করে—ডেটা হারালে বদলে একটি পরিষ্কার স্টপ পাওয়া যায়: “এই রেকর্ডটি আপনি খোলার পর থেকে বদলেছে।”\n\nকিন্তু কি করতে পারে না তা বুঝে নেওয়া জরুরি। এটা দুইজনকে একই পুরোনো তথ্যের ওপর ভিন্ন সিদ্ধান্ত নিতে আটকাবে না, এবং এটি নিজে থেকে পরিবর্তনগুলো মার্জ করে দেবে না। আর যদি আপনি সার্ভার‑সাইডে চেক না করেন, তাহলে সমস্যার সমাধান হয়নি।\n\nসাধারণ সীমাবদ্ধতা মনে রাখবেন:\n\n- এটা কনফ্লিক্টগুলো স্বয়ংক্রিয়ভাবে মিটাবে না (আপনাকে সিদ্ধান্ত নিতে হবে)।\n- অফলাইন এডিট করে পরে সিঙ্ক করলে এটি সাহায্য করবে না যদি চেক না থাকে।\n\n- ভুল পারমিশন থাকলে সেটা ঠিক করবে না (কেউ এখনও অনিচ্ছাকৃতভাবে সম্পাদনা করতে পারবে)।\n- যদি কেবল ক্লায়েন্ট‑সাইডে চেক করা হয়, তা কনফ্লিক্ট ধরবে না।\n\nপ্রায়োগিকভাবে, অপটিমিস্টিক লকিং হচ্ছে প্রতিটি এডিটের সঙ্গে একটি অতিরিক্ত মান পাঠানো, এবং সার্ভার‑সাইডে “শুধুমাত্র ম্যাচ করলে আপডেট কর” নিয়ম। AppMaster-এ এই চেক সাধারণত আপনার বিজনেস লজিকে থাকে যেখানে আপডেটগুলো কার্যকর করা হয়।\n\n## দুইটি সাধারণ পদ্ধতি: version কলাম বনাম updated_at\n\nরেকর্ডটি এডিট করার সময় বদলেছে কিনা তা সনাক্ত করতে সাধারণত দুইটি সিগন্যালই নেওয়া হয়: একটি version নম্বর বা একটি updated_at টাইমস্ট্যাম্প।\n\n### পদ্ধতি 1: Version কলাম (ইনক্রিমেন্টিং পূর্ণসংখ্যা)\n\nversion নামের একটি ফিল্ড যোগ করুন (সাধারণত একটি integer)। ফর্ম লোড করার সময় কারেন্ট version পড়ুন। সেভ করার সময় সেই একই মান ফিরিয়ে দিন।\n\nআপডেট কেবল তখনই সফল হবে যদি সংরক্ষিত version এখনও আপনার শুরু করার সময়ের মতোই থাকে। যদি মিলে যায়, রেকর্ড আপডেট করুন এবং version 1 বাড়ান। যদি না মিলে, ওভাররাইট না করে একটি কনফ্লিক্ট রিটার্ন করুন।\n\nএটা ব্যাখ্যা করা সহজ: version 12 মানে “এটি 12 নম্বর পরিবর্তন।” এটাও টাইম‑সংক্রান্ত এজ‑কেস এড়ায়।\n\n### পদ্ধতি 2: updated_at (টাইমস্ট্যাম্প তুলনা)\n\nঅধিকাংশ টেবিলে ইতোমধ্যেই updated_at ফিল্ড থাকে। ধারণা একই: ফর্ম খুললে updated_at পড়ে রাখুন, তারপর সেভে সেটা পাঠান। সার্ভার কেবল তখনই আপডেট করবে যদি updated_at অপরিবর্তিত থাকে।\n\nকিন্তু টাইমস্ট্যাম্পের কিছু জটিলতা আছে। বিভিন্ন ডেটাবেস বিভিন্ন প্রিসিশন রাখে। কিছু ক্ষেত্রে সেকেন্ডে রাউন্ডিং হয়, যা দ্রুত হওয়া এডিট মিস করে দিতে পারে। একই ডেটাবেসে একাধিক সিস্টেম লেখালেখি করে থাকলে ক্লক‑ড্রিফট এবং টাইমজোন হ্যান্ডলিংও বিভ্রান্তিকর হতে পারে।\n\nসহজ তুলনা বলা যায়:\n\n- Version কলাম: পরিষ্কার আচরণ, ডেটাবেস‑পারপাসে বহনযোগ্য, কোন ক্লক‑সমস্যা নেই।\n- updated_at: প্রায়ই “ফ্রি” কারণ এটা ইতিমধ্যেই আছে, কিন্তু প্রিসিশন এবং ক্লক‑হ্যান্ডলিং সমস্যার ঝুঁকি আছে।\n\nঅধিকাংশ টিমের জন্য version কলাম প্রাইমারি সিগন্যাল হিসেবে ভাল। এটি স্পষ্ট, প্রত্যাশিত এবং লগ বা সাপোর্ট টিকেটে রেফার করতে সহজ।\n\nAppMaster-এ এটি মানে Data Designer-এ একটি integer version ফিল্ড যোগ করা এবং আপডেট লজিকে সেভ করার আগে সেটা চেক করানো। updated_at লগিংর জন্য রাখা যেতে পারে, কিন্তু সিরিয়াস কনকারেন্সি সিদ্ধান্তে version‑কেই প্রাধান্য দিন।\n\n## প্রতিটি এডিটে কি রাখা উচিত এবং কি পাঠাতে হবে\n\nঅপটিমিস্টিক লকিং কাজ করবে যদি প্রতিটি এডিট একটি “last seen” মার্কার নিয়ে আসে—এটা version নম্বর বা updated_at টাইমস্ট্যাম্প হতে পারে। সেটা ছাড়া সার্ভার জানতে পারবে না ইউজার টাইপ করার সময় রেকর্ড বদলেছে কি না।\n\nরেকর্ডে আপনার নরমাল ব্যবসায়িক ফিল্ডগুলোর পাশাপাশি একটি concurrency ফিল্ড থাকা উচিত যা সার্ভারই কন্ট্রোল করে। ন্যূনতম সেট দেখতে এভাবেঃ\n\n- id (স্থিতিশীল আইডেন্টিফায়ার)\n- ব্যবসায়িক ফিল্ড (name, status, price, notes, ইত্যাদি)\n- version (প্রতিটি সফল আপডেটে ইনক্রিমেন্ট) অথবা updated_at (সার্ভার‑লিখিত টাইমস্ট্যাম্প)\n\nযখন এডিট স্ক্রিন লোড হয়, ফর্মটি সেই last‑seen মানটি স্টোর করবে। ব্যবহারকারী সেটা এডিট করবে না—এটি হিডেন ফিল্ড বা ফর্ম স্টেটে রাখুন। উদাহরণ: API version: 12 রিটার্ন করে, ফর্ম 12 টা ধরে রাখে যতক্ষণ না সেভ করা হচ্ছে।\n\nসেভ করার সময় দুটি জিনিস পাঠান: পরিবর্তনগুলো এবং last‑seen মার্কার। সবচেয়ে সোজা রিকোয়েস্ট বডি হবে id, বদলানো ফিল্ডগুলো, এবং expected_version (বা expected_updated_at)। AppMaster‑এ UI‑তে এটি অন্য বাউন্ড ভ্যালুর মত ট্রিট করুন: রেকর্ড লোড করুন, অপরিবর্তিত রাখুন, আপডেটে ফিরিয়ে দিন।\n\nসার্ভারে আপডেট কন্ডিশনাল হতে হবে। কেবলমাত্র যদি expected মান ডেটাবেসের সাথে মিলে তখনই আপডেট করবেন। কনফ্লিক্ট রেসপন্সটি UI‑তে হ্যান্ডল করার জন্য পরিষ্কার হওয়া উচিত। একটি প্রাকটিক্যাল কনফ্লিক্ট রেসপন্সে থাকতে পারে:\n\n- HTTP status 409 Conflict\n- সংক্ষিপ্ত বার্তা: “এই রেকর্ডটি অন্য কেউ আপডেট করেছে।”\n- বর্তমান সার্ভারের মান (current_version বা current_updated_at)\n- ঐচ্ছিকভাবে, বর্তমান সার্ভার রেকর্ড (যাতে UI‑তে কী বদলেছে দেখানো যায়)\n\nউদাহরণ: সাম Customer রেকর্ড version 12 এ খুলল। প্রিয়া পরিবর্তন করে version 13 করে সেভ করল। সাম পরে expected_version: 12 নিয়ে সেভ করলে সার্ভার 409 দিয়ে current version 13 রিটার্ন করে। UI তখন সামকে সর্বশেষ মান দেখে সিদ্ধান্ত নেওয়ার সুযোগ দেয় বদলে চুপচাপ ওভাররাইট করার বদলে।\n\n## ধাপে ধাপে: অপটিমিস্টিক লকিং সম্পূর্ণভাবে কিভাবে বাস্তবায়ন করবেন\n\nঅপটিমিস্টিক লকিং মূলত একটাই নিয়মে নেমে আসে: প্রতিটি এডিট প্রুভ করবে যে এটি রেকর্ডের সর্বশেষ সংরক্ষিত সংস্করণের ওপর ভিত্তি করে করা হয়েছে।\n\n### 1) একটি concurrency ফিল্ড যোগ করুন\n\nপ্রতিটি লেখার সময় বদলানো একটি ফিল্ড বেছে নিন।\n\nএকটি ডেডিকেটেড integer version সবচেয়ে সরল। 1 থেকে শুরু করে প্রতিটি আপডেটে 1 করে বাড়ান। যদি আপনার কাছে এমন একটি নির্ভরযোগ্য updated_at থাকে যা প্রতিটি লেখার সময় আপডেট হয়, সেটাও ব্যবহার করা যাবে, কিন্তু নিশ্চিত করুন এটি প্রতিটি write‑এ বদলে (background জবগুলোও)।\n\n### 2) রিডে সেই মানটি ক্লায়েন্টকে পাঠান\n\nযখন UI একটি এডিট স্ক্রীন ওপেন করে, রেসপন্সে কারেন্ট version (বা updated_at) অন্তর্ভুক্ত করুন। ফর্ম স্টেটে এটিকে হিডেন‑ভ্যালু হিসেবে রাখুন।\n\nএকটাকে রশিদ মনে করুন যে, “আমি আমি যা পড়েছি সেটাই আমি এডিট করছি।”\n\n### 3) আপডেটে সেই মানটি বাধ্যতামূলক করুন\n\nসেভ করার সময় ক্লায়েন্ট এডিট করা ফিল্ডগুলোর পাশাপাশি last‑seen concurrency মান পাঠাবে।\n\nসার্ভারে আপডেটটি কন্ডিশনাল করুন। SQL‑ধাঁচে এটি দেখতে এরকম:\n\n```sql
UPDATE tickets
SET status = $1,
version = version + 1
WHERE id = $2
AND version = $3;
```\n\nযদি এই আপডেট 1 সারি প্রভাবিত করে, সেভ সফল হয়েছে। যদি 0 সারি প্রভাবিত করে, কেউ ইতিমধ্যে রেকর্ড পরিবর্তন করেছে।\n\n### 4) সফল হলে নতুন মান রিটার্ন করুন\n\nসফল সেভের পরে, আপডেট করা রেকর্ডটি নতুন version (বা updated_at) সহ ফিরিয়ে দিন। ক্লায়েন্ট সেই রিটার্ন করা রেকর্ড দিয়ে ফর্ম স্টেট রিপ্লেস করবে। এটা একাধিক সেভ‑এজেন্ট օգտագործ করে পুরানো ভার্সন দিয়ে ডাবল সেভ প্রতিরোধ করে।\n\n### 5) কনফ্লিক্টকে সাধারণ ধারণা হিসেবে গ্রহণ করুন\n\nযখন কন্ডিশনাল আপডেট ব্যর্থ হয়, একটি স্পষ্ট কনফ্লিক্ট রেসপন্স (প্রায়ই HTTP 409) দিন যাতে:\n\n- বর্তমান সার্ভার রেকর্ড বর্তমান অবস্থায় দেখায়\n- ক্লায়েন্টের চেষ্টাকৃত পরিবর্তনগুলো (বা পুনর্গঠন করার জন্য পর্যাপ্ত তথ্য)\n- কোন ফিল্ডগুলো ভিন্ন তা (যদি দিতে পারেন)\n\nAppMaster‑এ এটি PostgreSQL মডেল ফিল্ড, একটি রিড এন্ডপয়েন্ট যা ভার্সন রিটার্ন করে, এবং একটি Business Process-এ কন্ডিশনাল আপডেট করে যা সফল বা কনফ্লিক্ট ব্রাঞ্চে ভাগ হয়—এই মানচিত্রটি ভালভাবে ফিট করে।\n\n## কনফ্লিক্ট হ্যান্ডলিং UI প্যাটার্ন (ব্যবহারকারীকে বিরক্ত না করে)\n\nঅপটিমিস্টিক লকিং কেবল কাজে লাগানো মাত্র কাজ হবে না—ব্যবহারকারীকে দেখানো কিভাবে তা মাটিতে নামাতে হবে।\n\nভাল কনফ্লিক্ট UI‑এর দুইটি লক্ষ্য: নীরব ওভাররাইট বন্ধ করা, এবং ব্যবহারকারীকে দ্রুত তার কাজ শেষ করাতে সাহায্য করা। ভালোভাবে করা হলে এটি বাধা না হয়ে সহায়ক হিসেবে মনে হবে।\n\n### প্যাটার্ন 1: সিম্পল ব্লকিং ডায়ালগ (দ্রুততম)\n\nএটা ব্যবহার করুন যখন এডিটগুলো ছোট এবং ব্যবহারকারী রিলোড করে দ্রুত তার পরিবর্তনগুলো আবার প্রয়োগ করতে পারে।\n\nমেসেজ সংক্ষিপ্ত ও স্পষ্ট রাখুন: “এই রেকর্ডটি আপনি এডিট করার সময় পরিবর্তিত হয়েছে। সর্বশেষ ভার্সন দেখার জন্য রিলোড করুন।” তারপর দুটি স্পষ্ট অপশন দিন:\n\n- রিলোড এবং চালিয়ে যান (প্রাইমারি)\n- আমার পরিবর্তনগুলো কপি করুন (ঐচ্ছিক কিন্তু উপকারী)\n\n“আমার পরিবর্তনগুলো কপি করুন” অপশনটি unsaved মানগুলো ক্লিপবোর্ডে রাখবে বা রিলোডের পর ফর্মে রেখে দেবে, যাতে মানুষ আবার সব টেক্সট টাইপ করতে না হয়।\n\nএটি একক‑ফিল্ড আপডেট, টগল, স্ট্যাটাস পরিবর্তন বা ছোট নোটের জন্য ভাল ও সহজ বাস্তবায়নযোগ্য। AppMaster‑এও এটা সহজে বানানো যায়।\n\n### প্যাটার্ন 2: “চেঞ্জ রিভিউ” (উচ্চ‑মূল্যের রেকর্ডে সেরা)\n\nরেকর্ডটি গুরুত্বপূর্ণ হলে (প্রাইসিং, পারমিশন, পেআউট, অ্যাকাউন্ট সেটিংস) বা ফর্মটি দীর্ঘ হলে ব্যবহার করুন। একটি কনফ্লিক্ট স্ক্রিন দেখান যা তুলনা করে:\n\n- “আপনার এডিট” (আপনি যেটা সেভ করতে চেয়েছিলেন)\n- “বর্তমান মান” (ডেটাবেসের সর্বশেষ)\n- “আপনি খুলার পর কী কী বদলেছে” (সংঘর্ষকারী ফিল্ডগুলো)\n\nকোশ্চনগুলোকেই কেন্দ্র করান—যদি শুধু তিনটি ফিল্ড কনফ্লিক্ট করে, সব ফিল্ড দেখাবেন না।\n\nপ্রতিটি কনফ্লিক্ট ফিল্ডের জন্য সরল পছন্দ দিন:\n\n- আমারটা রাখুন\n- ওদেরটা নিন\n- মার্জ করুন (যখন অর্থবহ, যেমন ট্যাগ বা নোট)\n\nরিচ টেক্সট বা দীর্ঘ নোট হলে ছোট ডিফ ভিউ দেখান যাতে ব্যবহারকারী দ্রুত সিদ্ধান্ত নিতে পারে। কনফ্লিক্ট মিট করার পর, সর্বশেষ ভার্সন মান নিয়ে আবার সেভ করুন।\n\n### কখন ফোর্সড ওভাররাইট দিয়ে দেয়া উচিত (আর কারা পারে)\n\nকখনও‑কখনও ফোর্সড ওভাররাইট দরকার হতে পারে, কিন্তু তা বিরল ও নিয়ন্ত্রিত হওয়া উচিত। থাকলে সতর্কভাবে রাখুন: একটা সংক্ষিপ্ত কারণ চাওয়া, করে কারা কবে ফোর্স করেছে লগ করা, এবং সীমিত ভূমিকা (admins/supervisors)‑এর জন্য রাখা।\n\nসাধারণ ব্যবহারকারীর জন্য ডিফল্ট হবে “চেঞ্জ রিভিউ।” রেকর্ড মালিক বা কম‑রিস্ক কেসে ফোর্সড ওভাররাইট যুক্তিসঙ্গত।\n\n## উদাহরণ দৃশ্য: দুই সহকর্মী একই রেকর্ড এডিট করেন\n\nদুই সাপোর্ট এজেন্ট, Maya এবং Jordan, একই কাস্টমার প্রোফাইল আপডেট করছে—স্ট্যাটাস বদলাতে এবং আলাদা কলের নোট যোগ করতে।\n\nটাইমলাইন (অপটিমিস্টিক লকিং সক্রিয় থাকলে, version বা updated_at চেক ব্যবহার করে):\n\n- 10:02 - Maya Customer #4821 খুলল। ফর্ম লোড হলো Status = "Needs follow-up", Notes = "Called yesterday" এবং Version = 7।\n- 10:03 - Jordan একই রেকর্ড খুলল, ওরও Version = 7 দেখা যায়।\n- 10:05 - Maya Status = "Resolved" রাখল এবং নোট যোগ করল: "Issue fixed, confirmed by customer." সে সেভ করল।\n- 10:05 - সার্ভার রেকর্ড আপডেট করে, Version 8 করে এবং একটি অডিট এন্ট্রি রাখে: কে কী বদলিয়েছে কখন।\n- 10:09 - Jordan আলাদা একটি নোট টাইপ করে: "Customer asked for a receipt" এবং সেভ করে।\n\nকনকারেন্সি চেক না থাকলে Jordan‑এর সেভ Maya‑র স্ট্যাটাস ও নোটকে চুপচাপ ওভাররাইট করে ফেলতে পারে। অপটিমিস্টিক লকিং থাকলে সার্ভার Jordan‑এর আপডেট প্রত্যাখ্যান করে কারণ সে Version = 7 পাঠাচ্ছে যখন রেকর্ড ইতিমধ্যে Version = 8 এ আছে।\n\nJordan একটি পরিষ্কার কনফ্লিক্ট বার্তা দেখে। UI কী হয়েছে দেখায় এবং তাকে নিরাপদ পরবর্তী ধাপ দেয়:\n\n- সর্বশেষ রেকর্ড রিলোড করুন (আমার এডিট বাতিল)\n- আমার পরিবর্তনগুলো সর্বশেষ রেকর্ডের ওপর প্রয়োগ করুন (সম্ভব হলে সুপারিশ)\n- পার্থক্য দেখুন ("Mine" বনাম "Latest") এবং কি রাখবেন তা নির্বাচন করুন\n\nসহজ একটি স্ক্রিনে দেখানো যেতে পারে:\n\n- “এই কাস্টমারকে Maya 10:05‑এ আপডেট করেছেন”\n- যেসব ফিল্ড বদলেছে (Status এবং Notes)\n- Jordan‑এর আনসেভড নোটের প্রিভিউ, যাতে সে কপি করে আবার যোগ করতে পারে\n\nJordan “Review differences” বেছে নেয়, Maya‑র Status = "Resolved" রাখে এবং তার নোটটি বিদ্যমান নোটের শেষে যোগ করে। সে আবার সেভ করে, এবার Version = 8 ব্যবহার করে, এবং আপডেট সফল হয় (এখন Version = 9)।\n\nশেষ অবস্থা: কোনো ডেটা লস নেই, কে কাকে ওভাররাইট করেছে তা পরিষ্কার, এবং একটি ক্লিন অডিট ট্রেইল আছে যা Maya‑র স্ট্যাটাস পরিবর্তন এবং দুইটি নোট আলাদা‑আলাদা ট্র্যাক করে। AppMaster‑এ এর মানে হচ্ছে এক চেক আপডেটে আর একটি ছোট কনফ্লিক্ট‑রেজোলিউশন ডায়ালগ UI‑তে।\n\n## সাধারণ ভুল যা এখনও ডেটা লস ঘটায়\n\nঅধিকাংশ অপটিমিস্টিক লকিং বাগ আইডিয়ায় নয়—তারা UI, API এবং ডেটাবেসের মধ্যে হ্যান্ডঅফে ঘটে। যদি কোনো একটি লেয়ার নিয়মগুলো ভুলে যায়, তখনও নীরব ওভাররাইট ঘটবে।\n\nএকটি ক্লাসিক ভুল হচ্ছে: এডিট স্ক্রীন লোড করার সময় ভার্সন সংগ্রহ করা কিন্তু সেভ‑এ তা পাঠানো না হওয়া। এটা ঘটে যখন একটি ফর্ম বিভিন্ন পেজে রিইউজ হয় এবং হিডেন ফিল্ডটি বাদ পড়ে যায়, অথবা API ক্লায়েন্ট শুধুমাত্র “পরিবর্তিত” ফিল্ডগুলো পাঠায়।\n\nআরেকটি এক্সিকিউশন ফাঁদ হচ্ছে কেবল ব্রাউজারে কনফ্লিক্ট চেক করা। ব্যবহারকারী হয়তো সতর্কতা দেখেন, কিন্তু যদি সার্ভার আপডেট গ্রহণ করে নেয়, তাহলে অন্য কোনো ক্লায়েন্ট বা রিট্রাই ডেটা ওভাররাইট করে দিতে পারে। সার্ভারই চূড়ান্ত গেটকীপার হতে হবে।\n\nযেসব প্যাটার্ন সবথেকে বেশি ডেটা লস করে:\n\n- সেভ রিকোয়েস্টে concurrency টোকেন নেই (version, updated_at, বা ETag), ফলে সার্ভারের কাছে তুলনা করার কিছুই থাকে না।\n- id দিয়ে কেবল আপডেট করা (অর্থাৎ id + version না করা)।\n- কম প্রিসিশনের updated_at (যেমন সেকেন্ড‑লেভেলের) ব্যবহার। একই সেকেন্ডে দুটি এডিট একে অপরকে মিস করতে পারে।\n- বড় ফিল্ড (নোট, ডেসক্রিপশন) বা পুরো অ্যারে (ট্যাগ, লাইন আইটেম) প্রতিস্থাপন করা, ফলে কি বদলেছে দেখানো না হলে অন্য কারো কাজও মুছে যেতে পারে।\n- যেকোন কনফ্লিক্টকে “শুধু রিট্রাই” হিসেবে দেখা, যা পুরানো মানগুলোকে নতুন ডেটার ওপর পুনরায় প্রয়োগ করে দিতে পারে।\n\nকনফ্লিক্ট হলে দলগুলো এখনও ডেটা হারায় যদি API‑র রেসপন্স পাতলা হয়। কেবল “409 Conflict” না দিয়ে পর্যাপ্ত তথ্য দিন যাতে মানুষের মাধ্যমে ঠিক করা যায়:\n\n- বর্তমান সার্ভার ভার্সন (বা updated_at)\n- সংশ্লিষ্ট ফিল্ডগুলোর সর্বশেষ সার্ভার মান\n- কোন ফিল্ডগুলো ভিন্ন তা একটি পরিষ্কার তালিকা\n- কে কখন পরিবর্তন করেছে (যদি ট্র্যাক থাকে)\n\nAppMaster‑এ এটি প্রয়োগ করলে একই শৃঙ্খলা রাখুন: UI স্টেটে version রাখুন, সেভ‑এ এটি পাঠান, এবং PostgreSQL‑এ লেখার আগে ব্যাকএন্ড লজিকে চেক চাপিয়ে দিন।\n\n## চালানোর আগে দ্রুত চেকলিস্ট\n\nরোলআউটের আগে সেগুলো পরীক্ষা করে নিন যা “সেভ ঠিকমতো হয়েছে” বলে চুপচাপ অন্য কারো কাজ ওভাররাইট করে দিতে পারে।\n\n### ডেটা ও API চেক\n\nরেকর্ডে একটি concurrency টোকেন শেষ পর্যন্ত বহন করছে কিনা নিশ্চিত করুন। এটা version integer বা updated_at timestamp হতে পারে, কিন্তু এটি রেকর্ডের অংশ হিসেবেই বিবেচিত হবে—অপশনাল মেটাডেটা নয়।\n\n- রিডে টোকেন অন্তর্ভুক্ত আছে (আর UI‑তে এটা ফর্ম স্টেটে রাখা হচ্ছে, শুধু স্ক্রিনে নয়)।\n- প্রতিটি আপডেটে last‑seen টোকেন পাঠানো হয়, এবং সার্ভার তা লিখার আগে যাচাই করে।\n- সফল হলে সার্ভার নতুন টোকেন রিটার্ন করে যাতে UI সিঙ্ক থাকে।\n- বড়‑পরিমাণ এডিট এবং ইনলাইন এডিটও একই নিয়ম মানে—কোন শর্টকাট না।\n- একই সারির ওপর ব্যাকগ্রাউন্ড জবগুলোও চেক করে (নাহলে তারা দেখাবে যেগুলো র্যান্ডম কনফ্লিক্ট)।\n\nAppMaster‑এ নিশ্চিত করুন Data Designer‑এ ফিল্ড আছে (version বা updated_at) এবং আপনার Business Process‑এ আপডেট চালানোর আগে তা তুলনা করা হয়।\n\n### UI চেক\n\nকনফ্লিক্ট শুধু তখনই “সেফ” যদি পরবর্তী ধাপ স্পষ্ট হয়।\n\nযখন সার্ভার আপডেট প্রত্যাখ্যান করে, একটি পরিষ্কার বার্তা দেখান: “এই রেকর্ডটি আপনি খুলার পর থেকে পরিবর্তিত হয়েছে।” তারপর একটি নিরাপদ একশন প্রথমে দিন: সর্বশেষ ডেটা রিলোড করুন। যদি সম্ভব হয়, একটি “রিলোড এবং পুনরায় প্রয়োগ” পাথ যোগ করুন যা ব্যবহারকারীর আনসেভড ইনপুট রাখে এবং রিফ্রেশ হওয়া রেকর্ডে পুনরায় প্রয়োগ করে, যাতে ছোট ফিক্স করার জন্য আবার টাইপ করতে না হয়।\n\nআপনার দল যদি সত্যিই প্রয়োজন মনে করে, একটি নিয়ন্ত্রিত “force save” অপশন দিন—রোল দ্বারা গেট করা, নিশ্চিতকরণ চাইবে, এবং যারা ফোর্স করেছে ও কী বদলিয়েছে তা লগ করবে। জরুরি ক্ষেত্রে এটা দরকারী হবে, কিন্তু ডিফল্টভাবে ডেটা লসকে অনুমোদিত করা ঠিক নয়।\n\n## পরবর্তী ধাপ: একটি ওয়ার্কফ্লোতে লকিং যোগ করুন এবং প্যাটার্ন ছড়িয়ে দিন\n\nছোট থেকেই শুরু করুন। এমন একটি অ্যাডমিন স্ক্রীন বেছে নিন যেখানে মানুষ ঘন ঘন একে অপরের সঙ্গে ধাক্কা খায়, এবং ওখানে প্রথমে অপটিমিস্টিক লকিং যোগ করুন। সাধারণত হাই‑কলিজন জায়গাগুলো হচ্ছে টিকিট, অর্ডার, প্রাইসিং এবং ইনভেন্টরি। একটি ব্যস্ত স্ক্রীনে কনফ্লিক্টগুলো নিরাপদ করলে আপনি দ্রুত বাকিগুলোতে একই প্যাটার্ন পুনরাবৃত্তি করতে পারবেন।\n\nআপনার ডিফল্ট কনফ্লিক্ট আচরণ আগে থেকেই বেছে নিন—কারণ এটি ব্যাকএন্ড লজিক এবং UI‑উভয়ের আচরণ নির্ধারণ করে:\n\n- Block‑and‑reload: সেভ বন্ধ করুন, সর্বশেষ রেকর্ড রিলোড করুন, এবং ব্যবহারকারীকে পুনরায় প্রয়োগ করতে বলুন।\n- Review‑and‑merge: “আপনার পরিবর্তন” বনাম “সর্বশেষ পরিবর্তন” দেখান এবং ব্যবহারকারীকে কি রাখা হবে তা বেছে নিতে দিন।\n\nBlock‑and‑reload দ্রুত বানানো যায় এবং ছোট এডিটের জন্য ভাল কাজ করে। Review‑and‑merge দীর্ঘ বা হাই‑স্টেক রেকর্ডের জন্য মূল্যবান।\n\nএকটি সম্পূর্ণ ফ্লো পরীক্ষা করে তারপর এক্সপ্যান্ড করুন:\n\n- একটি স্ক্রীন বেছে নিন এবং লোকেরা সবচেয়ে বেশি কোন ফিল্ডগুলো এডিট করে তা তালিকা করুন।\n- ফর্ম পে-লোডে version (বা updated_at) যোগ করুন এবং সেভ‑এ পাঠান।\n- ডেটাবেস‑রাইটে কন্ডিশনাল আপডেট প্রয়োগ করুন (শুধু তখনই আপডেট হবে যখন version মেলে)।\n- কনফ্লিক্ট মেসেজ এবং পরবর্তী একশন ডিজাইন করুন (রিলোড, কপি টেক্সট, তুলনা ভিউ)\n- দুইটি ব্রাউজারে পরীক্ষা করুন: ট্যাব A‑তে সেভ করুন, তারপর ট্যাব B‑তে স্টেল্ড ডেটা সেভ করে দেখুন।\n\nকনফ্লিক্টগুলোর জন্য লাইটওয়েট লগ রাখুন। একটি সাধারণ “কনফ্লিক্ট ঘটেছে” ইভেন্ট যেখানে রেকর্ড টাইপ, স্ক্রিন নাম এবং ইউজার রোল থাকে—এটি হটস্পট শনাক্ত করতে সাহায্য করে।\n\nAppMaster‑এ (appmaster.io) মেইন পিসগুলো ভালভাবে মানায়: Data Designer‑এ ভার্সন ফিল্ড মনুষ্যকৃত করুন, Business Processes‑এ কন্ডিশনাল আপডেট প্রয়োগ করুন, এবং UI বিল্ডারে একটি ছোট কনফ্লিক্ট ডায়ালগ যোগ করুন। প্রথম ওয়ার্কফ্লো স্থিতিশীল হলে একই প্যাটার্ন পরপর স্ক্রিনে প্রয়োগ করুন এবং কনফ্লিক্ট UI কনসিস্টেন্ট রাখুন যাতে ব্যবহারকারী একবার শিখলেই সারাবছর সেটাকে বিশ্বাস করতে পারে।
প্রশ্নোত্তর
একই রেকর্ডটি ভিন্ন‑ভিন্ন ট্যাব বা সেশনে একাধিক ব্যক্তির দ্বারা এডিট করলে এবং শেষে যে সেভ করে তার পরিবর্তনগুলো আগের কারো পরিবর্তনকে কোনো সতর্কতা ছাড়াই রিপ্লেস করে ফেললে সেটাই একটি নীরব ওভাররাইট। ঝুঁকিটা এটাই যে দুজনেই “সেভ সফল” দেখেন, ফলে মিসিং এডিট পরে বুঝে ওঠা যায়।
অপটিমিস্টিক লকিং মানে অ্যাপ শুধুমাত্র তখনই আপনার পরিবর্তনগুলো সেভ করবে যখন রেকর্ডটা আপনার এডিট শুরু করার পর থেকে অপরিবর্তিত আছে। কেউ আগে সেভ করে থাকলে আপনার সেভটি কনফ্লিক্ট রিটার্ন করে, যাতে আপনি নতুন ডেটা দেখে ওভাররাইট না করেন।
পেসিমিস্টিক লকিং কাউকে সম্পাদনা করা থেকে ব্লক করে রাখে, যে কারণে অপেক্ষা, টাইমআউট এবং ‘কে এটা লক করে রেখেছে?’ মত সমস্যা হয়ে ওঠে। অ্যাডমিন টুলে অনেকক্ষেত্রেই অপটিমিস্টিক লকিংকে ডিফল্ট হিসেবে বেছে নেয়া ভাল কারণ এটি মানুষদের পাশাপাশি কাজ করতে দেয় এবং কেবলমাত্র সংঘর্ষ ঘটলে হস্তক্ষেপ করে।
সাধারণত একটি version কলাম সবচেয়ে সরল এবং প্রত্যাশিত ফল দেয় কারণ এটি টাইমস্ট্যাম্প‑সংক্রান্ত পরিসংখ্যান ও ক্লক‑ড্রিফটের ঝামেলা কমায়। updated_at কাজ করবে, কিন্তু যদি টাইমস্ট্যাম্পের প্রিসিশন কম হয় বা বিভিন্ন সিস্টেমে টাইম একরকম না হয় তাহলে দ্রুত রবার্গে সমস্যা হতে পারে।
রেকর্ডে সার্ভার‑নিয়ন্ত্রিত একটি concurrency টোকেন থাকতে হবে—সাধারণত version (integer) অথবা updated_at (timestamp)। ক্লায়েন্টটি ফর্ম খোলার সময় এটাকে পড়ে রাখবে এবং সেভ করার সময় সেই ‘last‑seen’ টোকেনটি ফিরিয়ে দিবে।
ক্লায়েন্ট‑সাইড চেক যথেষ্ট নয় কারণ ক্লায়েন্টকে পুরোপুরি বিশ্বাস করা যায় না। সার্ভারকে একটি কনডিশনাল আপডেট (যেমন update where id = X and version = Y) প্রয়োগ করে শেষ কথাটা বলতে হবে—নাহলে অন্য ক্লায়েন্ট বা ব্যাকগ্রাউন্ড জব ডেটা ওভাররাইট করে দিতে পারে।
ভাল ডিফল্ট হল একটি ব্লকিং মেসেজ: “রেকর্ডটি এডিট করার সময় পরিবর্তিত হয়েছে।” পরে সবচেয়ে নিরাপদ ধাপ হিসেবে রিলোড দেখান। যদি ইউজার দীর্ঘ কিছু টাইপ করে থাকে, তাদের অনসেভড ইনপুট রাখুন যাতে রিলোডের পর সহজে পুনরায় প্রয়োগ করা যায়।
কনফ্লিক্টে API সাধারণত 409 রিটার্ন করে এবং কনটেক্সট দেয়—বর্তমান সার্ভার ভার্সন, সর্বশেষ সার্ভার মানগুলো, এবং যদি সম্ভব হয় কে কখন পরিবর্তন করেছে তার তথ্য—যাতে UI পুনরুদ্ধার সহজ হয়।
সেভ করার সময় টোকেন না পাঠানো, আপডেটে কেবল id ফিল্টার করা (মানে id + version না করা), এবং কম প্রিসিশনের টাইমস্ট্যাম্প ব্যবহার করা—এসবই সাধারণ ভুল যা ডাটা লস ঘটায়। বড় ফিল্ড বা পুরো অ্যারে রিপ্লেস করলে অন্য কারো এডিটও মুছে যেতে পারে।
AppMaster‑এ Data Designer-এ একটি version ফিল্ড যোগ করুন এবং রেকর্ডটি ফর্ম‑স্টেটে লোড করুন। Business Process-এ কনডিশনাল আপডেট প্রয়োগ করুন যাতে আপডেট কেবল তখনই সফল হয় যখন প্রত্যাশিত version মেলে, আর কনফ্লিক্ট‑ব্রাঞ্চ UI‑তে হ্যান্ডল করুন।


