কম্পিউটার টিউটোরিয়াল

ডুপ্লিকেট সাইডকিউ জব প্রতিরোধ: ৩টি প্রমাণিত কৌশল

সম্ভাবনা বেশি, আপনি যদি রুবি কোড লিখেন, তবে ব্যাকগ্রাউন্ড প্রসেসিং হ্যান্ডেল করতে Sidekiq ব্যবহার করছেন। আপনি যদি ActiveJob বা অন্য কোনো ব্যাকগ্রাউন্ড থেকে আসেন, তবুও 지켜 দেখুন, এখানে আলোচনা করা কিছু টিপস সেখানে ও প্রয়োগ করা যায়।

ফোকসরা (Sidekiq) ব্যাকগ্রাউন্ড জব বিভিন্ন কেসে ব্যবহার করে। কেউ নম্বর কাটা-छাটা করে, কেউ ইউজারদের স্বাগত ইমেইল পাঠায়, আর কেউ ডেটা সিঙ্ক শিডিউল করে। আপনার কেস যেকোনো হোক, আপনি 결과স্বরূপ ডুপ্লিকেট জব এড়িয়ে চলার রিকোয়ারমেন্টে পড়তে পারেন। ডুপ্লিকেট জব বলতে আমি বুঝি দুটি জব যা একই কাজ করে। এটাকে একটু গভীরে যান।

কেন জব ডি-ডুপ্লিকেট করবেন?

একটি সিনেরিও কল্পনা করুন যেখানে আপনার জবটি নিচের মতে:

class BookSalesWorker include Sidekiq::Worker def perform(book_id) crunch_some_numbers(book_id) upload_to_s3 end ... end

BookSalesWorker সবসময় একই কাজ করে — book_id বেসড বই ডিবি থেকে কোয়েরি করে এবং লেটেস্ট সেলস ডেটা ফেচ করে কিছু নম্বর ক্যালকুলেট করে। তারপর সেগুলো একটি স্টোরেজ সার্ভিসে আপলোড করে। মনে রাখবেন, আপনার ওয়েবসাইটে প্রতি বই বিক্রি হলে, এই জবটি এনকিউ হবে।

এখন, যদি আপনি একসাথে ১০০টি সেল পান? তাহলে এই জবের ১০০টি কপি থাকবে যা একই কাজ করছে। হয়তো আপনি সেটা মেনে নিতে পারেন। আপনি S3 রাইটের জন্য তত চিন্তা করেন না, এবং আপনার কিউগুলো তত কংজেস্টেড নয়, তাই লোড হ্যান্ডেল করতে পারেন। কিন্তু, "এটা স্কেল করে?"™️

অবশ্যই না। আপনি যদি আরও বইয়ের জন্য বেশি সেল পাওয়া শুরু করেন, আপনার কিউ দ্রুত অপ্রয়োজনীয় কাজের সাথে জমা হয়ে যাবে। আপনার কাছে একটি বইয়ের জন্য ১০০টি একই জব থাকে, এবং ১০টি বই প্যারালেলে বিক্রি হচ্ছে, তবে আপনি এখন আপনার কিউতে ১০০০টি জব দিয়ে আছেন, যেখানে বাস্তবে আপনি শুধু ১০টি জব (প্রতি বইয়ের জন্য একটা) দিয়ে কাজ चला পারতেন।

এখন, চলুন কয়েকটি অপশন দেখি কীভাবে আপনি ডুপ্লিকেট জব কিউতে জমা হয়ে যাওয়া থেকে রক্ষা করতে পারেন।

১. DIY পদ্ধতি (খুদের করুন)

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

১.১ वन ফ্ল্যাগ অ্যাপ্রোচ

আপনি একটি ফ্ল্যাগ যোগ করতে পারেন যা ডিসাইড করবে জব এনকিউ করতে হবে কিনা। কেউ তাদের বই টেবিলে sales_enqueued_at যোগ করতে পারে এবং সেটা মেইনটেইন করতে পারে। উদাহরণস্বরূপ:

