7 মিনিট

এজেন্টের পার্শ্বপ্রতিক্রিয়ার স্বয়ংক্রিয় রোলব্যাক

এজেন্টের পার্শ্বপ্রতিক্রিয়ার স্বয়ংক্রিয় রোলব্যাক সব বাহ্যিক কাজ উল্টাতে পারে না। স্ন্যাপশটের সীমা এবং কোথায় অনুমোদন বা ক্ষতিপূরণ দরকার, জানুন।

এজেন্টের পার্শ্বপ্রতিক্রিয়ার স্বয়ংক্রিয় রোলব্যাক

এজেন্ট রোলব্যাক কোড, কনফিগারেশন বা বাছাই করা অ্যাপ্লিকেশন ডেটা ফিরিয়ে আনতে পারে। এটি অন্য সংস্থাকে কোনো অনুরোধ ভুলে যেতে বাধ্য করতে পারে না, ইনবক্সে পৌঁছে যাওয়া ইমেল টেনে ফিরিয়ে আনতে পারে না, বা পেমেন্ট প্রসেসরে পৌঁছে যাওয়া কার্ড চার্জকে কখনো ঘটেনি বলে দেখাতে পারে না। দলগুলো যখন এসব কাজকেই «রোলব্যাক» বলে, তখন তারা এমন এক স্বস্তিদায়ক নিয়ন্ত্রণ তৈরি করে যা পরিণতি গুরুত্বপূর্ণ হওয়ার মুহূর্তেই ব্যর্থ হয়।

নিরাপদ নকশায় চারটি ব্যবস্থা আলাদা থাকে: অ্যাপ্লিকেশন স্ন্যাপশট, Git revert, ডেটাবেস পুনরুদ্ধার এবং ক্ষতিপূরণমূলক কাজ। প্রতিটির সীমানা আলাদা। অ্যাপ্লিকেশনের সীমানা পেরোনো যেকোনো কাজ এজেন্ট চালানোর আগে তার নিজস্ব অনুমোদন, প্রমাণ, পুনরায় চেষ্টার নিয়ম এবং পুনরুদ্ধার পথ চাই। কেউ যদি এক বাক্যে সেই পথ বলতে না পারে, কাজটি মানুষের তত্ত্বাবধান ছাড়া চালানোর জন্য প্রস্তুত নয়।

রোলব্যাকের চারটি আলাদা অর্থ আছে

দলটি কোন অবস্থা ফিরিয়ে আনবে এবং কোন অবস্থাকে স্পর্শ করতে পারবে না, তা স্পষ্ট করলে তবেই রোলব্যাক কাজে লাগে। শব্দটি প্রায়ই ভিন্ন নিশ্চয়তাসহ চারটি প্রক্রিয়াকে আড়াল করে।

স্ন্যাপশট অ্যাপ্লিকেশন বা ওয়ার্কস্পেসের ধারণ করা সংস্করণ ফিরিয়ে আনে। পণ্যের ওপর নির্ভর করে এতে তৈরি করা কোড, কনফিগারেশন এবং নির্বাচিত ব্যবস্থাপিত অবস্থা থাকতে পারে। স্ন্যাপশটের চুক্তিতে স্পষ্টভাবে না থাকলে এটি বাইরের সেবার ব্যাপারে কিছুই বলে না।

Git revert একটি নতুন কমিট রেকর্ড করে, যার পরিবর্তন আগের কমিটের পরিবর্তন উল্টে দেয়। এটি ইতিহাস না মুছে সোর্স ইতিহাস ঠিক করে। পুরোনো কোড চলার সময় যেসব সেবাকে ডেকেছিল, তাদের সঙ্গে এটি যোগাযোগ করে না।

ডেটাবেস পুনরুদ্ধার ডেটাবেসে থাকা রেকর্ড বদলায়। ট্রানজ্যাকশন রোলব্যাক একটি ট্রানজ্যাকশনের কমিট না হওয়া লেখা বাতিল করে। ব্যাকআপ পুনরুদ্ধার বা point-in-time recovery অনেক বড় কাজ, যা ডেটাবেস ক্লাস্টারকে আগের অবস্থার দিকে নিয়ে যায়। কোনোটিই সেই ডেটাবেসের বাইরের সিস্টেমগুলোকে স্বয়ংক্রিয়ভাবে মিলিয়ে দেয় না।

ক্ষতিপূরণমূলক কাজ পুরোনো প্রভাব পুষিয়ে দিতে নতুন প্রভাব তৈরি করে। ক্যাপচার হওয়া পেমেন্টের ক্ষতিপূরণ রিফান্ড। অর্ডারের ক্ষতিপূরণ বাতিলের অনুরোধ। ভুল ইমেলের ক্ষতি সংশোধনী ইমেল কিছুটা কমাতে পারে, কিন্তু প্রথম বার্তাটি সরাতে পারে না। ক্ষতিপূরণ অস্বস্তিকর সত্যটি বজায় রাখে: মূল কাজটি ঘটেছিল।

নকশা পর্যালোচনায় আমি একটি প্রভাব-মালিকানা টেবিল ব্যবহার করি, কারণ এটি নির্ভুল উত্তর দিতে বাধ্য করে:

পরিবর্তনপুনরুদ্ধারের মালিকসাধারণ পদ্ধতিমূল প্রভাব মুছতে পারে?
তৈরি করা সোর্সঅ্যাপ্লিকেশন বা রিপোজিটরিস্ন্যাপশট পুনরুদ্ধার বা Git revertসাধারণত, ভবিষ্যৎ চালানোর ক্ষেত্রে
কমিট হওয়া সারিডেটাবেস অপারেটরযৌক্তিক সংশোধন বা পুনরুদ্ধারকখনও কখনও স্থানীয়ভাবে
পৌঁছে যাওয়া ইমেলমেইল প্রোভাইডার ও প্রাপকফলো-আপ বা অপেক্ষমাণ মেইল বন্ধ করানা
ক্যাপচার হওয়া পেমেন্টপেমেন্ট প্রসেসরvoid বা রিফান্ডনা
বাহ্যিক API অনুরোধগ্রহণকারী সেবাপ্রোভাইডারভেদে বাতিল বা ক্ষতিপূরণসাধারণত না

