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

একটি ছোট Go সার্ভিসে, ব্যাকগ্রাউন্ড কাজ সাধারণত একটি সরল লক্ষ্য দিয়ে শুরু হয়: HTTP রেসপন্স দ্রুত ফেরত দিন, তারপর ধীর কাজগুলো পরে করুন। সেটা হতে পারে ইমেল পাঠানো, ইমেজ রিসাইজ করা, অন্য API-তে সিঙ্ক করা, সার্চ ইনডেক্স পুনর্নির্মাণ করা, বা রাত্রিবেলায় রিপোর্ট চালানো।
সমস্যা হল এগুলো বাস্তব প্রোডাকশন কাজ — শুধু যে রিকোয়েস্ট হ্যান্ডলিং-এ যে ধরণের নিরাপত্তা থাকে তা নেই। HTTP হ্যান্ডলার থেকে স্টার্ট করা একটি goroutine প্রথমে ঠিকঠাক লাগতে পারে, যতক্ষণ না কোনো ডিপ্লয় মাঝপথে ঘটে, কোনো আপস্ট্রিম API ধীর হয়ে পড়ে, বা একই রিকোয়েস্ট রি-ট্রাই হয়ে একই কাজ দুবার ট্রিগার করে।
প্রাথমিক যন্ত্রণা সম্মুখীন বিষয়গুলো ভবিষ্যদ্বাণীমূলক:
এখানেই একটি ছোট, স্পষ্ট প্যাটার্ন হিসেবে Go ওয়ার্কার পুল সাহায্য করে। এটা কনকারেন্সিকে একটি পছন্দ করে তোলে (N ওয়ার্কার), “পরে কর” কে স্পষ্ট জব টাইপ হিসেবে রূপ দেয়, এবং আপনাকে রিট্রাই, টাইমআউট এবং বাতিলকরণ হ্যান্ডল করার একটি কেন্দ্রীয় জায়গা দেয়।
উদাহরণ: একটি SaaS অ্যাপে ইনভয়েস পাঠাতে হবে। ব্যাচ ইম্পোর্টের পরে ৫০০টি সমান্তরাল পাঠা শুরু করতে চান না, এবং একই ইনভয়েস রি-সেন্ডও চান না কারণ কোনো রিকোয়েস্ট রি-ট্রাই হয়েছে। ওয়ার্কার পুল আপনাকে থ্রুপুট ক্যাপ করতে দেয় এবং “ইনভয়েস #123 পাঠান” কে ট্র্যাক করা কাজ হিসেবে বিবেচনা করতে দেয়।
একটি ওয়ার্কার পুল উপযুক্ত নয় যদি আপনাকে ডিউরেবল, ক্রস-প্রসেস গ্যারান্টি দরকার হয়। যদি কাজগুলি ক্র্যাশ থেকে বেঁচে থাকতে হয়, ভবিষ্যতের জন্য শিডিউল করা লাগবে, বা একাধিক সার্ভিসে প্রসেস করতে হবে, তাহলে সাধারণত আপনাকে একটি প্রকৃত কিউ ও পার্সিস্টেন্ট স্টোরেজ দরকার হবে।
একটি Go ওয়ার্কার পুল ইচ্ছাকৃতভাবে সরল: কাজকে একটি কিউতে রাখুন, একটি নির্দিষ্ট সংখ্যক ওয়ার্কার তা থেকে তুলি, এবং নিশ্চিত করুন পুরো সিস্টেমটি পরিষ্কারভাবে বন্ধ হতে পারে।
বেসিক টার্মগুলো:
অনেক ইন-প্রসেস ডিজাইনে, একটি Go channel কিউ হিসেবে ব্যবহৃত হয়। একটি বাফার্ড চ্যানেল নির্দিষ্ট সংখ্যক জব ধরে রাখতে পারে তা না হলে প্রযোজকরা ব্লক করে। সেই ব্লকিংই ব্যাকপ্রেশার, এবং এটা প্রায়ই আপনার সার্ভিসকে সীমাহীন কাজ গ্রহণ করে মেমরি শেষ হয়ে যাওয়া থেকে রক্ষা করে যখন ট্রাফিক স্পাইক হয়।
বাফার সাইজ সিস্টেমের অনুভূতি বদলে দেয়। ছোট বাফার চাপ দ্রুত দৃশ্যমান করে (কলাররা তাড়াতাড়ি অপেক্ষা করে)। বড় বাফার সংক্ষিপ্ত স্ফীতিকে স্মুথ করে কিন্তু বেশি লোড লুকিয়ে রাখতে পারে। সঠিক সংখ্যা নেই, কেবল এমন একটি সংখ্যা আছে যা আপনি কতটুকু অপেক্ষা সহ্য করতে পারেন তা মেলে।
আপনি নির্ধারণ করবেন পুল সাইজ স্থির হবে নাকি পরিবর্তনশীল। স্থির পুল বুঝতে সহজ এবং রিসোর্স ব্যবহার পূর্বানুমানযোগ্য রাখে। অটো-স্কেলিং ওয়ার্কার অব্যবহারে সহায়তা করতে পারে, তবে এতে আরো সিদ্ধান্ত আসে যা আপনাকে বজায় রাখতে হবে (কখন স্কেল করবেন, কতটুকু, এবং কখন কম করবেন)।
অবশেষে, ইন-প্রসেস পুলে “ack” সাধারণত মানে “ওয়ার্কার জব শেষ করেছে এবং কোন এরর ফেরত দেয়নি।” কোনো বাইরের ব্রোকার নেই ডেলিভারি নিশ্চিত করার জন্য, তাই আপনার কোডই নির্ধারণ করে কি “ডন” এবং কোনো জব ফেল হলে কি হবে।
একটি ওয়ার্কার পুল মেকানিক্যালি সরল: একটি নির্দিষ্ট সংখ্যক ওয়ার্কার চালান, তাদের জব খাওয়ান, এবং প্রসেস করুন। মূল্য হলো নিয়ন্ত্রণ: পূর্বানুমানীয় কনকারেন্সি, স্পষ্ট ব্যর্থতা পরিচালনা, এবং এমন একটি শাটডাউন পথ যা অর্ধেক করা কাজ পিছনে রেখে যায় না।
ছোট টিমকে শান্ত রাখতে তিনটি লক্ষ্য:\n
অধিকাংশ ব্যর্থতা সাধারণ, কিন্তু আপনি সেগুলোকে আলাদা ভাবে ট্রীট করতে চান:\n
বাতিলকরণ মানে এরর নয়। এটা একটি সিদ্ধান্ত: ব্যবহারকারী বাতিল করেছে, একটি ডিপ্লয় প্রসেস আপনার প্রক্রিয়াটি বদলে দিয়েছে, বা আপনার সার্ভিস শাটডাউন করছে। Go-তে বাতিলকরণকে কনটেক্সট বাতিলকরণ ব্যবহার করে প্রথম শ্রেণীর সিগন্যাল হিসাবে বিবেচনা করুন, এবং প্রতিটি জব শুরু করার আগে ও একাধিক স্থানে এক্সপেনসিভ কাজের মাঝে এটি চেক করতে নিশ্চিত করুন।
পরিষ্কার শাটডাউনই যেখানে অনেক পুল ব্যর্থ হয়। আগে থেকেই নির্ধারণ করুন আপনার জবের জন্য “সেফ” কি মানে: আপনি ইন-ফ্লাইট কাজ শেষ করবেন কি, না কি দ্রুত থেমে পরে পুনরায় চালাবেন? একটি বাস্তবসম্মত প্রবাহ:
যদি আপনি এগুলো আগে থেকেই সংজ্ঞায়িত করেন, তাহলে রিট্রাই, বাতিলকরণ, এবং শাটডাউন ছোট ও পূর্বানুমানযোগ্য থাকে, বড় কোনো হোমগ্রোন ফ্রেমওয়ার্কে পরিণত হয় না।
একটি ওয়ার্কার পুল হলো কিছু goroutine যা একটি চ্যানেল থেকে জব টানে এবং কাজ করে। গুরুত্বপূর্ণ অংশ হলো ভিত্তি নির্ভরযোগ্য করা: জব কেমন দেখতে লাগে, ওয়ার্কাররা কিভাবে থামে, এবং কিভাবে বুঝবেন সব কাজ শেষ হয়েছে।
একটি সরল Job টাইপ দিয়ে শুরু করুন। লগের জন্য ID দিন, একটি পে-লোড দিন (কি প্রসেস করতে হবে), একটি অ্যাটেম্পট কাউন্টার (রিট্রাইয়ের জন্য দরকারী), টাইমস্ট্যাম্প, এবং প্রতি-জব কনটেক্সট ডেটা রাখার জায়গা।
package jobs
import (
"context"
"sync"
"time"
)
type Job struct {
ID string
Payload any
Attempt int
Enqueued time.Time
Started time.Time
Ctx context.Context
Meta map[string]string
}
type Pool struct {
ctx context.Context
cancel context.CancelFunc
jobs chan Job
wg sync.WaitGroup
}
func New(size, queue int) *Pool {
ctx, cancel := context.WithCancel(context.Background())
p := \u00026Pool{ctx: ctx, cancel: cancel, jobs: make(chan Job, queue)}
for i := 0; i \u0003c size; i++ {
go p.worker(i)
}
return p
}
func (p *Pool) worker(_ int) {
for {
select {
case \u0003c-p.ctx.Done():
return
case job, ok := \u0003c-p.jobs:
if !ok {
return
}
p.wg.Add(1)
job.Started = time.Now()
_ = job // call your handler here
p.wg.Done()
}
}
}
// Submit blocks when the queue is full (backpressure).
func (p *Pool) Submit(job Job) error {
if job.Enqueued.IsZero() {
job.Enqueued = time.Now()
}
select {
case \u0003c-p.ctx.Done():
return context.Canceled
case p.jobs \u0003c- job:
return nil
}
}
func (p *Pool) Stop() { p.cancel() }
func (p *Pool) Wait() { p.wg.Wait() }
কিছু ব্যবহারিক পছন্দ যা আপনি তৎক্ষণাত করবেন:
Stop() এবং Wait() আলাদা রাখুন যাতে আপনি প্রথমে ইনটেক বন্ধ করে পরে ইন-ফ্লাইট কাজ শেষ হওয়া পর্যন্ত অপেক্ষা করতে পারেন।রিট্রাই দরকারি, কিন্তু একে নিয়েই পুল জটিল হয়ে পড়ে। লক্ষ্য সংকীর্ণ রাখুন: শুধুমাত্র সেই পরিস্থিতিগুলো পুনরায় চেষ্টা করুন যেখানে পুনরায় চেষ্টা করলে সফল হওয়ার বাস্তব সুযোগ আছে, এবং যেখানে না সেখানে দ্রুত থামুন।
শুরুতে নির্ধারণ করুন কী রিট্রাইযোগ্য। সাময়িক সমস্যা (নেটওয়ার্ক হিচ, টাইমআউট, “পরে চেষ্টা করুন” রেসপন্স) সাধারণত রিট্রাই করার যোগ্য। পার্মানেন্ট সমস্যা (খারাপ ইনপুট, অনুপস্থিত রেকর্ড, পারমিশন নেই) রিট্রাই করা ঠিক না।
একটি ছোট রিট্রাই পলিসি সাধারণত যথেষ্ট:
Retryable(err) হেল্পারের মাধ্যমে)।ব্যাকঅফ খুব জটিল হওয়া প্রয়োজন নেই। সাধারণ ফর্ম হলো: delay = min(base * 2^(attempt-1), max), তারপর জিটার যোগ করুন (±২০% র্যান্ডম)। জিটার গুরুত্বপূর্ণ কারণ না হলে বহু ওয়ার্কার একই সময়ে ব্যর্থ হয়ে একসাথে রিট্রাই করবে।
ডিলে কোথায় রাখবেন? ছোট সিস্টেমের জন্য, ওয়ার্কারের ভেতরেই স্লিপ করা ঠিক আছে, কিন্তু এটি একটি ওয়ার্কার স্লট আটকে রাখে। যদি রিট্রাই বিরল হয়, সেটা গ্রহণযোগ্য। যদি রিট্রাই সাধারণ বা ডিলে দীর্ঘ হয়, তাহলে জবটিকে পুনঃএনকিউ করে একটি “run after” টাইমস্ট্যাম্প দেওয়া বিবেচনা করুন যাতে ওয়ার্কাররা অন্য কাজ দিয়ে ব্যস্ত থাকে।
চূড়ান্ত ব্যর্থতায় স্পষ্ট হোন। ব্যর্থ জবটি (এবং শেষ এরর) স্টোর করুন পর্যালোচনার জন্য, পুনরায় চালানোর জন্য পর্যাপ্ত কনটেক্সট লগ করুন, বা একটি ডেড-লিস্টে ঠেলে দিন যা নিয়মিত পরীক্ষা করা হয়। নীরবভাবে জব ড্রপ করা এড়িয়ে চলুন। এমন একটি পুল যা ব্যর্থতাগুলো লুকায় সেটা আর পুল না থাকা থেকেই খারাপ।
ওয়ার্কার পুল তখনই নিরাপদ মনে হয় যখন আপনি তাদের থামাতে পারেন। সবচেয়ে সরল নিয়ম: প্রতিটি ব্লকিং স্তরে context.Context পাস করুন। এর মানে সাবমিশন, এক্সিকিউশন, এবং ক্লিনআপ—সব জায়গায় কনটেক্সট ব্যবহার করা।
একটি ব্যবহারিক সেটআপে দুটি টাইম লিমিট থাকে:
প্রতিটি জবকে ওয়ার্কারের কনটেক্সট থেকে ডেরাইভ করা নিজস্ব কনটেক্সট দিন। তারপর প্রতিটি ধীর কল (ডাটাবেস, HTTP, কিউ, ফাইল I/O) সেই কনটেক্সট ব্যবহার করবে যেন তা আগেভাগেই ফিরে আসতে পারে।
func worker(ctx context.Context, jobs \u0003c-chan Job) {
for {
select {
case \u0003c-ctx.Done():
return
case job, ok := \u0003c-jobs:
if !ok { return }
jobCtx, cancel := context.WithTimeout(ctx, job.Timeout)
_ = job.Run(jobCtx) // Run must respect jobCtx
cancel()
}
}
}
যদি Run আপনার DB বা কোনো API কল করে, ওই কলগুলিতে কনটেক্সট পাস করুন (উদাহরণ: QueryContext, NewRequestWithContext, বা ক্লায়েন্ট মেথডস যা কনটেক্সট নেয়)। যদি আপনি এক জায়গায় এটি উপেক্ষা করেন, বাতিলকরণ “সেরা প্রচেষ্টা” হয়ে যাবে এবং সাধারণত তখনই ব্যর্থ হয় যখন সবচেয়ে বেশি দরকার।
বাতিলকরণ জব মধ্যেকারেও ঘটতে পারে, তাই আংশিক কাজ স্বাভাবিক ধরে নিন। পুনরাবৃত্তি নিরাপদ করতে চেষ্টা করুন যাতে পুনরায় চালালে ডুপ্লিকেট তৈরি না হয়। সাধারণ উপায়গুলো: ইনসার্টের জন্য ইউনিক কী (অথবা আপসার্ট) ব্যবহার, প্রগ্রেস মার্কার লেখা (started/done), ফলাফল সংরক্ষণ করে তারপর এগোনো, এবং ধাপগুলোর মধ্যে ctx.Err() চেক করা।
শাটডাউনকে একটি ডেডলাইন হিসেবে দেখুন: নতুন জব গ্রহণ বন্ধ করুন, ওয়ার্কার কনটেক্সট বাতিল করুন, এবং শাটডাউন টাইমআউট পর্যন্ত শুধু ইন-ফ্লাইট জবগুলোর জন্য অপেক্ষা করুন।
একটি পরিষ্কার শাটডাউনের কাজ হলো: নতুন কাজ নেওয়া বন্ধ করা, ইন-ফ্লাইট কাজগুলোকে থামাতে বলা, এবং সিস্টেমকে অদ্ভুত অবস্থায় না রেখে এক্সিট করা।
সিগন্যাল দিয়ে শুরু করুন। বেশিরভাগ ডিপ্লয়মেন্টে আপনি লোকালভাবে SIGINT এবং প্রসেস ম্যানেজার বা কন্টেইনার রানটাইম থেকে SIGTERM পাবেন। একটি শাটডাউন কনটেক্সট ব্যবহার করুন যা সিগন্যাল আসলে বাতিল হয়, এবং সেটি আপনার পুল ও জব হ্যান্ডলারগুলোকে পাস করুন।
পরের ধাপ, নতুন জব গ্রহণ বন্ধ করুন। কাউকে এমন এক ফাংশনের পিছনে সাবমিশন রাখা উচিত যা শাটডাউন কনটেক্সট চেক করে বা একটি ক্লোজড ফ্ল্যাগ দেখিয়ে চ্যানেলে পাঠায় না — যাতে কোনো কলার চ্যানেলে পাঠাতে গিয়ে অনন্তকাল ব্লক না হয়।
তারপর কিউতে থাকা কাজগুলো কী হবে তা সিদ্ধান্ত নিন:
ড্রেইনিং পেমেন্ট বা ইমেলের মতো জিনিসের জন্য নিরাপদ। ড্রপ করা “আচ্ছা থাকলে” কাজগুলোর জন্য ঠিক আছে, যেমন কেশ পুনঃগণনা।
একটি বাস্তবসম্মত শাটডাউন সিকোয়েন্স:
ডেডলাইন গুরুত্বপূর্ণ। উদাহরণস্বরূপ, ইন-ফ্লাইট জবগুলোকে থামানোর জন্য ১০ সেকেন্ড দিন। এর পরও যেগুলো চলছে সেগুলো লগ করুন এবং বেরিয়ে পড়ুন। এটা ডিপ্লয়গুলো পূর্বানুমানযোগ্য রাখে এবং আটকে থাকা প্রসেস এড়ায়।
যখন একটি ওয়ার্কার পুল ভেঙে পড়ে, সাধারণত সেটা জোরে ফলপ্রসূ ব্যর্থতা দেখায় না। জবগুলো ধীর হয়, রিট্রাই জমে যায়, এবং কেউ রিপোর্ট করে “কিছুই হচ্ছে না।” লগিং এবং কিছু মৌলিক কাউন্টার সেটা পরিষ্কার গল্পে পরিণত করে।
প্রতিটি জবের জন্য একটি স্থিতিশীল ID রাখুন (অথবা সাবমিট হলে জেনারেট করুন) এবং প্রতিটি লগ লাইনে তা অন্তর্ভুক্ত করুন। লগ কনসিস্টেন্ট রাখুন: একটি লাইন জব শুরুতে, একটি লাইন শেষ হলে, এবং একটি লাইন ব্যর্থ হলে। যদি আপনি রিট্রাই করেন, অ্যাটেম্পট নম্বর এবং পরবর্তী ডিলে লগ করুন।
একটি সরল লগ ফরম্যাট:
মেট্রিক্সও মিনিমাল থাকতে পারে কিন্তু উপকারী। কিউ দৈর্ঘ্য, ইন-ফ্লাইট জব, মোট সাকসেস ও ফেলিওর, এবং জব লেটেন্সি (অন্তত গড় ও সর্বোচ্চ) ট্র্যাক করুন। যদি কিউ লেন্গথ বাড়তে থাকে এবং ইন-ফ্লাইট কনস্ট্যান্টভাবে ওয়ার্কার কাউন্টের সমান থাকে, তাহলে আপনি স্যাচুরেটেড। যদি সাবমিটাররা জব চ্যানেলে পাঠাতে গিয়ে ব্লক করে, তাহলে ব্যাকপ্রেশার কলারে পৌঁছেছে। এটা সব সময় খারাপ না, কিন্তু ইচ্ছাকৃত হওয়া উচিত।
“জব আটকে আছে” বললে দেখা দরকার প্রসেস এখনও জব নিচ্ছে কি না, কিউ লেন্গথ বাড়ছে কিনা, ওয়ার্কাররা জীবিত আছে কি না, এবং কোন জবগুলো সবচেয়ে বেশি সময় ধরে চলছে। দীর্ঘ রানটাইম সাধারণত নিয়ত টাইমআউট অনুপস্থিতি, ধীর ডিপেনডেন্সি, বা এমন একটি রিট্রাই লুপের ইঙ্গিত দেয় যা কখনই থামে না।
ভাবুন একটি ছোট SaaS-এ একটি অর্ডার PAID-এ বদলেছে। পেমেন্টের পরে আপনাকে ইনভয়েস PDF পাঠাতে হবে, কাস্টমারকে ইমেল পাঠাতে হবে, এবং ইনার্নাল টিমকে নোটিফাই করতে হবে। এই কাজগুলো ওয়েব রিকোয়েস্ট ব্লক করলে ঠিক হবে না। এটি ওয়ার্কার পুলের জন্য উপযুক্ত কারণ কাজগুলো বাস্তব, কিন্তু সিস্টেম এখনও ছোট।
জব পে-লোড মিনিমাল হতে পারে: বাকি ডেটা ডাটাবেস থেকে আনার জন্য যথেষ্ট। API হ্যান্ডলার অর্ডার আপডেটের একই ট্রানজ্যাকশনে একটি সারি লেখে যেমন jobs(status='queued', type='send_invoice', payload, attempts=0), তারপর একটি ব্যাকগ্রাউন্ড লুপ কিউড জবগুলোর জন্য পোল করে এবং সেগুলোকে ওয়ার্কার চ্যানেলে ঠেলে দেয়।
type SendInvoiceJob struct {
OrderID string
CustomerID string
Email string
}
যখন একটি ওয়ার্কার এটি নেয়, হ্যাপি পাথ সরল: অর্ডার লোড করুন, ইনভয়েস জেনারেট করুন, ইমেল প্রোভাইডার কল করুন, তারপর জবকে ডান হিসেবে মার্ক করুন।
রিট্রাই এখানে বাস্তবে আসে। যদি আপনার ইমেল প্রোভাইডারের সাময়িক আউটেজ হয়, আপনি চান না ১,০০০ জব চিরতরে ফেল হোক বা প্রতি সেকেন্ডে প্রোভাইডারকে হামার করা হোক। একটি ব্যবহারিক পদ্ধতি হলো:
আউটেজ চলাকালীন, জবগুলো queued থেকে in_progress তে যায়, তারপর আবার queued-তে ভবিষ্যৎ রান টাইম সহ ফিরে আসে। প্রোভাইডার ফিরে এলে, ওয়ার্কাররা স্বাভাবিকভাবে ব্যাকলগ ড্রেইন করে।
এখন ধরুন একটি ডিপ্লয়। আপনি SIGTERM পাঠালেন। প্রসেসটি নতুন কাজ নেবে না কিন্তু চলমান কাজগুলো শেষ করবে। পোলিং বন্ধ করুন, ওয়ার্কার চ্যানেলে ফিড বন্ধ করুন, এবং ওয়ার্কারদের জন্য একটি ডেডলাইনসহ অপেক্ষা করুন। যেসব জব শেষ হয় সেগুলো ডান হিসেবে মার্ক হবে। ডেডলাইন এলে এখনও চলমান থাকা জবগুলোকে আবার queued হিসেবে মার্ক করুন (অথবা একটি ওয়াচডগ দিয়ে in_progress রেখে দিন) যাতে নতুন ভার্সন শুরু হলে সেগুলো পুনরায় পিকআপ করা যায়।
ব্যাকগ্রাউন্ড প্রসেসিং-এ বেশিরভাগ বাগ জব লজিকেই নেই। এগুলো সমন্বয়জনিত ত্রুটি যা কেবল লোড বা শাটডাউনের সময় প্রকাশ পায়।
একটি ক্লাসিক ফাঁদ হলো একই চ্যানেল একাধিক জায়গা থেকে close করা। ফলাফল একটি panic যা পুনরুত্থাপন করা কঠিন। প্রতিটি চ্যানেলের জন্য এক মালিক নির্ধারণ করুন (সাধারণত প্রোডিউসার), এবং শুধুই সেই জায়গাই close(jobs) কল করুক।
রিট্রাই আরেকটি জায়গা যেখানে ভাল ইচ্ছা আউটেজ তৈরি করে। যদি আপনি সবকিছু রিট্রাই করেন, আপনি পার্মানেন্ট ফেলিওরও রিট্রাই করবেন। এটা সময় নষ্ট করে, লোড বাড়ায়, এবং একটি ছোট সমস্যা বড় ইন্সিডেন্টে পরিণত করতে পারে। এররগুলো শ্রেণীবদ্ধ করুন এবং রিট্রাই কপি সীমাবদ্ধ রাখুন।
ডুপ্লিকেট ঘটবেই এমনকি যত্নশীল ডিজাইনের সাথেও। ওয়ার্কার ক্র্যাশ হতে পারে মিদ-জব, একটি টাইমআউট কাজ শেষ হওয়ার পরে ফায়ার করতে পারে, বা ডিপ্লয়ের সময় পুনঃকিউ হতে পারে। যদি জব আইডেমপোটেন্ট না হয়, ডুপ্লিকেট বাস্তব ক্ষতি করে: দুটি ইনভয়েস, দুটি ওয়েলকাম ইমেল, দুটি রিফাণ্ড।
সবচেয়ে সাধারণ ভুলগুলো:
context.Context উপেক্ষা করা, ফলে শাটডাউন শুরু হলে কাজ চলতেই থাকে।অনবাউন্ডেড কিউ বিশেষভাবে ছলনায় ভরা। কাজের একটি স্পাইক RAM-এ চুপচাপ জমতে পারে। বাফারড চ্যানেল ব্যবহার করুন এবং নির্ধারণ করুন পূর্ণ হলে কী হবে: ব্লক, ড্রপ, বা এরর ফেরত।
প্রোডাকশনে ওয়ার্কার পুল পাঠানোর আগে, আপনি জব লাইফসাইকেলটি জিবর্ষণ করে বলতে পারেন। যদি কেউ জিজ্ঞাসা করে “এই জব এখন কোথায়?”, উত্তরটি অনুধাবনের বাইরে থাকা উচিৎ নয়।
একটি ব্যবহারিক প্রিফ্লাইট চেকলিস্ট:
workerCount) এবং পরিবর্তন করলে কোড রিরাইট করার দরকার নেই।রিলিজের আগে একটি বাস্তব অনুশীলন করুন: ১০০টি “সেন্ড রিসিট ইমেল” জব এনকিউ করুন, ২০টি জবকে ব্যর্থ করতে জোর করুন, তারপর রান মাঝপথে সার্ভিস রিস্টার্ট করুন। আপনি দেখতে পাবেন রিট্রাই প্রত্যাশিতভাবে আচরণ করে, ডুপ্লিকেট সাইড-ইফেক্ট নেই, এবং ক্যানসেলেশন ডেডলাইনে প্রকৃতভাবে কাজ থামায়।
যদি কোনো আইটেম ধূসর হয়, এখনই তা কঠোর করুন। ছোট সংস্কারগুলো পরে দিন বাঁচাতে পারে।
একটি সাদামাটা ইন-প্রসেস পুল প্রোডাক্ট তরুণ থাকাকালীন প্রায়ই যথেষ্ট। যদি আপনার জবগুলো “আচ্ছা থাকলে” ধরনের (ইমেল পাঠান, কেশ রিফ্রেশ, রিপোর্ট জেনারেট) এবং আপনি সেগুলো পুনরায় চালাতে পারেন, একটি ওয়ার্কার পুল সিস্টেমটিকে সহজে বোঝার যোগ্য রাখে।
এই চাপের জায়গাগুলো দেখুন:
যদি এগুলোর কোনোটি সত্য না, ভারি টুলগুলো মূল্য যোগ করার চেয়ে বেশি চলাচলের অংশ যোগ করতে পারে।
সেরা হেজ হলো একটি স্থিতিশীল জব ইন্টারফেস: একটি ছোট পে-লোড টাইপ, একটি ID, এবং একটি হ্যান্ডলার যা স্পষ্ট রেজাল্ট রিটার্ন করে। তারপর আপনি কিউ ব্যাকএন্ড পরিবর্তন করতে পারেন পরে (ইন-মেমরি চ্যানেল থেকে ডাটাবেস টেবিল, এবং তারপর একটি ডেডিকেটেড কিউ) ব্যবসায়িক কোড বদলানো ছাড়া।
একটি ব্যবহারিক মধ্যবর্তী ধাপ হলো একটি ছোট Go সার্ভিস যা PostgreSQL থেকে জব পড়ে, তাদের লক করে ক্লেইম করে, এবং স্ট্যাটাস আপডেট করে। আপনি ডিউরেবিলিটি ও বেসিক অডিটেবিলিটি পান একই ওয়ার্কার লজিক রাখা অবস্থায়।
যদি দ্রুত প্রোটোটাইপ করতে চান, Koder.ai (koder.ai) একটি চ্যাট প্রম্পট থেকে Go + PostgreSQL স্টার্টার জেনারেট করতে পারে, ব্যাকগ্রাউন্ড জব টেবিল এবং ওয়ার্কার লুপ সহ, এবং এর স্ন্যাপশট ও রোলব্যাক আপনাকে রিট্রাই ও শাটডাউন আচরণ টিউন করার সময় সহায়তা করতে পারে।