module BookSalesService def schedule_with_one_flag(book) # চেক করুন জবটি ১০ মিনিট আগে এনকিউ হয়েছিল কিনা if book.sales_enqueued_at < 10.minutes.ago book.update(sales_enqueued_at: Time.current) BookSalesWorker.perform_async(book.id) end end end

এর মানে হলো: লাস্ট জব এনকিউ হওয়ার পর ১০ মিনিট পাড়া পর্যন্ত কোনো নতুন জব এনকিউ হবে না। ১০ মিনিট পাড়ার পর, আমরা sales_enqueued_at আপডেট করি এবং একটি নতুন জব এনকিউ করি।

আপনি আরেকটা জিনিস করতে পারেন: একটি বুলিয়ান ফ্ল্যাগ সেট করুন, যেমন crunching_sales। প্রথম জব এনকিউ করার আগে crunching_sales কে true সেট করুন। তারপর, জব কমপ্লিট হলে false সেট করুন। অন্যান্য সব জব যারা শিডিউল হতে চায়, সেগুলো রিজেক্ট হবে যতক্ষণ না crunching_sales false হয়।

আপনি এই অ্যাপ্রোচটি আমার তৈরি করা উদাহরণ রিপোতে ট্রাই করতে পারেন।

১.২ টু ফ্ল্যাগ অ্যাপ্রোচ

যদি "একটি জবকে ১০ মিনিটের জন্য লক করা" খুব ভয়ংকর লাগে, কিন্তু আপনি এখনও এক্সট্রা ফ্ল্যাগ কোডে রাখতে পারেন, তবে পরের সাজেশন আপনার কাজে লাগতে পারে।

আপনি বিদ্যমান sales_enqueued_at-এর পাশে আরেকটি ফ্ল্যাগ যোগ করতে পারেন — sales_calculated_at। তাহলে আমাদের কোড দেখাবে এমন:

module BookSalesService def schedule_with_two_flags(book) # চেক করুন সেলস এখনো ক্যালকুলেট হচ্ছে কিনা if book.sales_enqueued_at <= book.sales_calculated_at book.update(sales_enqueued_at: Time.current) BookSalesWorker.perform_async(book.id) end end end class BookSalesWorker include Sidekiq::Worker def perform(book_id) crunch_some_numbers(book_id) upload_to_s3 # নতুন যোগ করা book.update(sales_calculated_at: Time.current) end ... end

ট্রাই আউট করতে, উদাহরণ রিপোর ইনস্ট্রাকশন চেক করুন।

এখন আমরা যে সময়ের অংশটি কন্ট্রোল করছি সেটা হলো: জব এনকিউ হওয়া থেকে ফিনিশ হওয়া পর্যন্ত। এই সময়ের অংশে কোনো জব এনকিউ হতে পারবে না। জব চলাকালীন, sales_enqueued_at sales_calculated_at-এর চেয়ে বড় হবে। জব ফিনিশ হলে, sales_calculated_at আরও সাম্প্রতিক হবে (বড়) sales_enqueued_at-এর চেয়ে এবং একটি নতুন জব এনকিউ হবে।

দুই ফ্ল্যাগ ব্যবহার করা ইন্টারেস্টিং হতে পারে, যাতে আপনি UI-তে দেখাতে পারেন সেলস নম্বরগুলো শেষ কখন আপডেট হয়েছিল। তাহলে সেগুলো পড়ে যারা তাদের কত সাম্প্রতিক ডেটা সেটা বুঝতে পারবেন। উইন-উইন সিচুয়েশন।

ফ্ল্যাগ সারাংশ