শেষ কলামটিই সবচেয়ে জরুরি। বিপরীত অপারেশন থাকলেই মূল কাজটি অদৃশ্য হয়েছে, তার প্রমাণ হয় না। অডিট রেকর্ড, প্রাপকের কপি, সেটেলমেন্ট এন্ট্রি, webhook এবং বাস্তব কাজ রয়ে যেতে পারে।

কোড revert প্রোগ্রাম বদলায়, অতীত নয়

কোড revert ভবিষ্যতের আচরণ ঠেকায় বা বদলায়, পুরোনো সংস্করণ ইতিমধ্যে যে আচরণ ঘটিয়েছে তা উল্টায় না। দল Git, প্ল্যাটফর্ম স্ন্যাপশট বা ডিপ্লয়মেন্ট রোলব্যাক যেটিই ব্যবহার করুক, বিষয়টি একই।

Git-এর নথিতে git revert-কে এমন কমিট রেকর্ড করা বলা হয়েছে, যা আগের কমিটে আনা পরিবর্তন উল্টে দেয়। কথাটি একদম নির্ভুল: Git রিপোজিটরির কনটেন্টে একটি বিপরীত patch প্রয়োগ করে। revert হওয়া কমিট চলার সময় তৈরি হওয়া ইমেল, পেমেন্ট, ক্লাউড রিসোর্স, সাপোর্ট টিকিট বা পার্টনার API সম্পর্কে Git কিছু জানে না।

ধরা যাক, একটি এজেন্ট বিলিং ফাংশন বদলাল, ডিপ্লয় করল এবং মনিটরিং ত্রুটি ধরার আগে সেটি দুবার চালাল। কমিট revert করলে খারাপ ফাংশনটি আবার চলা থামতে পারে। কিন্তু প্রসেসরে পেমেন্টের দুটি চেষ্টা থেকেই যায়। স্থানীয় ডেটাবেসে যদি একটিই চেষ্টা লেখা থাকে, revert তদন্ত আরও কঠিনও করতে পারে, কারণ দ্বিতীয় প্রতিক্রিয়াটি বোঝার কোড পথটি হারিয়ে যেতে পারে।

ডিপ্লয়মেন্ট রোলব্যাকের সীমানাও একই। ট্র্যাফিক আগের build-এ ফিরিয়ে নিলে চালানো যায় এমন আচরণ ফেরে। বদলে দেওয়া build ইতিমধ্যে যে অনুরোধ গ্রহণ করেছে, তার পরিণতি থেকেই যায়। সারিবদ্ধ job-ও ডিপ্লয়মেন্ট টিকে থাকতে পারে এবং পুনরুদ্ধার করা সংস্করণের ওপর পুরোনো ধারণা নিয়ে চলতে পারে।

revert করার আগে পুরোনো build যে কার্যগত প্রমাণ তৈরি করেছে তা সংরক্ষণ করুন:

  • ডিপ্লয়মেন্ট শনাক্তকারী ও সোর্স কমিট
  • এজেন্ট রান ও intent শনাক্তকারী
  • queue message শনাক্তকারী ও lease অবস্থা
  • বাহ্যিক অনুরোধ শনাক্তকারী
  • প্রোভাইডারের প্রতিক্রিয়া ও সময়চিহ্ন

তারপর নতুন চালনা থামান, অসম্পূর্ণ কাজ মিলিয়ে নিন এবং কোড পুনরুদ্ধার করুন। আগে revert করে পরে প্রশ্ন করলে কোন প্রভাব সীমানা পেরিয়েছে তা বোঝার সহজতম পথটি প্রায়ই নষ্ট হয়।

স্ন্যাপশট Git কমিটের চেয়ে বিস্তৃত হতে পারে, তবে একই নিয়ম প্রযোজ্য। স্ন্যাপশট চুক্তিতে কোন রিসোর্স আছে তা হুবহু বলা চাই। কোড ও কনফিগারেশন থাকলে একে কোড ও কনফিগারেশন পুনরুদ্ধার ব্যবস্থা বলুন। ইন্টারফেসের আশাবাদী ভাষা দিয়ে একে সার্বজনীন আনডু বানাবেন না।

ডেটাবেস পুনরুদ্ধারের কাজ মানুষের ধারণার চেয়ে সংকীর্ণ

ডেটাবেস পুনরুদ্ধার ডেটাবেসের অবস্থা ফিরিয়ে আনে, লেনদেনের সব অংশগ্রহণকারীর ব্যবসায়িক বাস্তবতা নয়। একটি ডেটাবেসের ভেতরে ট্রানজ্যাকশন atomic হতে পারে, অথচ আশপাশের কাজ কয়েকটি সিস্টেমে ভাগ হয়ে থাকতে পারে।

এই ক্রমটি বিবেচনা করুন:

  1. এজেন্ট একটি invoice সারি যোগ করে।
  2. এটি একটি payment API-তে কল করে।
  3. প্রসেসর চার্জ গ্রহণ করে।
  4. ডেটাবেস কমিট ব্যর্থ হয়।

স্থানীয় রোলব্যাক invoice সারিটি মুছে দেয়। চার্জটি তবু থাকে। মিলিয়ে না দেখে পুরো কাজ আবার চালালে গ্রাহকের কাছ থেকে আবার চার্জ হতে পারে। এটি classic dual write failure: অ্যাপ্লিকেশন এমন সিস্টেমে একটি ব্যবসায়িক কাজকে atomic করতে চেয়েছে, যেগুলো একই transaction coordinator ভাগ করে না।

