২৩ অক্টো, ২০২৫·7 মিনিট
Cron + database প্যাটার্ন: কিউ ছাড়া ব্যাকগ্রাউন্ড জব
ক্রন + ডেটাবেস প্যাটার্ন শিখুন: পূর্ণ queue সিস্টেম ছাড়া নির্ধারিত ব্যাকগ্রাউন্ড জব চলান, রিট্রাই, লকিং ও আইডেম্পোটেন্সি সহ।
সমস্যা: অতিরিক্ত ইনফ্রাস্ট্রাকচার ছাড়া নির্ধারিত কাজ\n\nঅধিকাংশ অ্যাপে কাজ পরে বা নির্ধারিত সময়েই করা লাগে: ফলো-আপ ইমেইল পাঠানো, রাতে বিলিং চেক চালানো, পুরনো রেকর্ডগুলো পরিস্কার করা, রিপোর্ট পুনর্নির্মাণ করা, অথবা ক্যাশ রিফ্রেশ করা।\n\nশুরুতে, ব্যাকগ্রাউন্ড জবের জন্য পূর্ণQueue সিস্টেম যোগ করা প্রলুব্ধ করে কারণ তা মনে হয় “ঠিক” উপায়। কিন্তু queue গুলো আরও অনেক moving part যোগ করে: আরেকটি সার্ভিস চালানো, মনিটার করা, ডিপ্লয় করা এবং ডিবাগ করা। ছোট টীম (বা একক প্রতিষ্ঠাতা) জন্য সেই অতিরিক্ত ওজন আপনাকে ধীর করতে পারে।\n\nতাহলে মূল প্রশ্ন হল: অতিরিক্ত ইনফ্রাস্ট্রাকচার চালানো ছাড়া নির্ধারিত কাজ কিভাবে নির্ভরযোগ্যভাবে চালাবেন?\n\nপ্রথম সাধারণ চেষ্টা সাধারণত সহজ: একটি cron এন্ট্রি যোগ করে একটি endpoint হিট করানো, এবং সেই endpoint কাজটি করবে। এটি কাজ করে যতদিন না করে। একবার আপনার একটির বেশি সার্ভার থাকে, ভুল সময়ে ডিপ্লয় হয়, বা একটি কাজ প্রত্যাশার চেয়ে বেশি সময় নেয়, তখন বিভ্রান্তিকর ব্যর্থতা দেখা শুরু করে।\n\nনির্ধারিত কাজ সাধারণত কয়েকটি পূর্বানুমানযোগ্যভাবে ভেঙে পড়ে:\n\n- ডাবল রান: দুইটি সার্ভার একসঙ্গে একই টাস্ক চালায়, ফলে চালান দুবার তৈরি হয় বা ইমেইল দুবার পাঠে।\n- হারানো রান: ডিপ্লয়ের সময় cron কল ব্যর্থ হয় এবং কেউ লক্ষ্য করে না যতক্ষণ না ব্যবহারকারী অভিযোগ করে।\n- নীরব ব্যর্থতা: জব একবার এরর করে, তারপর আর চালানো হয় না কারণ কোনো রিট্রাই পরিকল্পনা নেই।\n- আংশিক কাজ: জব মাঝপথে ক্র্যাশ করে এবং ডেটা অদ্ভুত অবস্থায় রেখে যায়।\n- অডিট ট্রেইল নেই: আপনি জানাতে পারেন না “শেষে কখন এটি চলেছে?” বা “গত রাতে কি হয়েছে?”\n\ncron + database প্যাটার্ন একটি মধ্যপথ। আপনি এখনও cron ব্যবহার করে নির্ধারিত সময়ে "ওয়েক আপ" করছেন, কিন্তু কাজের উদ্দেশ্য ও অবস্থান ডাটাবেসে সংরক্ষণ করা হয় যাতে সিস্টেম সমন্বয়, রিট্রাই এবং কি ঘটেছে তা রেকর্ড করতে পারে।\n\nএটি ভাল মানায় যখন আপনার ইতিমধ্যে একটি ডেটাবেস (অften PostgreSQL), সীমিত সংখ্যক জব টাইপ আছে, এবং আপনি কম অপস কাজের সাথে পূর্বানুমানযোগ্য আচরণ চান। এটি দ্রুত তৈরি করা আধুনিক স্ট্যাকের অ্যাপগুলোর জন্যও প্রাকৃতিক পছন্দ (উদাহরণস্বরূপ, React + Go + PostgreSQL সেটআপ)।\n\nএটি সঠিক নয় যখন আপনাকে খুব উচ্চ থ্রুপুট, দীর্ঘ-চলমান কাজ যা প্রগতি স্ট্রিম করে চালাতে হয়, বিভিন্ন জব টাইপের মধ্যে কঠোর ordering প্রয়োজন, বা ভারী fan-out (প্রতি মিনিটে হাজারো সাব-টাস্ক) দরকার। সেক্ষেত্রে একটি প্রকৃত queue এবং ডেডিকেটেড ওয়ার্কার সাধারণত নিজেকে মেটায়।\n\n## সরল ভাষায় মূল ধারণা\n\ncron + database প্যাটার্ন একটি পূর্ণ queue সিস্টেম চালানো ছাড়া নির্ধারিত ব্যাকগ্রাউন্ড কাজ চালায়। আপনি এখনও cron (অথবা যেকোনো scheduler) ব্যবহার করেন, কিন্তু cron নির্ধারণ করে না কী চালানো হবে। এটি শুধু worker-কে ঘুম থেকে জাগায় (একমিনিটে একবার সাধারণ)। ডেটাবেস সিদ্ধান্ত নেয় কোন কাজ অনিবার্য এবং নিশ্চিত করে যে প্রতিটি জব শুধু একটিই নেয়।\n\nএটিকে ভাবুন একটি শেয়ার্ড চেকলিস্টের মতো। Cron হচ্ছে সেই ব্যক্তি যে প্রতি মিনিটে ঘরে এসে বলে, “কারো কিছু করবার আছে কি?” ডেটাবেস হচ্ছে সেই হোয়াইটবোর্ড যা দেখায় কী নির্ধারিত, কী নেওয়া হয়েছে, এবং কী সমাপ্ত।\n\nপর্দা-পেছনের অংশগুলো সরল:\n\n- একটি একক scheduler trigger ঘনঘন চলে।\n- একটি jobs টেবিল থাকে যা “কি” এবং “কখন” (due time), সাথে status ও attempt count ধারণ করে।\n- এক বা একাধিক worker টেবিল পোল করে, একটি জব দাবি করে, এবং কাজটি করে।\n- দাবি করার সময় ডেটাবেস লক ব্যবহার করা হয় যাতে দুটি worker একই row নিতে না পারে।\n- ডেটাবেস কি রন করেছে, কী ব্যর্থ হয়েছে, এবং কী রিট্রাই হওয়া উচিত—সবকিছুর source of truth থাকে।\n\nউদাহরণ: প্রতিদিন সকালে invoice রিমাইন্ডার পাঠাতে চান, প্রতিটি 10 মিনিটে ক্যাশ রিফ্রেশ করতে চান, এবং রাতের বেলা পুরনো সেশন ক্লিনআপ করতে চান। আলাদা তিনটি cron কমান্ডের বদলে (প্রতিটিতেই ওভারল্যাপ ও ব্যর্থতার মোড আছে), আপনি জব এন্ট্রিগুলো এক জায়গায় স্টোর করেন। Cron একই worker প্রসেস শুরু করে। Worker Postgres-কে জিজ্ঞাসা করে, “এখন কী_due?” এবং Postgres নিরাপদভাবে একে একে প্রতিটি জব কে claim করতে দেয়।\n\nএটি ধীরে ধীরে স্কেল করে। আপনি এক সার্ভারে একটি worker দিয়ে শুরু করতে পারেন। পরে, আপনি পাঁচটি worker বিভিন্ন সার্ভারে চালাতে পারেন। কনট্রাক্ট একই থাকে: টেবিলই কনট্রাক্ট।\n\nমনোভাব পরিবর্তন সহজ: cron কেবল ওয়েক-আপ কল। ডেটাবেস ট্রাফিক কপ যা সিদ্ধান্ত নেয় কী চালানো যাবে, কী হয়েছে তা রেকর্ড করে, এবং কিছু ভুল হলে পরিষ্কার ইতিহাস দেয়।\n\n## jobs টেবিল ডিজাইন করা (প্র্যাকটিক্যাল স্কিমা)\n\nএই প্যাটার্নটি সবচেয়ে ভাল কাজ করে যখন আপনার ডেটাবেসই কী চালানো উচিত, কখন চালানো উচিত, এবং শেষবার কী ঘটেছিল—এর source of truth হয়। স্কিমাটা জটিল নয়, কিন্তু ছোট ডিটেইলগুলো (লক ফিল্ড এবং সঠিক ইনডেক্স) লোড বাড়ার সাথে বড় প্রভাব ফেলে।\n\n### এক টেবিল না দুই টেবিল?\n\nদুইটি সাধারণ পদ্ধতি:\n\n- যখন আপনি প্রতিটি জবের সর্বশেষ অবস্থা নিয়ে কেবল আগ্রহী (সরল, কম joins)।\n- যখন আপনি “এই জব কি” এবং “প্রতিবার এটি চালানো হয়েছিল” আলাদা রাখতে চান (ভিত্তিহীন ইতিহাস, ডিবাগ করা সহজ)।\n\nআপনি যদি আশা করেন যে বারবার ফেইলিউর ডিবাগ করতে হবে, ইতিহাস রাখুন। সবচেয়ে ছোট সেটআপ চাইলে এক টেবিল দিয়েই শুরু করুন এবং পরে ইতিহাস যোগ করুন।\n\n### একটি ব্যবহারিক স্কিমা (দুই-টেবিল ভার্সন)\n\nনিচে একটি PostgreSQL-বান্ধব বিন্যাস। আপনি যদি Go দিয়ে PostgreSQL তৈরি করেন, এই কলামগুলো struct-এ সহজে মানচিত্র হয়।\n\n\n\nকয়েকটি ডিটেইল যা পরে ব্যথা বাঁচায়:\n\n- একটি ছোট স্ট্রিং রাখুন যাতে আপনি রাউটিং করতে পারেন (যেমন )।\n- হিসেবে রাখুন যাতে মাইগ্রেশন ছাড়া এটি পরিবর্তন করা যায়।\n- হচ্ছে আপনার “পরবর্তী নির্ধারিত সময়”। Cron (অথবা scheduler script) এটি সেট করে, worker এটি গ্রহণ করে।\n- এবং worker-দের জব claim করার সুযোগ দেয় যাতে তারা একে অপরের ওপর পা না রাখে।\n- সংক্ষিপ্ত এবং মানুষের জন্য পাঠ যোগ্য রাখুন। স্ট্যাক ট্রেস দরকার হলে সেটা আলাদা রাখুন।\n\n### প্রয়োজনীয় ইনডেক্সগুলো\n\nইনডেক্স না থাকলে worker-রা বেশি সার্চ করে ফেলবে। শুরুতে রাখুন:\n\n- দ্রুত due কাজ খুঁজতে ইনডেক্স: \n- মেয়াদোত্তীর্ণ লক শনাক্ত করতে সহায়াকারী ইনডেক্স: \n- ঐচ্ছিক: active work-এ partial index (উদাহরণ: status এবং )\n\nএগুলো “পরবর্তী runnable job খুঁজুন” কুয়েরিটিকে দ্রুত রাখে এমনকি টেবিল বড় হলেও।\n\n## নিরাপদভাবে লকিং এবং জব দাবি করা\n\nলক্ষ্য সহজ: অনেক worker চলতে পারে, কিন্তু একটি নির্দিষ্ট জব কেবল একজনই গ্রহণ করবে। যদি দুটি worker একই row প্রসেস করে, আপনি ডাবল ইমেইল, ডাবল চার্জ বা বিশৃঙ্খল ডেটা পাবেন।\n\nনিরাপদ উপায় একটি জব ক্লেইমকে একটি “লিজ” হিসেবে বিবেচনা করা। Worker জবটিকে একটি সংক্ষিপ্ত সময়ের জন্য লক করে। Worker ক্র্যাশ করলে লিজ মেয়াদ শেষ হয়ে অন্য worker এটি নিতে পারে। এ কারণেই আছে।\n\n### ক্র্যাশ কাজগুলো চিরস্থায়ী ব্লক না করার জন্য লিজ ব্যবহার করুন\n\nলিজ না থাকলে, একটি worker জব লক করে এবং কখনো আনলক না করেও যেতে পারে (প্রক্রিয়া নিহত, সার্ভার রিবুট, ভুল ডিপ্লয়)। থাকলে সময় পেরিয়ে গেলে জবটি আবার পাওয়া যায়।\n\nসাধারণ নিয়ম: একটি জব তখনই claim করা যাবে যখন বা ।\n\n### এক এটমিক আপডেটে জব claim করুন\n\nকী ডিটেইল হল: জবকে একটি সিঙ্গেল স্টেটমেন্ট (বা এক ট্রানজ্যাকশনে) এ দাবি করা। আপনি চান ডাটাবেস রেফারি হিসেবে কাজ করুক।\n\nনিচে একটি সাধারণ PostgreSQL প্যাটার্ন: একটি due জব বেছে নিন, সেটি লক করুন, এবং worker-কে রিটার্ন করুন। (এই উদাহরণটি একটি সিঙ্গেল টেবিল ব্যবহার করে; একই ধারণা থেকেই প্রয়োগ করা যাবে.)\n\n\n\nকেন এটি কাজ করে:\n\n- একাধিক worker-কে প্রতিদ্বন্দ্বিতা করার সুযোগ দেয় ব্লক না করে।\n- লিজ দাবি করার সময় সেট করা হয়, তাই অন্য worker গুলো একে মেয়াদ শেষ না হওয়া পর্যন্ত উপেক্ষা করে।\n- রেস জয় করা worker-কে row হস্তান্তর করে।\n\n### লিজ কতক্ষণ হওয়া উচিত, এবং কীভাবে এটি নবায়ন করবেন?\n\nলিজটি সাধারণ রান থেকে লম্বা হতে হবে, কিন্তু যথেষ্ট সংক্ষিপ্ত যাতে ক্র্যাশ দ্রুত পুনরুদ্ধার হয়। যদি বেশিরভাগ জব 10 সেকেন্ডে শেষ হয়, 2 মিনিট লিজ যথেষ্ট।\n\nদীর্ঘ টাস্কের জন্য, কাজ করার সময়ে লিজ নবায়ন করুন (একটি হার্টবিট)। সহজ পদ্ধতি: প্রতি 30 সেকেন্ডে যদি আপনি এখনও জবের মালিক হন, বাড়িয়ে দিন।\n\n- লিজ দৈর্ঘ্য: আপনার সাধারণ কাজ সময়ের 5x থেকে 20x\n- হার্টবিট ইন্টারভাল: লিজের 1/4 থেকে 1/2\n- নবায়ন আপডেটে থাকা উচিত \n\nএই শেষ শর্তটি গুরুত্বপূর্ণ। এটি প্রতিরোধ করে যে একটি worker সে আর মালিক নয় এমন জবের লিজ বাড়িয়ে দেয়।\n\n## রিট্রাই এবং ব্যাকঅফ যা পূর্বানুমানযোগ্যভাবে আচরণ করে\n\nরিট্রাই-এই প্যাটার্নটি শান্ত মনে হবে নাকি বিশৃঙ্খল হবে সেটাই নির্ধারণ করে। লক্ষ্য সহজ: যখন একটি জব ব্যর্থ হয়, পরবর্তীতে পুনরায় চেষ্টা করুন এমনভাবে যা ব্যাখ্যা করা যায়, মাপা যায়, এবং বন্ধ করা যায়।\n\nপ্রথমে জব স্টেট স্পষ্ট এবং সসীম রাখুন: , , , , । বাস্তবে, বেশিরভাগ টীম -কে “ব্যর্থ কিন্তু আবার রিট্রাই করা হবে” এবং -কে “ব্যর্থ এবং আমরা ছাড় দিয়েছি” হিসেবে ব্যবহার করে। এই পার্থক্যটি ইনফিনিট লুপ প্রতিরোধ করে।\n\nচেষ্টা গণনা দ্বিতীয় গার্ডরেইল। (কতবার চেষ্টা করা হয়েছে) এবং (কতবার অনুমোদিত) রাখুন। যখন worker একটি ত্রুটি ধরে, এটি উচিত:\n\n- বাড়ানো\n- যদি তাহলে স্টেট সেট করা, অন্যথায় \n- পরবর্তী ট্রাই-এর জন্য গণনা করা (শুধু -এর ক্ষেত্রে)\n\nব্যাকঅফ হল কেবল নিয়ম যা পরবর্তী নির্ধারণ করে। একটি পদ্ধতি বেছে নিন, ডকুমেন্ট করুন, এবং ধারাবাহিক রাখুন:\n\n- স্থির বিলম্ব: সর্বদা 1 মিনিট অপেক্ষা করুন\n- এক্সপোনেনশিয়াল: 1m, 2m, 4m, 8m\n- সীমাবদ্ধ এক্সপোনেনশিয়াল: এক্সপোনেনশিয়াল কিন্তু সর্বোচ্চ, ধরুন 30m পর্যন্ত নয়\n- জিটার যোগ করুন: একটু এলোমেলো করুন যাতে সব জব একই সেকেন্ডে রিট্রাই না করে\n\nজিটার তখনই গুরুত্বপূর্ণ যখন একটি নির্ভরতা ডাউন হয় এবং ফিরে আসে। জিটারের ছাড়া, শত শত জব একসঙ্গে রিট্রাই করে আবার ব্যর্থ হয়ে যায়।\n\nব্যর্থতা দৃশ্যমান ও ডিবাগ যোগ্য করে রাখার জন্য পর্যাপ্ত ত্রুটি বিবরণ স্টোর করুন। পুরো লগিং সিস্টেমের প্রয়োজন নেই, কিন্তু মৌলিক জিনিসগুলো দরকার:\n\n- (সংক্ষিপ্ত বার্তা, admin স্ক্রিনে দেখানোর মতো)\n- বা (গ্রুপিং-এ সাহায্য করে)\n- এবং \n- ঐচ্ছিক (সাইজ আপনি নিয়ন্ত্রণ করলে)\n\nএকটি বাস্তব নিয়ম যেটি ভাল কাজ করে: 10 চেষ্টা পর চিহ্নিত করুন, এবং জিটারের সাথে এক্সপোনেনশিয়াল ব্যাকঅফ ব্যবহার করুন। এতে ট্রানজিয়েন্ট ব্যর্থতা পুনরায় চেষ্টা হয়, কিন্তু ভাঙা জবগুলো CPU নিরবলে খেয়ে ফেলে না।\n\n## আইডেম্পোটেন্সি: রিট্রাই-এও ডুপ্লিকেট প্রতিরোধ করা\n\nআইডেম্পোটেন্সি মানে আপনার জব দুবার চালালে শেষ ফল একই থাকা। এই প্যাটার্নে এটি গুরুত্বপূর্ণ কারণ একই row ক্র্যাশ, টাইমআউট, বা রিট্রাই নিয়ে আবার নেওয়া হতে পারে। যদি আপনার কাজ হয় “ইনভয়েস ইমেইল পাঠানো”, তা দুবার চালানো নিরপত্তে নয়।\n\nপ্রাযুক্তিকভাবে ভাবুন: প্রতিটি জবকে (1) কাজ করা এবং (2) প্রভাব প্রয়োগ করা—এই দুই ভাগে ভাগ করুন। আপনি চান যে প্রভাব একবারই ঘটুক, যদিও কাজটি একাধিকবার চেষ্টা করা হতে পারে।\n\n### ব্যবসায়িক ইভেন্ট-ভিত্তিক একটি idempotency কী ব্যবহার করুন\n\nএকটি idempotency কী হওয়া উচিত জব যা প্রতিনিধিত্ব করে তার সঙ্গে সম্পর্কিত, worker অ্যাটেম্পট থেকে নয়। ভালো কী গুলো স্থির এবং সহজে বর্ণনীয়, যেমন , , বা । যদি দুইটি জব অ্যাটেম্পট একই বাস্তব-বিশ্বের ইভেন্টকে বোঝায়, তাদের একই কী থাকা উচিত।\n\nউদাহরণ: “2026-01-14-র জন্য দৈনিক সেলস রিপোর্ট জেনারেট” এর কী হতে পারে । “Invoice 812 চার্জ করা” হতে পারে ।\n\n### ডেটাবেস কনস্ট্রেইন্ট দিয়ে “কেবল একবার” প্রয়োগ করুন\n\nসবচেয়ে সহজ গার্ডরেইল হল PostgreSQL-কে ডুপ্লিকেট নাকচ করতে দেয়া। idempotency কী কোথাও স্টোর করুন যা ইনডেক্স করা যায়, তারপর একটি unique constraint যোগ করুন।\n\n\n\nএটি একই কী সহ দুটি row একই সময়ে থাকা প্রতিরোধ করে। যদি আপনার ডিজাইন ইতিহাস বজায় রাখে (একাধিক row অনুমোদন করে), তাহলে uniqueness আরেকটায় রাখুন—যেমন বা ।\n\nসাধারণ সাইড-ইফেক্ট যা রক্ষা করা উচিত:\n\n- ইমেইল: টেবিলে একটি ইউনিক কী দিয়ে রেকর্ড তৈরি করুন আগে পাঠানোর, অথবা পাঠানোর পরে provider message id রেকর্ড করুন।\n- ওয়েবহুক: স্টোর করুন এবং যদি সেটা থাকে তাহলে স্কিপ করুন।\n- পেমেন্ট: সর্বদা payment provider-এর idempotency ফিচার ব্যবহার করুন সাথে নিজের ডাটাবেস ইউনিক কী।\n- ফাইল লিখা: টেম্প নাম দিয়ে লিখুন, তারপর রিনেম করুন, বা দিয়ে কীবদ্ধ করা রেকর্ড স্টোর করুন।\n\nআপনি যদি Postgres-backed স্ট্যাকে তৈরি করছেন (উদাহরণ, Go + PostgreSQL ব্যাকএন্ড), এই ইউনিকনেস চেকগুলো দ্রুত এবং ডেটার কাছে রাখা সহজ। মূল ধারণা সহজ: রিট্রাই স্বাভাবিক, ডুপ্লিকেট অপশনাল।\n\n## ধাপে ধাপে: একটি মিনিমাল worker এবং scheduler বানানো\n\nএকটি বিরক্তিকর runtime বাছুন এবং সেটাতে থাকুন। cron + database প্যাটার্নের লক্ষ্য কম moving part, তাই একটি ছোট Go, Node, বা Python প্রসেস যা PostgreSQL-এ কথা বলে সাধারণত যথেষ্ট।\n\n### পাঁচটি ছোট ধাপে বানান\n\n1) একটি টেবিল (ওপরের মতো) যোগ করুন, তারপর ইনডেক্স করুন, এবং একটি ইনডেক্স যোগ করুন যা আপনার worker-কে দ্রুত উপলব্ধ জব খুঁজতে সাহায্য করবে (উদাহরণ: )।\n\n2) আপনার অ্যাপ একটি রো ইনসার্ট করবে এখন বা ভবিষ্যত সময় দিয়ে। payload ছোট ও পূর্বানুমানযোগ্য রাখুন (IDs এবং job type, বড় ব্লব নয়)।\n\n\n\n3) এটি একটি ট্রানজ্যাকশনে চালান। কয়েকটি due জব সিলেক্ট করুন, সেগুলো লক করুন যাতে অন্য worker ছাড়ে, এবং একই ট্রানজ্যাকশনে সেগুলোকে হিসেবে চিহ্নিত করুন।\n\n\n\n4) প্রত্যেক claimed job-এর জন্য কাজটি করুন, তারপর -এ -সহ আপডেট করুন। যদি ব্যর্থ হয়, একটি ত্রুটি বার্তা রেকর্ড করে সেটিকে আবার -এ নিয়ে যান নতুন (backoff) সহ। ফাইনালাইজেশন আপডেটগুলো ছোট রাখুন এবং সবসময় চালান, এমনকি আপনার প্রসেস বন্ধ হলে ও।\n\n5) একটি সহজ সূত্র ব্যবহার করুন যেমন , এবং পেরলে সেট করুন।\n\n### মৌলিক ভিজিবিলিটি যোগ করুন\n\nপ্রথম দিনেই পূর্ণ ড্যাশবোর্ড দরকার নেই, কিন্তু সমস্যা লক্ষ্য করার মতো কিছু দরকার।\n\n- প্রতিটি জবের জন্য একটি লাইন লগ করুন: claimed, succeeded, failed, retried, dead।\n- “dead jobs” এবং “old running jobs” এর জন্য একটি সহজ admin কুয়েরি বা ভিউ তৈরি করুন।\n- বাড়তে থাকা ফেইলিউর কাউন্টে অ্যালার্ট দিন (উদাহরণ: গত একঘন্টায় N-র বেশি dead jobs)।\n\nআপনি যদি আগে থেকেই Go + PostgreSQL স্ট্যাকে থাকেন, এটি একটি single worker binary এবং cron-এ ভালো মানায়।\n\n## এমন একটি বাস্তব উদাহরণ যা আপনি কপি করতে পারেন\n\nধরা যাক একটি ছোট SaaS অ্যাপের দুটি নির্ধারিত কাজ:\n\n- রাতের ক্লিনআপ যা মেয়াদোত্তীর্ণ সেশন এবং পুরনো টেম্পরারি ফাইল মুছে দেয়।\n- সাপ্তাহিক “আপনার কার্যকলাপ রিপোর্ট” ইমেইল যা প্রতি সোমবার প্রত্যেক ব্যবহারকারীকে পাঠানো হয়।\n\nসরল রাখুন: একটি PostgreSQL টেবিল জবগুলো ধরে রাখে, এবং একটি worker প্রতি মিনিটে চলে (cron দ্বারা ট্রিগার)। Worker due জব claim করে, চালায়, এবং সাফল্য বা ব্যর্থতা রেকর্ড করে।\n\n### কি_enqueue হয়, এবং কখন\n\nআপনি বিভিন্ন জায়গা থেকে জব enqueue করতে পারেন:\n\n- প্রতিদিন 02:00: একটি জব enqueue করুন “আজকের” জন্য।\n- সাইনআপে: ব্যবহারকারীর পরবর্তী সোমবারের জন্য জব enqueue করুন।\n- একটি ইভেন্টের পরে (যেমন “ব্যবহারকারী Export report ক্লিক করেছে”): নির্দিষ্ট তারিখ-রেঞ্জের জন্য তাৎক্ষণিকভাবে enqueue করুন।\n\nপে-লোড হল কেবল worker-কে যা লাগে তা—ছোট রাখুন যাতে রিট্রাই সহজ হয়।\n\n\n\n### কিভাবে idempotency ডাবল-সেন্ড রোধ করে\n\nএকটি worker সবচেয়ে খারাপ মুহূর্তে ক্র্যাশ করতে পারে: ইমেইল পাঠানোর ঠিক পরে, কিন্তু জবটিকে “done” চিহ্নিত করার আগে। পুনরায় শুরু হলে এটি একই জব আবার নিতে পারে।\n\nডাবল-সেন্ড বন্ধ করতে, কাজটিকে একটি প্রাকৃতিক dedupe কী দিন এবং ডেটাবেস যেখানে এটি প্রয়োগ করতে পারে সেখানে এটি সংরক্ষণ করুন। সাপ্তাহিক রিপোর্টের জন্য ভাল কী হতে পারে । পাঠানোর আগে worker রেকর্ড করে “আমি রিপোর্ট X পাঠাতে যাচ্ছি”। যদি সেই রেকর্ড ইতিমধ্যেই থাকে, worker স্কিপ করে।\n\nএটি একটি টেবিল যেটির -এ ইউনিক কনস্ট্রেইন্ট থাকতে পারে, অথবা জবটিকেই একটি ইউনিক দেওয়া হতে পারে।\n\n### একটি ব্যর্থতা কেমন দেখায় (এবং কিভাবে পুনরুদ্ধার করে)\n\nধরা যাক আপনার ইমেইল প্রোভাইডার টাইমআউট দেয়। জব ব্যর্থ হয়, তখন worker:\n\n- বাড়ায়\n- ডিবাগের জন্য ত্রুটি বার্তা সংরক্ষণ করে\n- ব্যাকঅফ সহ পরবর্তী ট্রাই শিডিউল করে (উদাহরণ: +1 মিনিট, +5 মিনিট, +30 মিনিট, +2 ঘন্টা)\n\nযদি এটি আপনার সীমা (উদাহরণ 10 অ্যাটেম্পট) পেরিয়ে যায়, সেটি হিসেবে চিহ্নিত করুন এবং পুনরায় চেষ্টা বন্ধ করুন। জব বা তো সাফল্য একবার পাবে, বা এটি স্পষ্ট পরিকল্পনায় পুনরায় চেষ্টা করবে, এবং idempotency রিট্রাইকে নিরাপদ করে।\n\n## সাধারণ ভুল ও ফাঁদগুলি\n\ncron + database প্যাটার্নটি সরল, কিন্তু ছোট ভুলগুলো এটিকে ডুপ্লিকেট, আটকে থাকা কাজ, বা হঠাৎ লোডে পরিণত করতে পারে। বেশিরভাগ সমস্যা প্রথম ক্র্যাশ, ডিপ্লয়, বা ট্রাফিক স্পাইকে দেখা যায়।\n\n### ডাবল বা আটকে থাকা কাজ ঘটানোর ভুলগুলো\n\nবাস্তব-জীবনের বেশিরভাগ ইনসিডেন্ট কয়েকটি ফাঁদ থেকে আসে:\n\n- একাধিক cron এন্ট্রি থেকে একই জব চালানো লিজ না থাকলে। যদি দুই সার্ভার একই মিনিটে টিক করে, তারা একই কাজ claim করতে পারে যদি claim step এটমিক না হয় এবং একই ট্রানজ্যাকশনে লক/লিজ সেট না করে।\n- বাদ দেয়া। যদি একটি worker claim করার পরে ক্র্যাশ করে, rowটি “in progress” হতেই থাকতে পারে। একটি লিজ টাইমস্ট্যাম্প অন্য worker-কে পরে নিতে দেয়।\n- ব্যর্থতার উপর তাত্ক্ষণিক রিট্রাই করা। যখন কোনো API ডাউন, ইনস্ট্যান্ট রিট্রাই স্পাইক তৈরি করে, রেট লিমিট বার্ন করে, এবং সংকীর্ণ লুপে ব্যর্থতা চালিয়ে যায়। সর্বদা পরবর্তী চেষ্টা ভবিষ্যতে শিডিউল করুন।\n- “at least once” কে “exactly once” মনে করা। একটি জব দুবার চলতে পারে (টাইমআউট, worker রিস্টার্ট, নেটওয়ার্ক সমস্যা)। যদি দুইবার চালালে ক্ষতি হয়, side effects-কে পুনরাবৃত্তি-নিরাপদ বানান।\n- জব রো-তে বিশাল পে-লোড রাখা। বড় JSON ব্লব টেবিলকে ফোলা করে, ইনডেক্স ধীর করে, এবং লকিংকে ভারী করে। একটি রেফারেন্স (যেমন , , বা ফাইল কী) রাখুন এবং রান করার সময় বাকি ডেটা ফেচ করুন।\n\nউদাহরণ: আপনি সাপ্তাহিক ইনভয়েস ইমেইল পাঠান। যদি worker পাঠানোর পরে কিন্তু জব ডান না করা পর্যন্ত টাইমআউট করে, একই জব পুনরায় চালানো হতে পারে এবং ডুপ্লিকেট ইমেইল পাঠাতে পারে। এই প্যাটার্নের জন্য এটি স্বাভাবিক, যদি না আপনি একটি গার্ডরেইল যোগ করেন (উদাহরণ: invoice id দিয়ে ইউনিক "email sent" ইভেন্ট রেকর্ড)।\n\n### কম স্পষ্ট গটচাস\n\nএকই দীর্ঘ ট্রানজ্যাকশনে scheduling এবং execution মিশিয়ে দেবেন না। যদি আপনি নেটওয়ার্ক কল করার সময় একটি ট্রানজ্যাকশন খোলা রাখেন, আপনি লকগুলো অপ্রয়োজনীয়ভাবে দীর্ঘ সময় ধরে রাখেন এবং অন্য worker-দের ব্লক করেন।\n\nমেশিনগুলোর মধ্যে ঘড়ির পার্থক্য নজর রাখুন। এবং এর জন্য্ ডাটাবেস টাইম ( in PostgreSQL) ব্যবহার করুন, অ্যাপ সার্ভারের ঘড়ি নয়।\n\nএকটি স্পষ্ট সর্বোচ্চ runtime সেট করুন। যদি একটি জব 30 মিনিট নিতে পারে, লিজটি তার চেয়েও বড় করুন, এবং প্রয়োজন হলে নবায়ন করুন। অন্যথায় আরেকটি worker মাঝখানে নিয়ে নিতে পারে।\n\nজব টেবিলটি সুস্থ রাখুন। যদি সম্পন্ন জব চিরতরে জমে থাকে, কুয়েরি ধীর হয় এবং লক contention বাড়ে। টেবিল খুব বড় হওয়ার আগে একটি retention নিয়ম নির্ধারণ করুন (archive বা delete)।\n\n## দ্রুত চেকলিস্ট এবং পরবর্তী ধাপ\n\n### দ্রুত চেকলিস্ট\n\nএই প্যাটার্ন শিপ করার আগে মৌল বিষয়গুলো চেক করুন। এখানে ছোট একটি ভুল সাধারণত আটকে থাকা জব, আশ্চর্যের ডুপ্লিকেট, বা ডেটাবেসে hammer করা হয়ে ওঠে।\n\n- আপনার jobs টেবিলে মৌলিক কলাম আছে: , , , , এবং (সাথে বা অনুরূপ যেন আপনি কী হয়েছে দেখেন)।\n- প্রতিটি জব নিরাপদে দুইবার চালানো যাবে। যদি নিশ্চিত না হন, একটি idempotency কী বা সাইড-ইফেক্টের চারপাশে একটি uniqueness নিয়ম যোগ করুন (যেমন, প্রতি -এর জন্য একমাত্র invoice)।\n- ব্যর্থতা পর্যবেক্ষণ এবং সিদ্ধান্ত নেওয়ার স্থান আছে: failed jobs দেখুন, একটি জব পুনরায় চালান, বা যখন আর চালানো উচিত নয় তখন সেটিকে dead চিহ্নিত করুন।\n- আপনার লিজ (লক) টাইমআউট কাজের জন্য যুক্তিযুক্ত। এটি সাধারণ রান থেকে লম্বা, কিন্তু এতটাই সংক্ষিপ্ত যে ক্র্যাশ করা worker ঘন্টার পর ঘন্টা ব্লক না করে।\n- রিট্রাই ব্যাকঅফ পূর্বানুমানযোগ্য। এটি পুনরাবৃত্ত ব্যর্থতাকে ধীর করে, এবং পেরলে থামে।\n\nএগুলো সত্য হলে, cron + database প্যাটার্ন সাধারণত বাস্তব ওয়ার্কলোডের জন্য পর্যাপ্ত স্থিতিশীল।\n\n### পরবর্তী ধাপ\n\nচেকলিস্ট ঠিকঠাক হলে, প্রতিদিনের অপারেশনের দিকে মনোযোগ দিন।\n\n- দুটি ছোট admin অ্যাকশন যোগ করুন: “now retry” ( এবং লক ক্লিয়ার) এবং “cancel” (ট্রানজিশন টার্মিনাল স্থিতিতে)। এগুলো ইনসিডেন্ট সময়ে সময় বাঁচায়।\n- worker প্রত্যেক জবের জন্য একটি লাইন লগ করা উচিত: job type, job id, attempt সংখ্যা, এবং ফল। বাড়তে থাকা ব্যর্থতা কনের ওপর অ্যালার্ট দিন।\n- বাস্তবসম্মত স্পাইকের সঙ্গে লোড টেস্ট করুন: একই মিনিটে অনেক জব শিডিউল করা। যদি জব claim করা ধীর হয়, সঠিক ইনডেক্স দিন (সাধারণত )।\n\nদ্রুত এমন একটি সেটআপ বানাতে, Koder.ai (koder.ai) আপনাকে schema থেকে একটি ডিপ্লয়ড Go + PostgreSQL অ্যাপে দ্রুত নিয়ে যেতে সাহায্য করতে পারে, যাতে আপনি লকিং, রিট্রাই, এবং idempotency নিয়মগুলোর উপর মনোযোগ দিতে পারেন।\n\nপরবর্তীতে যদি আপনি এই সেটআপ ছেড়ে আরও বড় সিস্টেমে যান, আপনি তখনও জব lifecycle সম্পর্কে স্পষ্ট ধারণা পাবেন, এবং একই ধারণা একটি পূর্ণ queue সিস্টেমে ভালোভাবে মানায়।