আবশ্যকতার সময়ে এই ধরণের সমাধান তৈরি করা লোভনীয় হতে পারে, কিন্তু আমার মতে এগুলো একটু clumsy (অনুপयुक्त/বেরান) লাগে, এবং কিছু ওভারহেড যোগ করে। আমি রিকমেন্ড করব এই ব্যবহার করতে যদি আপনার ইউজ কেস সিম্পল হয়, কিন্তু যতক্ষণ না এটি জটিল বা পর্যাপ্ত না প্রমাণ হয়, আমি আপনাকে অন্য অপশনগুলো ট্রাই করতে উত্সাহিত করব।

ফ্ল্যাগ অ্যাপ্রোচের একটি বড় কন: আপনি সেই ১০ মিনিটের মধ্যে এনকিউ হয়ে যাওয়া চেষ্টা করা সব জব হারিয়ে ফেলবেন। একটি বড় প্রো: আপনি কোনো ডিপেনডেন্সি আনছেন না, এবং কিউতে জব সংখ্যা খুব দ্রুত কমিয়ে দিচ্ছেন।

১.৩ কিউ ট্রাভার্স করা

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

module BookSalesService def schedule_unique_across_queue(book) queue = Sidekiq::Queue.new('default') queue.each do |job| return if job.klass == BookSalesWorker.to_s && job.args == [book.id] end BookSalesWorker.perform_async(book.id) end end class BookSalesWorker include Sidekiq::Worker def perform(book_id) crunch_some_numbers(book_id) upload_to_s3 end ... end

উপরের উদাহরণে আমরা চেক করছি 'default' কিউতে BookSalesWorker ক্লাস নামের জব আছে কিনা। আমরা চেক করছি জব আর্গুমেন্টগুলো বই আইডির সাথে মেলে কিনা। যদি BookSalesWorker জব সেম বই আইডির সাথে কিউতে থাকে, তবে আপনি vroeg রিটার্ন করবেন এবং অন্য একটি শিডিউল করবেন না।

নোট করুন: যদি আপনি খুব দ্রুত জব শিডিউল করেন, তবে কিছু জব এনকিউ হতে পারে কারণ কিউ খালি থাকে। এক্স্যাক্ট একই জিনিস আমার সাথে ঘটেছিল যখন লোকালি টেস্ট করলাম:

100.times { BookSalesService.schedule_unique_across_queue(book) }

আপনি উদাহরণ রিপোতে ট্রাই আউট করতে পারেন।

এই অ্যাপ্রোচের ভালো দিক: আপনি সকল কিউ ট্রাভার্স করতে পারেন এক্সিস্টিং জব খুঁজে বের করার জন্য যদি প্রয়োজন হয়। কন: আপনার কিউ খালি থাকলে এবং আপনি একসাথে অনেক জব শিডিউল করলে আপনার কাছে এখনও ডুপ্লিকেট জব থাকতে পারে। এছাড়া, আপনি পোটেনশিয়ালি কিউর সকল জব ট্রাভার্স করছেন এক জব শিডিউল করার আগে, তাই আপনার কিউ বড় হলে সেটা কস্টলি হতে পারে।

২. সাইডকিউ এন্টারপ্রাইজে আপগ্রেড

আপনার বা আপনার অর্গানাইজেশনের কাছে যদি কিছু টাকা পড়ে থাকে, তবে আপনি Sidekiq-এর এন্টারপ্রাইজ ভার্সনে আপগ্রেড করতে পারেন। এটা মাসিক $১৭৯ থেকে শুরু হয়, এবং এতে একটি কুল ফিচার রয়েছে ডুপ্লিকেট জব এড়াতে। দুর্ভাগ্যক্রমে, আমার কাছে Sidekiq Enterprise নেই, কিন্তু আমি মনে করি তাদের ডকুমেন্টেশন যথেষ্ট। আপনি সহজে ইউনিক (নন-ডুপ্লিকেটেড) জব পেতে পারেন নিচের কোড দিয়ে:

class BookSalesWorker include Sidekiq::Worker sidekiq_options unique_for: 10.minutes def perform(book_id) crunch_some_numbers(book_id) upload_to_s3 end ... end