ক্রম উল্টালেও সমাধান হয় না। অ্যাপ্লিকেশন আগে invoice কমিট করে এবং পরে payment call ব্যর্থ হলে ডেটাবেসে অপরিশোধিত invoice থাকে। এই অবস্থা পরীক্ষা করা সহজ, কিন্তু তবু অ্যাপের এমন state machine দরকার যা payment_pending, payment_confirmed, payment_failed এবং payment_unknown আলাদা করে।

PostgreSQL-এর নথিতে point-in-time recovery বলতে base backup পুনরুদ্ধার করে নির্বাচিত recovery target পর্যন্ত write ahead log রেকর্ড আবার চালানোর কথা বলা হয়েছে। এটি ডেটাবেস ক্লাস্টার পুনরুদ্ধারের জন্য অপারেটরের প্রক্রিয়া। একটি এজেন্ট রানের বাছাই করা আনডু নয়, এবং এটি পেমেন্ট প্রসেসর বা মেইল সেবাকে একই সময়চিহ্নে ফিরতে বলতে পারে না।

ডেটাবেসকে পেছনে নিলে আরেকটি অমিল তৈরি হতে পারে। ধরা যাক, বিভ্রাটের পর 10:00-এ পুনরুদ্ধার করা হলো। একটি প্রোভাইডার 10:07 পর্যন্ত অনুরোধ গ্রহণ করেছে, কিন্তু পুনরুদ্ধার করা ডেটাবেসে সেগুলোর রেকর্ড আর নেই। «নিখোঁজ» সারি দেখে এজেন্টরা সাত মিনিটের সব কাজ আবার তৈরি করতে পারে। তাই worker পুনরায় চালুর আগে বাহ্যিক মিলিয়ে দেখার ধাপ দরকার।

outbox pattern একটি বিপজ্জনক ফাঁক কমায়। অ্যাপ্লিকেশন একই স্থানীয় ট্রানজ্যাকশনে ব্যবসায়িক পরিবর্তন এবং প্রভাবের intent কমিট করে:

BEGIN;

INSERT INTO invoices (invoice_id, customer_id, status)
VALUES ('inv_2048', 'cust_91', 'payment_pending');

INSERT INTO effect_intents
  (intent_id, operation, subject_id, status)
VALUES
  ('eff_7f31', 'capture_payment', 'inv_2048', 'pending');

COMMIT;

একটি worker পরে eff_7f31 দাবি করে, স্থায়ী idempotency মান দিয়ে প্রোভাইডারকে কল করে এবং ফল লেখে। outbox বাহ্যিক কলকে atomic করে না। এটি কাজটি করা উদ্দেশ্য ছিল তার স্থায়ী প্রমাণ দেয়, ফলে পুনরায় চেষ্টা ও মিলিয়ে দেখা সম্ভব হয়।

বাহ্যিক কাজের ক্ষতিপূরণ দরকার, আর কিছু কাজের নেই

বাহ্যিক পার্শ্বপ্রতিক্রিয়ার জন্য প্রোভাইডার-নির্দিষ্ট ক্ষতিপূরণ বা অর্থবহ ক্ষতিপূরণ নেই, এমন স্পষ্ট ঘোষণা দরকার। সব কাজ ফিরিয়ে আনা যায় ধরে নেওয়া, কিছু কাজের অনুমোদন দরকার বলে স্বীকার করার চেয়েও খারাপ।

ইমেল সবচেয়ে সহজ উদাহরণ। প্রোভাইডার বার্তাটি গ্রহণ করার আগে অ্যাপ্লিকেশন সারিবদ্ধ job বাতিল করতে পারে। গ্রহণের পর প্রোভাইডার অল্প সময়ের অভ্যন্তরীণ ধাপে বাতিলের সুযোগ দিতে পারে, কিন্তু প্রাপক ও মেইল সিস্টেমজুড়ে সাধারণ recall নিশ্চয়তা নেই। পৌঁছে গেলে দ্বিতীয় বার্তা রেকর্ড সংশোধন করতে পারে, প্রথমটি মুছতে পারে না। সংবেদনশীল তথ্য, আইনি নোটিশ ও সুনামগত ক্ষতি তখনও প্রকাশিত থাকে।

পেমেন্টের কয়েকটি অবস্থা দলগুলো প্রায়ই «চার্জ হয়েছে» বলে এক করে ফেলে। authorization খরচের সক্ষমতা সংরক্ষণ করে। capture সেই authorization-এর বিপরীতে অর্থ সরানোর অনুরোধ করে। settlement-এর আগে void authorization ছেড়ে দিতে পারে। refund, capture-এর পর টাকা ফেরত দিতে পরের আর্থিক এন্ট্রি তৈরি করে। এসব কাজের সময়, ফি, অনুমতি ও গ্রাহকের ওপর প্রভাব আলাদা। সাধারণ undo_payment টুল এজেন্টের নিরাপদে কাজ করার প্রয়োজনীয় তথ্য লুকিয়ে রাখে।

অন্য API আরও কম সহযোগিতাপূর্ণ। একটি অনুরোধ মজুত পণ্য অর্ডার, অবকাঠামো provision, কনটেন্ট প্রকাশ, অ্যাক্সেস প্রদান, প্যাকেজ পাঠানো বা মানুষকে কাজ শুরু করতে বলতে পারে। DELETE endpoint ফিরিয়ে আনা যাবে, তা প্রমাণ করে না। রিসোর্স মুছলেও অডিট লগ, কপি করা ডেটা, নোটিফিকেশন, নির্ভরশীল রিসোর্স বা বাস্তব পরিণতি থেকে যেতে পারে।

