বাস্তবে Oracle DBMS_SCHEDULER: জব, ক্যালেন্ডার সিনট্যাক্স, চেইন ও মনিটরিং
প্রতিটি প্রোডাকশন ডেটাবেজ চলে শিডিউল করা কাজের ওপর - রাতের ব্যাচ, statistics gathering, আর্কাইভ ক্লিনআপ, ভোর ৬টায় ফাইল টেনে আনা ইন্টারফেস। Oracle-এ এই সবকিছুর পেছনের ইঞ্জিন হলো DBMS_SCHEDULER, আর একটি শান্ত অপারেশন আর একটি রহস্যময় অপারেশনের মধ্যে পার্থক্য সাধারণত এটাই - DBA শিডিউল করা জবগুলোকে সুশৃঙ্খলভাবে ব্যবস্থাপনা করেন, নাকি ছেড়ে-দিয়ে-ভুলে-যাওয়া স্ক্রিপ্ট হিসেবে দেখেন। এই গাইডে scheduler-কে ঠিক সেভাবেই দেখানো হয়েছে যেভাবে প্রোডাকশনে এটি আসলে ব্যবহৃত হয়: সঠিকভাবে জব তৈরি, মুখস্থ রাখার মতো ক্যালেন্ডার সিনট্যাক্স, ব্যবহারকারীদের আগেই ব্যর্থতা ধরে ফেলা মনিটরিং, বহু-ধাপের ব্যাচের জন্য chains, আর জব নীরবে বন্ধ হয়ে যাওয়ার চিরচেনা কারণগুলো।
মূল কথাগুলো
- DBMS_SCHEDULER অনেক আগেই পুরোনো DBMS_JOB-এর জায়গা নিয়েছে - সমৃদ্ধ শিডিউলিং, সত্যিকারের লগিং, chains, external স্ক্রিপ্ট, আর প্রতিটি জবে আলাদা নিয়ন্ত্রণ।
- ক্যালেন্ডার সিনট্যাক্স (FREQ=DAILY;BYHOUR=2;BYMINUTE=30) cron-এর কসরত ছাড়াই প্রায় যেকোনো ব্যবসায়িক শিডিউল সামলায় - আর EXCLUDE সামলায় ছুটির দিন।
- যে জব আপনি মনিটর করেন না, সে জব নীরবে ব্যর্থ হয়: DBA_SCHEDULER_JOB_RUN_DETAILS-এ প্রতিটি রানের স্ট্যাটাস, সময়কাল ও error ধরা থাকে।
- ব্যর্থতার নোটিফিকেশন (FAILED রানে ইমেইল) একবার বানিয়ে ফেলুন - ভবিষ্যতের প্রতিটি জব সেই অপারেশনাল নিরাপত্তা এমনিতেই পাবে।
- Chains বহু-ধাপের ব্যাচকে dependency-সহ সঠিকভাবে প্রকাশ করে - A সফল হলে তবেই B - ভঙ্গুর sleep-নির্ভর স্ক্রিপ্টের বদলে।
- জব 'রহস্যজনকভাবে' বন্ধ হলে: ENABLED অবস্থা, broken/failure কাউন্ট, JOB_QUEUE_PROCESSES, আর জবটি ভুল PDB-তে তৈরি হয়েছে কিনা - এগুলো দেখুন।
১. কেন DBMS_SCHEDULER (Cron নয়, DBMS_JOB-ও নয়)
ডেটাবেজের নিজস্ব scheduler কেন তার জায়গা অর্জন করেছে - তিনটি কারণ। প্রথমত, এটি ডেটাবেজের সঙ্গে চলে - জব ডেটাবেজের ভেতরে থাকে, তাই RAC-এ failover হয়, Data Guard switchover-এর সঙ্গে যায়, ব্যাকআপের সঙ্গে restore হয়; মৃত সার্ভারের cron এন্ট্রি এর কিছুই করে না। দ্বিতীয়ত, এটি ঠিকঠাক লগ রাখে - প্রতিটি রান, তার সময়কাল আর error text কোয়েরিযোগ্য ইতিহাস হিসেবে থাকে, ছড়িয়ে-ছিটিয়ে থাকা লগ ফাইল নয়। তৃতীয়ত, এটি ডেটাবেজকে বোঝে - জব সত্যিকারের প্রিভিলেজসহ স্কিমা মালিক হিসেবে চলে, maintenance window-র সঙ্গে বাঁধা যায়, আর একে অপরের সাফল্যের ওপর chain করা যায়।
প্রাচীন DBMS_JOB ইন্টারফেসটি টেকনিক্যালি এখনও একটি compatibility wrapper হিসেবে টিকে আছে, কিন্তু নতুন প্রতিটি জব হওয়া উচিত scheduler জব। আর OS-এর cron-এরও এখনও একটি ভূমিকা আছে - এমন কাজের জন্য যা ডেটাবেজ বন্ধ থাকা অবস্থায় চলতে হয় (যেমন startup স্ক্রিপ্ট নিজেই) - বাকি সবকিছুর জায়গা ভেতরেই।
২. সঠিকভাবে একটি জব তৈরি
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'NIGHTLY_SALES_ROLLUP',
job_type => 'STORED_PROCEDURE',
job_action => 'SALES_OWNER.PKG_ROLLUP.RUN_NIGHTLY',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=30',
enabled => TRUE,
comments => 'Nightly sales rollup - owner: Sales IT, escalation: Khan');
END;
/
পরে কাজে দেবে এমন অভ্যাস: comments-এ সবসময় মালিক ও escalation লিখে রাখুন (তিন বছর পর কেউ মনে রাখবে না NIGHTLY_SALES_ROLLUP কী, বা তা নিরাপদে disable করা যায় কিনা); জবের নাম দিন কাজ অনুযায়ী, টিকিট নম্বর অনুযায়ী নয়; আর anonymous PL/SQL ব্লকের বদলে job_type => 'STORED_PROCEDURE' বেছে নিন, কারণ একটি procedure ভার্সন করা যায়, টেস্ট করা যায়, grep করা যায়।
Stored procedure ছাড়াও job_type হতে পারে 'PLSQL_BLOCK', অথবা OS-স্তরের কাজের জন্য (ফাইল ট্রান্সফার, এক্সপোর্ট) 'EXTERNAL_SCRIPT'/'EXECUTABLE' - এভাবেই আপনি আপনার crontab-এর অর্ধেকটাকে এমন জায়গায় সরিয়ে আনেন যেখানে সত্যিকারের লগিং আছে।
৩. ক্যালেন্ডার সিনট্যাক্স: মুখস্থ রাখার মতো অংশটি
repeat_interval-এর ক্যালেন্ডার ভাষাটি সংক্ষিপ্ত, আর প্রায় প্রতিটি বাস্তব ব্যবসায়িক শিডিউল সামলাতে পারে:
-- every day at 02:30
FREQ=DAILY; BYHOUR=2; BYMINUTE=30
-- every 15 minutes, business hours, weekdays only
FREQ=MINUTELY; INTERVAL=15; BYHOUR=9,10,11,12,13,14,15,16,17; BYDAY=MON,TUE,WED,THU,FRI
-- last day of every month at 23:00
FREQ=MONTHLY; BYMONTHDAY=-1; BYHOUR=23
-- first Sunday of each quarter
FREQ=MONTHLY; BYMONTH=1,4,7,10; BYDAY=1 SUN
-- every Friday plus month-end
FREQ=DAILY; BYDAY=FRI; BYMONTHDAY=-1
জেনে রাখার মতো দুটি শক্তিশালী ফিচার। আপনি বিশ্বাস করার আগে শিডিউল টেস্ট করতে পারেন - DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING পরবর্তী রানের সময়গুলো হিসাব করে ফেরত দেয়, ফলে "মাসের শেষ কর্মদিবস" জাতীয় লজিক এক মাস অপেক্ষা না করেই যাচাই করা যায়। আর আপনি exclusion-সহ named schedule বানাতে পারেন - একবার একটি ছুটির শিডিউল তৈরি করুন, তারপর EXCLUDE=holiday_sched প্রতিটি জব আলাদাভাবে না ছুঁয়েই ব্যাচকে ঈদ ও অন্যান্য বন্ধের দিন থেকে দূরে রাখে।
৪. মনিটরিং: 'শিডিউল করা' আর 'ব্যবস্থাপনা করা'র মধ্যে পার্থক্য
scheduler নিখুঁতভাবে ইতিহাস রাখে; বেশিরভাগ জায়গায় শুধু কেউ তা দেখে না - যতক্ষণ না কিছু একটা মাসখানেক ধরে ভেঙে থাকে। দুটি ভিউ-ই কাজটা করে দেয়:
-- Current state of your jobs
SELECT job_name, enabled, state, last_start_date, next_run_date, failure_count
FROM dba_scheduler_jobs
WHERE owner = 'SALES_OWNER'
ORDER BY next_run_date;
-- Run history: status, duration, and the actual error text
SELECT job_name, status,
actual_start_date,
run_duration,
additional_info
FROM dba_scheduler_job_run_details
WHERE actual_start_date > SYSDATE - 7
AND status = 'FAILED'
ORDER BY actual_start_date DESC;
শুধু ব্যর্থতা নয়, run_duration-এর ট্রেন্ডও দেখুন - যে rollup গত বছর ১০ মিনিটে শেষ হতো আর এখন ৭০ মিনিট নেয়, সেটি সকালের ব্যবহারকারীদের সঙ্গে সংঘর্ষে যাওয়ার অনেক আগেই আপনাকে কিছু একটা বলছে (আর তখন সেটি হয়ে দাঁড়ায় একটি টিউনিং আলোচনা)।
সক্রিয় অ্যালার্টের জন্য পরিচ্ছন্ন প্যাটার্নটি হলো একটি ছোট watchdog: একটিমাত্র scheduler জব, যা গত এক ঘণ্টার FAILED রানের জন্য রান ডিটেইলস কোয়েরি করে এবং কিছু পেলে UTL_MAIL/UTL_SMTP দিয়ে ইমেইল পাঠায়। একবার বানিয়ে ফেলুন - ইনস্ট্যান্সের প্রতিটি জব, বর্তমান ও ভবিষ্যৎ, কাভারড। মনিটরড পরিবেশে একই কোয়েরি আপনার এন্টারপ্রাইজ মনিটরিংয়ে জুড়ে দিন।
৫. Chains: সুতো-টেপ ছাড়াই বহু-ধাপের ব্যাচ
বাস্তব ব্যাচ মানে ধারাবাহিকতা: load, তারপর validate, তারপর rollup, তারপর report - আর দ্বিতীয় ধাপ ব্যর্থ হলে তৃতীয়টি চলতেই পারবে না। এটিকে আন্দাজে ঠিক করা শুরু-সময়ে ফাঁক রাখা চারটি জব হিসেবে লেখা - সেই চিরচেনা ভঙ্গুর পদ্ধতি। Chains কাজটি সঠিকভাবে করে:
BEGIN
DBMS_SCHEDULER.CREATE_CHAIN(chain_name => 'NIGHT_BATCH');
DBMS_SCHEDULER.DEFINE_CHAIN_STEP('NIGHT_BATCH','LOAD', 'PRG_LOAD');
DBMS_SCHEDULER.DEFINE_CHAIN_STEP('NIGHT_BATCH','VALIDATE','PRG_VALIDATE');
DBMS_SCHEDULER.DEFINE_CHAIN_STEP('NIGHT_BATCH','ROLLUP', 'PRG_ROLLUP');
DBMS_SCHEDULER.DEFINE_CHAIN_RULE('NIGHT_BATCH','TRUE','START LOAD');
DBMS_SCHEDULER.DEFINE_CHAIN_RULE('NIGHT_BATCH','LOAD SUCCEEDED','START VALIDATE');
DBMS_SCHEDULER.DEFINE_CHAIN_RULE('NIGHT_BATCH','VALIDATE SUCCEEDED','START ROLLUP');
DBMS_SCHEDULER.DEFINE_CHAIN_RULE('NIGHT_BATCH','ROLLUP COMPLETED','END');
DBMS_SCHEDULER.ENABLE('NIGHT_BATCH');
END;
/
(প্রতিটি step একটি program অবজেক্টকে নির্দেশ করে যা CREATE_PROGRAM দিয়ে তৈরি; তারপর একটিমাত্র scheduler জব chain-টি চালায়।) লাভগুলো: আন্দাজে বাড়িয়ে ধরা সময়ের বদলে আগের ধাপ শেষ হওয়ামাত্র পরের ধাপ চলে, ব্যর্থ ধাপ downstream-এ আবর্জনা না পাঠিয়ে লাইনটাই থামিয়ে দেয়, আর DBA_SCHEDULER_RUNNING_CHAINS দেখায় আজ রাতের ব্যাচ এই মুহূর্তে ঠিক কোথায় - ফলে ভোর ৬টার "ব্যাচ কি শেষ হয়েছে?" প্রশ্নটি প্রত্নতাত্ত্বিক খোঁড়াখুঁড়ির বদলে একটি কোয়েরিতে নেমে আসে।
৬. Windows ও রিসোর্স নিয়ন্ত্রণ
Oracle-এর নিজস্ব স্বয়ংক্রিয় মেইনটেন্যান্স (statistics gathering, advisor-গুলো) চলে maintenance window-তে - আপনার ভারী জবগুলোও চলতে পারে। জবকে একটি window আর Resource Manager প্ল্যানের সঙ্গে জুড়ে দিলে ব্যাচ রাতে পুরো মেশিন পায়, কিন্তু সকাল পর্যন্ত গড়িয়ে গেলেও অনলাইন ব্যবহারকারীদের রিসোর্স-শূন্য করতে পারে না। ন্যূনতম হলেও নিজের window-গুলো চিনে রাখুন: DBA_SCHEDULER_WINDOWS দেখায় কখন সেগুলো খোলে, আর "সকাল ৮টার স্লোনেস রহস্য"-র অনেকগুলোই আসলে সীমা ছাড়িয়ে বেড়ে যাওয়া একটি maintenance window, কিংবা statistics gathering-এর সঙ্গে ধাক্কা খাওয়া একটি ব্যাচ chain।
৭. জব নীরবে বন্ধ হয়ে গেলে: চেকলিস্ট
চিরচেনা টিকিট: "রিপোর্টটি তিন সপ্তাহ ধরে পুরোনো ডেটা দেখাচ্ছে।" জবটি জোরে কোনো error দেয়নি - স্রেফ থেমে গেছে। ক্রমান্বয়ে দেখুন:
- এটি কি enabled?
DBA_SCHEDULER_JOBS.ENABLED- কোনো ইনসিডেন্টের সময় কেউ disable করেছিল, আর কখনও re-enable করেনি। আপনি যে comments লিখে রেখেছিলেন, তা এখন পরের মানুষটিকে বলে দেবে সেটি নিরাপদ ছিল কিনা। - ব্যর্থতার সীমা কি শেষ? বারবার ব্যর্থতার পর (ডিফল্ট ১৬,
max_failuresঅনুযায়ী) scheduler হাল ছেড়ে জবটিকে BROKEN চিহ্নিত করে। রান ডিটেইলস ভিউতে তিন সপ্তাহ আগের মূল error-টি পাওয়া যাবে। - জব কি আদৌ চলতে পারে?
JOB_QUEUE_PROCESSES=0- কখনও কখনও কোনো আপগ্রেড বা মাইগ্রেশন স্ক্রিপ্ট ফেলে যায় - ইনস্ট্যান্সের প্রতিটি জবকে নীরবে জমিয়ে দেয়। - সঠিক container তো? multitenant-এ ভুল PDB-তে সংযুক্ত অবস্থায় তৈরি করা জব সেখানেই চলে, আপনি যেখানে খুঁজছেন সেখানে নয় - এক আধুনিক ক্লাসিক। container-গুলো জুড়ে
CDB_SCHEDULER_JOBSদেখুন (দেখুন multitenant গাইড)। - টাইম জোনের চমক? timestamp literal দিয়ে তৈরি জব সেই session-এর টাইম জোন উত্তরাধিকার পায়; DST বা সার্ভার সরানোর পর "রাত ২টা" হয়তো আর আপনার রাত ২টা নয়। ব্যবসায়িকভাবে গুরুত্বপূর্ণ সবকিছুতে explicit টাইম জোনসহ named schedule ব্যবহার করুন।
৮. একটি বাস্তব ঘটনা: যে ইন্টারফেস এক মাস ধরে ভদ্রভাবে ব্যর্থ হচ্ছিল
এক ফার্মা ক্লায়েন্টের ইনভেন্টরি ইন্টারফেস - প্রতি ৩০ মিনিটে ERP ফাইল টেনে আনা একটি scheduler জব - ফাইল সার্ভারে পাসওয়ার্ড রোটেশনের পর এক সকালে বন্ধ হয়ে গেল। জবটি ব্যর্থ হলো, retry করল, আবার ব্যর্থ হলো - ষোলোবার - তারপর scheduler সেটিকে BROKEN চিহ্নিত করে চুপ হয়ে গেল, ঠিক যেমনটি ডিজাইন করা। কেউ ব্যর্থতার অ্যালার্ট বানায়নি, তাই ব্যবসা সমস্যাটি জানল কয়েক সপ্তাহ পর, মাস-শেষের স্টক গরমিল দিয়ে।
মেরামতের চেয়ে প্রতিরোধটাই ছিল আসল শিক্ষা। credential আমরা মিনিটের মধ্যে ঠিক করলাম; তারপর সেকশন ৪-এর watchdog প্যাটার্নটি বানালাম - একটি জব, DBA_SCHEDULER_JOB_RUN_DETAILS-এর ওপর একটি কোয়েরি, একটি ইমেইল - আর প্রতিটি ইন্টারফেস জবের স্বাস্থ্যকে সকালের চেকলিস্টে জুড়ে দিলাম। মোট তৈরির সময়: এক বিকেল। পরের credential রোটেশনে সেই একই ইন্টারফেস ভাঙল ঠিক ৩১ মিনিটের জন্য - কারণ প্রথম মিনিটেই সঠিক মানুষটি ইমেইল পেয়েছিলেন। এই একটি ঘটনাতেই পুরো লেখাটির দর্শন: শিডিউল করা সহজ; প্রোডাকশনের দরকার ব্যবস্থাপনা করা শিডিউলিং।
সাধারণ জিজ্ঞাসা (FAQ)
DBMS_JOB আর DBMS_SCHEDULER-এর মধ্যে পার্থক্য কী?
DBMS_JOB হলো পুরোনো (legacy) ইন্টারফেস - খুবই সীমিত ফিচার, শুধু backwards compatibility-র জন্য রাখা (আর 19c থেকে এটি এমনিতেই scheduler-এর ওপরেই তৈরি)। DBMS_SCHEDULER যোগ করে ক্যালেন্ডারভিত্তিক শিডিউল, error text-সহ পূর্ণাঙ্গ রান লগিং, dependency-সহ chains, external OS জব, windows ও resource management। নতুন সব কাজে DBMS_SCHEDULER-ই ব্যবহার করা উচিত।
scheduler জব কেন ব্যর্থ হলো তা কীভাবে দেখব?
DBA_SCHEDULER_JOB_RUN_DETAILS ভিউতে কোয়েরি করুন - প্রতিটি রানের স্ট্যাটাস, শুরুর সময়, সময়কাল আর ADDITIONAL_INFO কলামে error text রেকর্ড থাকে। জবের নাম আর STATUS='FAILED' দিয়ে ফিল্টার করুন। বর্তমানে চলমান জবের জন্য DBA_SCHEDULER_RUNNING_JOBS লাইভ অবস্থা দেখায়।
কোনো error ছাড়াই আমার scheduler জব কেন বন্ধ হয়ে গেল?
সাধারণ কারণগুলো: জবটি disable করা হয়েছিল আর কখনও re-enable করা হয়নি; বারবার error-এর পর এটি MAX_FAILURES (ডিফল্ট ১৬) ছাড়িয়ে broken হিসেবে চিহ্নিত হয়েছে; JOB_QUEUE_PROCESSES শূন্য (0) করা ছিল, যা সব জব জমিয়ে দেয়; অথবা multitenant পরিবেশে জবটি আপনি যে PDB-তে খুঁজছেন তার বদলে অন্য PDB-তে আছে। রান-ডিটেইলস হিস্টরিতে সাধারণত মূল error-টি পাওয়া যায়।
মাসের শেষ দিনে চলার জন্য জব কীভাবে শিডিউল করব?
ক্যালেন্ডার সিনট্যাক্সে negative month day ব্যবহার করুন: FREQ=MONTHLY; BYMONTHDAY=-1; BYHOUR=23 প্রতি মাসের শেষ দিন রাত ২৩:০০-এ চলে। যেকোনো ক্যালেন্ডার স্ট্রিং DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING দিয়ে যাচাই করা যায় - এটি হিসাব করা পরবর্তী রানের সময়গুলো আগেই দেখিয়ে দেয়।
DBMS_SCHEDULER কি অপারেটিং সিস্টেমের স্ক্রিপ্ট চালাতে পারে?
হ্যাঁ - EXECUTABLE ও EXTERNAL_SCRIPT জব টাইপ দিয়ে OS প্রোগ্রাম ও shell স্ক্রিপ্ট চালানো যায়, আর তার আউটপুট জব রান ডিটেইলসে ধরা থাকে। Credentials অবজেক্ট নিয়ন্ত্রণ করে স্ক্রিপ্টটি কোন OS ইউজার হিসেবে চলবে। এতে ফাইল ট্রান্সফার ও OS মেইনটেন্যান্সের কাজ cron থেকে সরিয়ে এমন জায়গায় আনা যায় যেখানে সত্যিকারের লগিং, retry আর dependency আছে।
⏰ ব্যাচ জব কি আপনাকে চালাচ্ছে, আপনি নন?
আমি ব্যবস্থাপিত শিডিউলিং ডিজাইন করি - chains, মনিটরিং, ব্যর্থতার অ্যালার্ট আর পরিচ্ছন্ন ব্যাচ window - যাতে আপনার রাতের কাজ নিজে নিজেই চলে, আর না পারলে আপনাকে জানায়। বাংলাদেশ ও বিশ্বজুড়ে।
তথ্যসূত্র ও আরও পড়ুন
- 📄 Oracle Database Administrator's Guide (19c) - Scheduling Jobs with Oracle Scheduler
- 📄 Oracle PL/SQL Packages Reference - DBMS_SCHEDULER
এই লেখার পদ্ধতি ও কেস স্টাডিগুলো ম্যানুফ্যাকচারিং, ব্যাংকিং ও ফার্মা পরিবেশে ১৮+ বছরের Oracle প্রোডাকশন ডেটাবেজ অ্যাডমিনিস্ট্রেশনের অভিজ্ঞতার ভিত্তিতে লেখা।