এতটুকুই। আপনার কাছে 'One Flag Approach' সেকশনে আমরা যা বর্ণনা করলাম তার সিমিলার একটি জব ইমপ্লিমেন্টেশন। জবটি ১০ মিনিটের জন্য ইউনিক থাকবে, অর্থাৎ সেই সময়ের মধ্যে সেম আর্গুমেন্টসের সাথে অন্য কোনো জব শিডিউল করা যাবে না।

খুব কুল वन-লাইনার, ঠিক না? যদি আপনার কাছে এন্টারপ্রাইজ সাইডকিউ থাকে এবং আপনি এখন এই ফিচার সম্পর্কে জানলেন, তবে আমি সত্যিকারের খুশি যে আমি সাহায্য করলাম। আমাদের অধিকাংশই এটা ব্যবহার করব না, তাই চলুন পরবর্তী সমাধানের দিকে যাই।

৩. sidekiq-unique-jobs রক্ষা করবে

হ্যাঁ, আমি জানি আমরা একটি জেম বোলব। এবং হ্যাঁ, এতে কিছু Lua ফাইল রয়েছে যা কয়েকজনের মানসিকতা বIgাড়াতে পারে। কিন্তু ধৈর্য ধারণ করুন, এটি একটি খুব সুন্দর ডিল যা আপনি পাচ্ছেন। sidekiq-unique-jobs জেম আসে অনেক লকিং এবং অন্যান্য কনফিগারেশন অপশনসহ — সম্ভবত আপনার প্রয়োজনের চেয়ে বেশি।

দ্রুত শুরু করতে, আপনার Gemfile-এ sidekiq-unique-jobs জেম যোগ করুন, bundle করুন, এবং আপনার ওয়ার্কারকে নিচের মত কনফিগার করুন:

class UniqueBookSalesWorker include Sidekiq::Worker sidekiq_options lock: :until_executed, on_conflict: :reject def perform(book_id) book = Book.find(book_id) logger.info "I am a Sidekiq Book Sales worker - I started" sleep 2 logger.info "I am a Sidekiq Book Sales worker - I finished" book.update(sales_calculated_at: Time.current) book.update(crunching_sales: false) end end

অপশনগুলো অনেক, কিন্তু আমি সরল করে এইটা ব্যবহার করলাম:

sidekiq_options lock: :until_executed, on_conflict: :reject

lock: :until_executed প্রথম UniqueBookSalesWorker জবটি এক্সিকিউট হওয়া পর্যন্ত লক করে রাখবে। on_conflict: :reject দিয়ে আমরা বলছি: আমরা চাই অন্যান্য সব জব যারা এক্সিকিউট হতে চাইছে সেগুলো রিজেক্ট হয়ে ডেড কিউতে চলে যাবে। এখানে আমরা যা অর্জন করলাম সেটা আমাদের DIY উদাহরণগুলোর সাথে সিমিলার।

এই DIY উদাহরণগুলোর চেয়ে একটু উন্নতি: আমাদের একটি ধরনের লগ আছে কী ঘটেছে। সেটা কেমন দেখে, বুঝতে নিচের কোড ট্রাই করুন:

5.times { UniqueBookSalesWorker.perform_async(Book.last.id) }

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

লকিং এবং কনফ্লিক্ট রেজোলভেশনের জন্য অনেক অপশন রয়েছে। আপনার স্পেসিফিক ইউজ কেসের জন্য জেমের ডকুমেন্টেশন কনসাল্ট করার পরামর্শ দিই।

মহৎ অন্তর্দৃষ্টি

এই জেমের চমৎকার দিক: আপনি লকগুলো এবং কিউতে কী ঘটল তার ইতিহাস দেখতে পারেন। আপনার config/routes.rb-এ শুধু নিচের লাইনগুলো যোগ করুন:

# config/routes.rb require 'sidekiq_unique_jobs/web' Rails.application.routes.draw do mount Sidekiq::Web, at: '/sidekiq' end 

এতে আরিজিনাল Sidekiq ক্লায়েন্ট ইনক্লুড হবে, কিন্তু একসাথে আপনাকে দুটি এক্সট্রা পেজ দেবে — একটি জব লকসের জন্য এবং অন্যটি চেঞ্জলগের জন্য। এটা দেখতে এমন:

নোট করুন কীভাবে আমাদের দুটি নতুন পেজ আছে, "Locks" এবং "Changelogs"। খুব কুল ফিচার।

আপনি উদাহরণ প্রজেক্টে এই সব ট্রাই আউট করতে পারেন যেখানে জেম ইনস্টলড এবং রেডি-টু-গো।

কেন Lua?

প্রথমে, আমি জেমের লেখক নই, তাই শুধু অনুমান করছি। প্রথমবার জেমটি দেখে আমি চিন্তা করলাম: রুবি জেমের ভিতরে কেন Lua? প্রথম নজরে এটি অদ্ভুত লাগতে পারে, কিন্তু Redis Lua স্ক্রিপ্ট রান করতে সাপোর্ট করে। আমি অনুমান করি জেমের লেখকের এটা মস্তিষ্কে ছিল এবং তারা Lua-তে আরও চতুর লজিক করতে চেয়েছিলেন।

আপনি যদি জেমের রিপোতে Lua ফাইলগুলো দেখেন, সেগুলো তত জটিল নয়। সকল Lua স্ক্রিপ্ট পরে রুবি কোডে SidekiqUniqueJobs::Script::Caller-এ কল করা হয়। সোর্স কোড একবার দেখুন, পড়ে বুঝতে ইন্টারেস্টিং যে কীভাবে জিনিসগুলো কাজ করে।

বিকল্প জেম

আপনি যদি ব্যাপকভাবে ActiveJob ব্যবহার করেন, তবে active-job-uniqueness জেম ট্রাই করতে পারেন। আইডিয়া সিমিলার, কিন্তু কাস্টম Lua স্ক্রিপ্টের বদলে এটি [Redlock] ব্যবহার করে রেডিসে আইটেম লক করতে।

এই জেম দিয়ে ইউনিক জব পাওয়ার জন্য, আপনি এমন একটি জব কল্পনা করতে পারেন:

class BookSalesJob < ActiveJob::Base unique :until_executed def perform ... end end

সিনট্যাক্স কম verbose (বিস্তারিত) কিন্তু sidekiq-unique-jobs জেমের সাথে খুব সিমিলার। আপনি যদি বেশি নির্ভর করেন ActiveJob-এর ওপর, তবে এটি আপনার কেস সমাধান করতে পারে।

চূড়ান্ত চিন্তা

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

এখানে সব কোড স্নিপেটসহ উদাহরণ প্রজেক্ট।

পরবর্তীতে দেখা হবে, চিয়ার্স।

P.S. Ruby Magic পোস্টগুলো প্রেস থেকে বের होतेই পেতে চাইলে, আমাদের Ruby Magic নিউজলেটার সাবস্ক্রাইব করুন এবং কখনো একক পোস্ট মিস করবেন না!

  1. উইন্ডোজ ১০-এ আটকে যাওয়া প্রিন্ট জব মুছে ফেলার ৬টি কার্যকর পদ্ধতি

  2. এক্সেলে ডুপ্লিকেট সারিগুলোকে কলামে রূপান্তর: ৪টি প্রমাণিত পদ্ধতি

  3. আইফোনে ডুপ্লিকেট ফটো মুছে ফেলার ৩টি প্রমাণিত উপায় (২০২৪ গাইড)

  4. উইন্ডোজ ১০-এ কীবোর্ড ল্যাঙ্গুয়েজ পরিবর্তন: ৩টি সহজ পদ্ধতি