ক্ষতিপূরণকে সে যা সৎভাবে করতে পারে তার ভিত্তিতে ভাগ করুন:

  • সঠিক স্থানীয় বিপরীত ব্যবস্থা নিয়ন্ত্রিত অবস্থাকে আগের মানে ফেরায়।
  • প্রোভাইডার বাতিল সম্পন্ন না হওয়া কাজ থামায়।
  • আর্থিক ক্ষতিপূরণ রিফান্ড বা ক্রেডিট তৈরি করে।
  • সংশোধনী যোগাযোগ স্বীকার করে যে প্রথম বার্তা দৃশ্যমানই আছে।
  • ম্যানুয়াল সমাধান সেই প্রভাব সামলায় যার প্রেক্ষাপট নিরাপদ স্বয়ংক্রিয় নিয়মে ধরা যায় না।

ক্ষতিপূরণও ব্যর্থ হতে পারে। refund endpoint টাইমআউট করতে পারে। বাতিলের সময়সীমা শেষ হতে পারে। প্রাপকের ঠিকানা সংশোধনী প্রত্যাখ্যান করতে পারে। এজেন্টের ব্যবহৃত অ্যাকাউন্টে অনুমতি নাও থাকতে পারে। তাই সিস্টেমকে ক্ষতিপূরণকেও নিজস্ব intent শনাক্তকারী, অবস্থা, চেষ্টা, প্রমাণ ও অনুমোদন নীতিসহ আরেকটি অপারেশন হিসেবে ট্র্যাক করতে হবে।

পুনরাবৃত্ত «রোলব্যাকের রোলব্যাক» ফিচার বানাবেন না। ইতিহাসকে কাজের ledger হিসেবে মডেল করুন। ক্ষতিপূরণে নতুন ত্রুটি হলে বর্তমান অবস্থা পর্যালোচনার পর আরেকটি স্পষ্ট কাজ করুন। ইতিহাস দীর্ঘ হয়, তবে ঘটনার সময় তা বোঝা যায়।

Idempotency পুনরাবৃত্তি থামায়, সফলতা উল্টায় না

রোলব্যাকের সীমানা স্পষ্ট রাখুন
চ্যাটে অ্যাপ তৈরি করুন, তারপর রোলব্যাককে Koder.ai পরিচালিত সফটওয়্যারেই সীমিত রাখুন।

idempotency পুনরায় চেষ্টায় একই উদ্দেশ্যপ্রণোদিত প্রভাব একাধিকবার তৈরি হওয়া আটকায়। প্রথম সফল প্রভাবটিকে এটি আনডু করে না। দলগুলো নিয়মিত এই দুই ধারণা গুলিয়ে ফেলে, তারপর টাইমআউটের পরে পার্থক্য বোঝে।

RFC 9110 একই ধরনের একাধিক অনুরোধের উদ্দেশ্যপ্রণোদিত প্রভাব একটি অনুরোধের মতো হলে request method-কে idempotent বলে। প্রোটোকলের অর্থগত স্তরে PUT, DELETE ও safe method-গুলোকে idempotent বলা হয়েছে। POST সাধারণত idempotent নয়, যদিও কোনো API তার নিজস্ব চুক্তির মাধ্যমে এমন আচরণ যোগ করতে পারে।

এই শর্তটি গুরুত্বপূর্ণ। idempotent DELETE প্রতিবার নতুন log entry, metric বা response তৈরি করতে পারে। প্রোভাইডারের idempotency ব্যবস্থা রেকর্ডের মেয়াদ শেষ করতে পারে, শনাক্তকারীকে এক অ্যাকাউন্টে সীমিত রাখতে পারে, বদলানো parameter প্রত্যাখ্যান করতে পারে বা শুধু বাছাই করা ফল cache করতে পারে। HTTP verb দেখে নিশ্চয়তা অনুমান না করে প্রোভাইডারের চুক্তি পড়ুন।

প্রতিটি effect intent-কে প্রথম চেষ্টার আগে একটি স্থায়ী idempotency মান দিন। একই intent-এর retry-তে সেটিই ব্যবহার করুন। নতুন ব্যবসায়িক intent নতুন মান পাবে। শুধু customer, amount ও date-এর মতো পরিবর্তনযোগ্য parameter থেকে এটি তৈরি করবেন না, কারণ দুটি বৈধ কেনাকাটায়ও এসব মান এক হতে পারে।

টাইমআউট ব্যর্থতা নয়, অজানা ফলাফল। এই ক্রম ব্যবহার করুন:

  1. চেষ্টাটিকে outcome_unknown হিসেবে চিহ্নিত করুন, বদলি intent তৈরি করবেন না।
  2. idempotency মান বা operation reference দিয়ে প্রোভাইডারের কাছে খোঁজ নিন।
  3. প্রোভাইডার সফলতা নিশ্চিত করলে স্থানীয়ভাবে সেই সফলতা লিখুন।
  4. কোনো অপারেশন হয়নি নিশ্চিত করলে একই মান দিয়ে আবার চেষ্টা করুন।
  5. প্রোভাইডার উত্তর দিতে না পারলে মিলিয়ে দেখা বা মানুষের পর্যালোচনার জন্য কাজটি আটকে রাখুন।

এই প্রতিক্রিয়ার কাঠামো এজেন্টকে গ্রহণ হওয়া আর পরিবহন-অনিশ্চয়তা আলাদা করার মতো তথ্য দেয়:

{
  "intent_id": "eff_7f31",
  "attempt": 2,
  "idempotency_key": "eff_7f31",
  "transport_status": "timeout",
  "provider_status": "unknown",
  "provider_reference": null,
  "next_action": "reconcile"
}

সমর্থিত হলে idempotency মান log, queue message, API header এবং provider metadata-তে যেতে হবে। অপারেটররা সীমানার দুই পাশে এটি দিয়ে খুঁজতে না পারলে পুনরুদ্ধারের সময় আন্দাজ করতে হবে।

অনুমোদনের স্থান প্রভাবের সীমানায়

এজেন্ট সঠিক বাহ্যিক কাজ গঠন করার পরে এবং প্রথম অপরিবর্তনীয় অনুরোধ সিস্টেম ছাড়ার আগে অনুমোদন হতে হবে। বিস্তৃত কাজের শুরুতে অনুমোদন দিলে পরে প্রাপক, পরিমাণ, পরিধি বা টুল বদলানোর অতিরিক্ত সুযোগ এজেন্ট পায়।

«এই গ্রাহকের অ্যাকাউন্ট সামলান» কার্ডে চার্জ দেওয়া বা সব ব্যবহারকারীকে ইমেল পাঠানোর যথেষ্ট অনুমোদন নয়। বৈধ অনুমোদনে নির্দিষ্ট কাজটি থাকে: প্রাপক, পরিমাণ ও মুদ্রা, বার্তা বা payload digest, লক্ষ্য অ্যাকাউন্ট, টুল, মেয়াদ এবং অনুমোদিত চেষ্টার সংখ্যা। অনুমোদিত কোনো ক্ষেত্র বদলালে অনুমোদন আর মেলে না।

ভালো নীতি কোন এজেন্ট বা model কাজটি চেয়েছে তার বদলে পরিণতি অনুযায়ী প্রভাব সাজায়। read access ব্যক্তিগত তথ্য প্রকাশ করতে পারে, কিন্তু বহির্গামী কাজের মতো পুনরুদ্ধার সমস্যা তৈরি করে না। ইমেল draft করা স্থানীয় এবং ফিরিয়ে আনা যায়। পাঠানো সীমানা পেরোয়। পেমেন্ট proposal তৈরি স্থানীয়। অর্থ capture সীমানা পেরোয়।

যে কাজগুলোতে স্পষ্ট অনুমোদন দরকার:

  • অর্থ সরায় বা আর্থিক বাধ্যবাধকতা তৈরি করে
  • কোনো ব্যক্তি বা বাইরের সংস্থাকে তথ্য পাঠায়
  • নিয়ন্ত্রিত স্টোরেজের বাইরে তথ্য প্রকাশ, মুছে বা প্রকাশ্যে আনতে পারে
  • পরিচয়, অ্যাক্সেস, মালিকানা বা নিরাপত্তা সেটিং বদলায়
  • এমন বাস্তব কাজ বা প্রক্রিয়া শুরু করে যা নির্ভরযোগ্যভাবে ফিরিয়ে আনা যায় না

কম পরিণতির পুনরাবৃত্তিমূলক কাজে সীমাবদ্ধ স্থায়ী অনুমোদন ব্যবহার করা যায়। সীমায় সর্বোচ্চ পরিমাণ, প্রাপক সেট, অনুমোদিত টুল, মেয়াদ, হার এবং মোট অপারেশনের সংখ্যা থাকতে হবে। «বিলিংয়ের জন্য অনুমোদিত» কথাটির প্রয়োগযোগ্য কোনো সীমানা নেই।

অনুমোদনকে replay থেকেও রক্ষা করতে হবে। কাজের অপরিবর্তনীয় digest-এর সঙ্গে এটি বাঁধুন এবং নীতিতে একবার চালানোর অনুমতি থাকলে ব্যবহৃত হিসেবে চিহ্নিত করুন। চালানোর ফল অজানা হলে নতুন অনুমোদন নিয়ে দ্বিতীয় intent বানাবেন না। আগে অনুমোদিত intent-টিই মিলিয়ে দেখুন।

অনুমোদন পর্দায় সাধারণ ভাষায় পুনরুদ্ধারের সত্য বলা উচিত। «ডেলিভারির পরে এই বার্তা ফিরিয়ে আনা যাবে না» কথাটি উপকারী। «এই অপারেশন ফিরিয়ে আনা যায়» কথাটি বিভ্রান্তিকর, যখন আসল পুনরুদ্ধার একটি রিফান্ড, যা সময় নিতে পারে এবং আর্থিক বিবৃতিতে থেকে যায়।

টুল কনট্র্যাক্টে প্রভাবের পুরো জীবনচক্র দেখান

হোস্টিং ও প্রভাব আলাদা রাখুন
অ্যাপের জন্য কাস্টম ডোমেইন ও হোস্টিং ব্যবহার করুন, আর বাইরের প্রভাবের অনুমোদন টুল কনট্র্যাক্টেই রাখুন।

এজেন্ট টুল কনট্র্যাক্টে intent, execution, observation ও compensation আলাদা অপারেশন হিসেবে বর্ণিত থাকা উচিত। প্রভাব ঘটিয়ে শুধু success: true ফেরত দেয় এমন ফাংশন retry, অনুমোদন বা ঘটনার প্রতিক্রিয়ার জন্য খুব কম প্রমাণ দেয়।

এই নীতির অংশটি প্রয়োগ করার মতো ছোট, আবার পর্যালোচনার মতো নির্দিষ্ট:

tools:
  send_email:
    effect: irreversible
    approval: required
    idempotency_field: intent_id
    evidence_field: provider_message_id
    compensation: null

  capture_payment:
    effect: compensatable
    approval: required
    idempotency_field: intent_id
    evidence_field: provider_payment_id
    compensation: refund_payment

  update_draft:
    effect: local_reversible
    approval: none
    compensation: restore_version

effect planner-কে বলে কোন পুনরুদ্ধার শ্রেণি প্রযোজ্য। approval সঠিক arguments অনুমোদিত না হওয়া পর্যন্ত execution আটকে রাখে। idempotency field retry-কে একই পরিচয় ব্যবহার করায়। evidence field অপারেটরদের বলে কোন তথ্য টিকে থাকা চাই। compensation field আলাদা টুল নির্দেশ করে, মূল কলটি উল্টো চলতে পারে এমন ভান করে না।

execution-এর জন্য অপরিবর্তনীয় envelope নিন:

{
  "intent_id": "eff_7f31",
  "operation": "capture_payment",
  "arguments": {
    "invoice_id": "inv_2048",
    "amount_minor": 12900,
    "currency": "USD"
  },
  "approval": {
    "approval_id": "apr_662",
    "scope_hash": "sha256:8b4f...",
    "expires_at": "2026-07-27T18:00:00Z"
  }
}

executor অপারেশনের digest হিসাব করে, scope_hash-এর সঙ্গে মেলায়, মেয়াদ দেখে, intent reserve করে এবং তারপরই প্রোভাইডারের সঙ্গে যোগাযোগ করে। কলের আগে request metadata এবং পরে response সংরক্ষণ করে। ওই লেখাগুলোর মাঝখানে crash হলে স্থায়ী intent মিলিয়ে দেখার জন্য রয়ে যায়।

executor-কে অনুমান না করে চারটি অবস্থা প্রত্যাখ্যান করতে হবে: বদলে যাওয়া arguments, মেয়াদোত্তীর্ণ অনুমোদন, অন্য অপারেশনের জন্য intent পুনর্ব্যবহার, এবং সফল সমাপ্তি নিশ্চিত না হওয়া প্রভাবের ক্ষতিপূরণের চেষ্টা। এজেন্ট বিশ্বাসযোগ্য পরের ধাপ খুঁজে নিতে পারদর্শী। আর্থিক ও যোগাযোগ নিয়ন্ত্রণে বিশ্বাসযোগ্য অনুমানের চেয়ে দৃশ্যমান থামাকে অগ্রাধিকার দিতে হবে।

observation-এর জন্য আলাদা টুল দিন, যেমন get_payment_status(intent_id)। observation কোনো প্রভাব তৈরি করবে না। এটি আলাদা থাকলে এজেন্ট মূল কাজটি আবার চালানোর অনুমতি না পেয়েও অস্পষ্ট ফল মেটাতে পারে।

একটি ব্যর্থ রান সব পুনরুদ্ধার সীমানা পেরোতে পারে

সোর্স পুনরুদ্ধার হাতের কাছে রাখুন
পুনরুদ্ধার প্রক্রিয়ায় রিপোজিটরি স্তরের পর্যালোচনা ও Git revert দরকার হলে সোর্স কোড এক্সপোর্ট ব্যবহার করুন।

একটি এজেন্ট রান কোড, ডেটাবেস রেকর্ড ও বাইরের সিস্টেমকে সময়ের ভিন্ন বিন্দুতে রেখে যেতে পারে। চালুর আগে এই ব্যর্থতার পথ ধরে চললে সাধারণ রোলব্যাক নিয়ন্ত্রণে ঢাকা ফাঁকগুলো বের হয়।

ধরা যাক, একটি এজেন্ট সদস্যপদ অ্যাপ তৈরি করে, পরিবর্তন ডিপ্লয় করে, গ্রাহক তালিকা import করে, বার্ষিক ফি চার্জ করে এবং স্বাগত ইমেল পাঠায়। কাজটি একক মনে হলেও অন্তত চারটি পুনরুদ্ধার সীমানা পেরোয়।

14:00-এ এজেন্ট ভুল কলাম থেকে বার্ষিক ফি হিসাব করা কোড ডিপ্লয় করে। 14:02-এ এটি ডেটাবেসে 40টি payment intent ও email draft লেখে। 14:03-এ worker কয়েকটি payment capture করে। 14:04-এ মেইল প্রোভাইডার ভুল ফিসহ স্বাগত বার্তা গ্রহণ করে। 14:05-এ মনিটরিং worker থামায়। কিছু payment call প্রসেসরে পৌঁছানোর পরে টাইমআউট হয়েছে, তাই স্থানীয় অবস্থা বলে না সেগুলো সফল হয়েছিল কি না।

13:59-এর কোড স্ন্যাপশট পুনরুদ্ধার ভবিষ্যৎ রানে ভুল হিসাব থামায়। বিদ্যমান intent-এ কপি হওয়া ফি এতে বদলায় না। Git commit revert সোর্স সংশোধন নথিবদ্ধ করে, কিন্তু সীমাটি একই।

ডেটাবেসকে 13:59-এ ফিরিয়ে নিলে provider reference ও idempotency মান থাকা স্থানীয় intent রেকর্ড মুছে যাবে। এতে বাহ্যিক অমিল আরও খারাপ হবে। ভালো পদক্ষেপ যৌক্তিক সংশোধন: intent সংরক্ষণ করুন, অনিশ্চিত কাজকে মিলিয়ে দেখার জন্য চিহ্নিত করুন এবং provider state মেলানোর পরই সারি সংশোধন করুন।

এরপর প্রতিক্রিয়া দল প্রভাবের শ্রেণি অনুযায়ী এগোবে। স্থায়ী operation identity দিয়ে প্রতিটি অনিশ্চিত payment খোঁজা হয়। নিশ্চিত capture রিফান্ড পর্যালোচনায় যায়, ব্যর্থ চেষ্টা retry ছাড়াই বন্ধ হয়, আর অমীমাংসিত চেষ্টা আটকে থাকে। পৌঁছে যাওয়া ইমেল সতর্কতার সঙ্গে অনুমোদিত সংশোধনী পায়। প্রোভাইডারের কাছে না পৌঁছানো সারিবদ্ধ বার্তা বাতিল হয়। প্রতিটি ক্ষতিপূরণ মূলটির সঙ্গে যুক্ত নতুন intent পায়।

উদাহরণটি আরও দেখায় কেন স্বয়ংক্রিয় ক্ষতিপূরণ বিপজ্জনক হতে পারে। সিস্টেম যদি প্রতিটি স্থানীয় payment_unknown রেকর্ডে সঙ্গে সঙ্গে রিফান্ড দেয়, তবে যে চার্জ কখনো হয়নি তার জন্য রিফান্ড দিতে পারে অথবা ভুল reference দিয়ে refund endpoint ডাকতে পারে। ডেটাবেস পুনরুদ্ধারের পর প্রতিটি অনুপস্থিত ইমেল আবার পাঠালে প্রাপকরা ডুপ্লিকেট পাবেন। স্থানীয় ও প্রোভাইডারের অবস্থা না মিললে ক্ষতিপূরণের আগে মিলিয়ে দেখা জরুরি।

প্রতিটি intent succeeded, confirmed_failed, compensated বা manual_exception-এর মতো terminal state-এ পৌঁছালেই রানটি সম্পন্ন। «অ্যাপ্লিকেশন রোল ব্যাক করা হয়েছে» কথাটি ঘটনার প্রথম অংশটুকুই বলে।

প্রমাণ টিকে থাকলেই পুনরুদ্ধার কাজ করে

রোলব্যাক যখন কী ঘটেছিল নির্ধারণের জন্য দরকারি রেকর্ডই মুছে দেয়, তখন পুনরুদ্ধার নিয়ন্ত্রণ ব্যর্থ হয়। অ্যাপ্লিকেশন state থেকে আলাদা append-only effect ledger রাখুন, যেটি নিয়মিত স্ন্যাপশট বা restore বদলে দিতে পারে না। প্রতিটি অপারেশন মেলাতে যথেষ্ট প্রোভাইডার প্রমাণও রাখুন।

ledger-এ intent তৈরি, argument digest, অনুমোদন, execution lease, চেষ্টা, transport ফল, provider reference, পর্যবেক্ষিত provider state এবং compensation link থাকা উচিত। এজেন্টকে পুরোনো entry বদলাতে দেওয়ার বদলে পরিবর্তনকে state transition-এ সীমিত রাখুন। সংশোধনে ইতিহাস সম্পাদনা না করে event যোগ করুন।

শুধু স্পষ্ট ত্রুটি নয়, অমীমাংসিত অবস্থাও নজরে রাখুন। দশ মিনিট ধরে থাকা outcome_unknown পরিষ্কার প্রত্যাখ্যানের চেয়েও বিপজ্জনক হতে পারে, কারণ অপারেটর সেটি হাতে আবার চেষ্টা করতে পারেন। execution চলার সময় অনুমোদনের মেয়াদ শেষ হলে, ভিন্ন argument digest-সহ idempotency মান দেখা গেলে বা compensation ব্যর্থ হলেও সতর্কতা দিন।

ইচ্ছাকৃতভাবে অস্বস্তিকর ব্যর্থতার বিন্দু দিয়ে পুনরুদ্ধার drill চালান। প্রোভাইডার অনুরোধ গ্রহণ করেছে কিন্তু স্থানীয় সফলতার লেখা হওয়ার আগে worker বন্ধ করুন। effect ledger রেখে অ্যাপ ডেটা আগের স্ন্যাপশট থেকে ফিরিয়ে আনুন। পরিকল্পনা ও execution-এর মাঝখানে অনুমোদনের মেয়াদ শেষ করুন। observation API অনুপলব্ধ করুন। drill তখনই সফল যখন সিস্টেম থামে, মিলিয়ে দেখে এবং প্রভাব ডুপ্লিকেট না করে অমীমাংসিত সিদ্ধান্ত দেখায়।

Koder.ai ব্যবহার করলে তার স্ন্যাপশট ও রোলব্যাককে অ্যাপ্লিকেশন পুনরুদ্ধার স্তর হিসেবে ধরুন। এরপর ডেটাবেস ইতিহাস এবং অ্যাপ্লিকেশন যে প্রতিটি বাইরের কাজ ঘটাতে পারে, তার জন্য আলাদা নিয়ন্ত্রণ নকশা করুন। সোর্স এক্সপোর্ট, ডিপ্লয়মেন্ট নিয়ন্ত্রণ ও রোলব্যাক সফটওয়্যার ফিরিয়ে আনতে সাহায্য করে, আর অ্যাপের টুল কনট্র্যাক্ট এখনও অনুমোদন ও ক্ষতিপূরণের দায়ে থাকে।

রোলব্যাক বাটনের পাশেই তার সীমানা লেখা উচিত: কোড, কনফিগারেশন, ব্যবস্থাপিত ডেটা, নাকি বাহ্যিক প্রভাব। ইন্টারফেস যদি বাক্যটি নির্ভুল করতে না পারে, তাহলে রোলব্যাকের প্রতিশ্রুতি দেওয়া উচিত নয়। সৎ নিয়ন্ত্রণটি কম জাদুকরী দেখাতে পারে, কিন্তু রাত 2টায় ঘটনা সামলানো দলকে তার প্রয়োজনীয় জিনিসটি দেয়: কী বদলেছে, কী সীমানা পেরিয়েছে এবং পরের কোন কাজটি নিরাপদ, তার নির্ভরযোগ্য বিবরণ।

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

একটি AI এজেন্টকে রোল ব্যাক করলে কি পাঠানো ইমেল ফিরিয়ে আনা যায়?

না। ইমেল পাঠানোর অনুরোধের আগের কোড বা অ্যাপ্লিকেশন স্ন্যাপশট পুনরুদ্ধার করা যায়, কিন্তু মেইল প্রোভাইডার ইতিমধ্যে গ্রহণ করা বার্তা ফিরিয়ে আনা যায় না। পাঠানোর আগে সিস্টেমের অনুমোদন এবং প্রোভাইডারের প্রতিক্রিয়ার স্থায়ী রেকর্ড দরকার।

স্বয়ংক্রিয় রোলব্যাক কি ক্রেডিট কার্ডের চার্জ উল্টে দিতে পারে?

সাধারণত যায় না। রোলব্যাক প্রসেসরের রেকর্ড থেকে নিষ্পত্তি হওয়া পেমেন্ট মুছতে পারে না। নতুন আর্থিক লেনদেন হিসেবে রিফান্ড দিতে হয়। ক্যাপচার না হওয়া অনুমোদন বাতিল করা যেতে পারে, তবে সেটিও কোড রোলব্যাক নয়, স্পষ্ট পেমেন্ট অপারেশন।

Git revert আসলে কী পূর্বাবস্থায় ফেরায়?

Git revert একটি নতুন কমিট তৈরি করে, যা আগের কোড পরিবর্তনের বিপরীত পরিবর্তন প্রয়োগ করে। এটি ডেটাবেসের সারি ফিরিয়ে আনে না, API অনুরোধ বাতিল করে না, পৌঁছে যাওয়া বার্তা মুছে না, বা পেমেন্ট ফেরত দেয় না। একে শুধু সোর্স ইতিহাস মেরামতের উপায় হিসেবে দেখুন।

ডেটাবেস রোলব্যাক কি বাহ্যিক API কল পূর্বাবস্থায় ফেরায়?

ডেটাবেস ট্রানজ্যাকশন কমিট না হওয়া স্থানীয় লেখা রোল ব্যাক করতে পারে। কমিটের পরে পুনরুদ্ধার ডেটাবেসকে আগের অবস্থায় ফেরাতে পারে, তবে এতে সম্পর্কহীন বৈধ লেখাও হারাতে পারে এবং বাইরের সিস্টেমের কাজ উল্টাতে পারে না। একে সার্বজনীন আনডু বাটন নয়, পুনরুদ্ধার প্রক্রিয়া হিসেবে ব্যবহার করুন।

idempotency key কি রোলব্যাকের মতো?

না। প্রোভাইডার সঠিকভাবে সমর্থন করলে idempotency একই প্রভাব বারবার তৈরি হওয়া থেকে আটকায়। এটি প্রথম সফল অনুরোধ বাতিল করে না, বা একই শনাক্তকারীসহ ভিন্ন অনুরোধ নিরাপদ হবে এমন নিশ্চয়তা দেয় না।

কোন এজেন্ট কাজের জন্য মানুষের অনুমোদন দরকার?

অ্যাপের বাইরে আইনি, আর্থিক, গোপনীয়তা, সুনামগত বা কার্যগত পরিণতি তৈরি করতে পারে এমন কাজে অনুমোদন নিন। যেমন বার্তা পাঠানো, পেমেন্ট ক্যাপচার, তথ্য প্রকাশ, অ্যাক্সেস বদল, অর্ডার দেওয়া এবং শারীরিক কাজ শুরু করে এমন API কল। রিড অপারেশন ও স্থানীয় ড্রাফটে সাধারণত একই গেটের দরকার হয় না।

বাহ্যিক API কলের আগে এজেন্টের কী রেকর্ড করা উচিত?

intent শনাক্তকারী, সঠিক আর্গুমেন্ট, অনুমোদন, অনুমোদনের পরিধি, idempotency মান, চেষ্টার নম্বর, প্রোভাইডার রেফারেন্স, প্রতিক্রিয়ার অবস্থা এবং ক্ষতিপূরণের অবস্থা লিখে রাখুন। চালানোর আগে রেকর্ড রাখুন এবং প্রতিটি চেষ্টার পর আপডেট করুন। যে রোলব্যাক তদন্ত করছেন, তাতে অ্যাপ লগ খুব সহজেই হারিয়ে যেতে পারে।

টাইমআউট হওয়া অনুরোধ এজেন্ট কীভাবে নিরাপদে আবার চেষ্টা করবে?

অপারেশন সত্যিকার অর্থে idempotent হলে, অথবা গ্রহণকারী সেবা স্থায়ী idempotency মান চিনতে পারলে পুনরায় চেষ্টা নিরাপদ। টাইমআউট অস্পষ্ট, কারণ কলার কোনো উত্তর না পেলেও প্রথম অনুরোধ সফল হতে পারে। সম্ভব হলে নতুন অনুরোধ পাঠানোর আগে একই অপারেশন শনাক্তকারী দিয়ে প্রোভাইডারের কাছে খোঁজ নিন।

কখন ক্ষতিপূরণমূলক কাজ স্বয়ংক্রিয়ভাবে চালানো উচিত?

বাহ্যিক কাজটির অর্থবহ বিপরীত ব্যবস্থা থাকলে এবং নীতি অনুমতি দিলে ক্ষতিপূরণ ব্যবহার করুন। রিফান্ড, বাতিলের অনুরোধ ও সংশোধনী বার্তা নতুন ইতিহাস যোগ করে, পুরোনো ইতিহাস মুছে না। এগুলো আরেকটি চার্জ, বার্তা, অনুমতি পরিবর্তন বা আইনি অঙ্গীকার তৈরি করতে পারে বলে অন্ধভাবে স্বয়ংক্রিয় করবেন না।

এজেন্ট টুলের রোলব্যাক নিরাপত্তা কীভাবে পরীক্ষা করবেন?

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

Related posts

AI অ্যাপ বিল্ডারের মূল্য নির্ভর করে কোনটিকে কাজ ধরা হচ্ছে তার উপর

পুনরায় চেষ্টা ও ব্যাকগ্রাউন্ড এজেন্টসহ সপ্তাহে 100টি প্রম্পটের জন্য AI অ্যাপ বিল্ডারের মূল্য তুলনা করুন, একটি কাজের খাতা ও স্পষ্ট খরচের সূত্রে।

AI নিরাপত্তা পরীক্ষা কি SAST, DAST ও পেনটেস্টের বিকল্প হতে পারে?

AI নিরাপত্তা পরীক্ষা কোথায় বাস্তব ত্রুটি খুঁজে পায়, কোথায় SAST, DAST ও মানবীয় পেনটেস্ট এখনও এগিয়ে, এবং পুনরাবৃত্ত শব্দ ছাড়াই কীভাবে এগুলো একসঙ্গে ব্যবহার করবেন জানুন।

প্রোডাকশন React ও Flutter প্ল্যাটফর্মের তুলনা

২০২৬ সালের স্ট্যাক বাছার আগে প্রোডাকশন React ও Flutter প্ল্যাটফর্মের কোড আউটপুট, ব্যাকএন্ড, টেস্টিং, ডিপ্লয়মেন্ট ও মালিকানা তুলনা করুন।