নিম্নলিখিত অনুমানমূলক দৃশ্যকল্পটি কল্পনা করুন:একটি ভাড়া সম্পত্তি ব্যবস্থাপনা সিস্টেমে, কর্মচারী A রেন্টাল এক্স-এর জন্য যোগাযোগের তথ্য সম্পাদনা শুরু করে, কিছু অতিরিক্ত ফোন নম্বর যোগ করে। প্রায় একই সময়ে, কর্মচারী B ঠিক সেই Rental X-এর যোগাযোগের তথ্যে একটি টাইপো লক্ষ্য করে এবং একটি আপডেট করে। কয়েক মিনিট পরে, কর্মচারী A নতুন ফোন নম্বরগুলির সাথে ভাড়া X-এর যোগাযোগের তথ্য আপডেট করে, এবং ... টাইপো সংশোধন করার আপডেটটি এখন চলে গেছে!
যে স্পষ্টভাবে মহান না! এবং এটি একটি চমত্কার তুচ্ছ দৃশ্যকল্প. একটি আর্থিক ব্যবস্থায় ঘটছে অনুরূপ দ্বন্দ্ব কল্পনা করুন!
আমরা কি ভবিষ্যতে এই ধরনের পরিস্থিতি এড়াতে পারি? সৌভাগ্যবশত, উত্তর হ্যাঁ! এই ধরনের সমস্যা প্রতিরোধ করার জন্য আমাদের একযোগে সুরক্ষা এবং লকিং - বিশেষত, আশাবাদী লকিং - প্রয়োজন৷
আসুন Rails REST API-এ আশাবাদী লকিং অন্বেষণ করি।
'হারানো আপডেট' এবং আশাবাদী লকিং বনাম হতাশাবাদী লকিং
আমরা এইমাত্র যে পরিস্থিতির মধ্য দিয়ে চলেছি তা হল এক ধরনের 'লস্ট আপডেট'। যখন দুটি সমসাময়িক লেনদেন একই সারির একই কলাম আপডেট করে, তখন দ্বিতীয়টি প্রথমটির পরিবর্তনগুলিকে ওভাররাইড করবে, মূলত যেন প্রথম লেনদেন কখনও হয়নি৷
সাধারণত, এই সমস্যার সমাধান করা যেতে পারে:
- একটি সঠিক লেনদেন বিচ্ছিন্নতা স্তর সেট করা, যা ডাটাবেস স্তরে সমস্যা পরিচালনা করে৷
- হতাশাবাদী লকিং — একই সারি আপডেট করা থেকে সমসাময়িক লেনদেন প্রতিরোধ করা। দ্বিতীয় লেনদেনটি ডেটা পড়ার আগে প্রথম লেনদেন শেষ হওয়ার জন্য অপেক্ষা করে। এখানে দুর্দান্ত সুবিধা হল যে এটি পুরানো ডেটাতে কাজ করা অসম্ভব। যদিও প্রধান অসুবিধা হল, এটি একটি প্রদত্ত সারি থেকে ডেটা পড়াও ব্লক করে।
- অপটিমিস্টিক লকিং — প্রদত্ত সারিটির পরিবর্তন বন্ধ করে দেয় যদি পরিবর্তনের সময় এটির অবস্থা পড়ার সময় থেকে আলাদা হয়।
আমাদের সমস্যা সমসাময়িক ডাটাবেস লেনদেন নিয়ে নয় (আরও ব্যবসায়িক লেনদেনের মতো) — তাই প্রথম সমাধানটি সত্যিই প্রযোজ্য নয়। এর মানে আমাদের কাছে হতাশাবাদী লকিং এবং আশাবাদী লকিং বাকি আছে৷
হতাশাবাদী লকিং আমাদের অনুমানমূলক পরিস্থিতিতে প্রথম স্থানে হারিয়ে যাওয়া আপডেটটিকে ঘটতে বাধা দেবে। যাইহোক, এটি ব্যবহারকারীদের জন্য জীবনকে কঠিন করে তুলবে যদি এটি খুব দীর্ঘ সময়ের জন্য ডেটা অ্যাক্সেস ব্লক করে দেয় (ধারণা করুন এটি 30 মিনিট বা তার বেশি সময় ধরে কিছু ক্ষেত্র পড়া এবং সম্পাদনা করছে)।
আশাবাদী লকিং অনেক কম সীমাবদ্ধ হবে, কারণ এটি একাধিক ব্যবহারকারীকে ডেটা অ্যাক্সেস করার অনুমতি দেবে। যাইহোক, যদি একাধিক ব্যবহারকারী একসাথে ডেটা সম্পাদনা শুরু করে, শুধুমাত্র একজনই অপারেশনটি সম্পাদন করতে পারে। বাকিরা একটি ত্রুটি দেখতে পাবে যে তারা পুরানো ডেটাতে কাজ করেছে এবং পুনরায় চেষ্টা করতে হবে। আদর্শ নয়, কিন্তু সঠিক UX সহ, এটি অগত্যা ততটা বেদনাদায়ক নাও হতে পারে।
আসুন দেখি কিভাবে আমরা একটি অনুমানমূলক Rails REST API তে আশাবাদী লকিং বাস্তবায়ন করতে পারি।
REST API-এ আশাবাদী লকিং
আমরা প্রকৃত Rails অ্যাপে প্রয়োগ করার আগে, সাধারণ REST API-এর প্রেক্ষাপটে আশাবাদী লকিং কেমন হতে পারে তা নিয়ে চিন্তা করা যাক।
উপরে বর্ণিত হিসাবে, আপডেটের সময় তার পরবর্তী অবস্থার সাথে তুলনা করার জন্য এটি পড়ার সময় আমাদের একটি বস্তুর আসল অবস্থা ট্র্যাক করতে হবে। শেষ পড়ার পর থেকে যদি অবস্থার পরিবর্তন না হয়, তাহলে অপারেশন অনুমোদিত। যদি এটি পরিবর্তিত হয়, তবে, এটি ব্যর্থ হবে৷
REST API-এর প্রসঙ্গে আমাদের যা বের করতে হবে তা হল:
- প্রদত্ত সংস্থানের ডেটা পড়ার সময়, আমরা কীভাবে একটি বস্তুর বর্তমান অবস্থা প্রকাশ করব এবং একটি ভোক্তার জন্য প্রতিক্রিয়া হিসাবে এটি ফিরিয়ে দেব?
- আপডেট করার সময় ভোক্তাদের কীভাবে একটি রিসোর্সের আসল অবস্থা একটি API-তে প্রচার করা উচিত?
- অবস্থা পরিবর্তিত হলে এবং আপডেট করা সম্ভব না হলে এপিআই গ্রাহকের কাছে কী ফেরত দেবে?
দুর্দান্ত খবর হল যে এই সমস্ত প্রশ্নের উত্তর দেওয়া যেতে পারে এবং HTTP শব্দার্থবিদ্যার সাথে পরিচালনা করা যেতে পারে।
একটি সম্পদের অবস্থা ট্র্যাকিং যতদূর যায়, আমরা Entity Tags
এর সুবিধা নিতে পারি (বা ETags)। আমরা উৎসের আঙুলের ছাপ/চেকসাম/সংস্করণ নম্বর ডেডিকেটেড ETag
-এ ফেরত দিতে পারি এপিআই গ্রাহকদের কাছে হেডার পরে প্যাচ অনুরোধের সাথে পাঠাতে। আমরা একটি If-Match
ব্যবহার করতে পারি হেডার, এপিআই সার্ভারের জন্য রিসোর্স পরিবর্তিত হয়েছে কিনা তা পরীক্ষা করার জন্য এটি বেশ সহজ করে তোলে। এটি শুধুমাত্র চেকসাম/সংস্করণ নম্বর/আপনি যা কিছু ETag হিসেবে বেছে নিন তা তুলনা করার একটি কেস।
বর্তমান ETag
হলে অনুরোধটি সফল হবে এবং If-Match
মান একই। যদি তা না হয়, API-এর সাথে খুব কমই-ব্যবহৃত 412 Precondition Failed
এর সাথে সাড়া দেওয়া উচিত স্ট্যাটাস, সবচেয়ে উপযুক্ত এবং অভিব্যক্তিপূর্ণ স্ট্যাটাস যা আমরা এই উদ্দেশ্যে ব্যবহার করতে পারি।
অন্য একটি সম্ভাব্য দৃশ্য আছে. যদি API গ্রাহক If-Match
প্রদান করেন তবেই আমরা ETags তুলনা করতে পারি হেডার এটা না হলে কি হবে? আপনি কনকারেন্সি সুরক্ষা উপেক্ষা করতে পারেন এবং আশাবাদী লকিং সম্পর্কে ভুলে যেতে পারেন, তবে এটি আদর্শ নাও হতে পারে। আরেকটি সমাধান হল If-Match
প্রদান করার জন্য এটি একটি প্রয়োজনীয়তা তৈরি করা শিরোনাম এবং 428 Precondition Required
সহ উত্তর দিন স্ট্যাটাস যদি না হয়।
REST API-এ কীভাবে আশাবাদী লকিং কাজ করতে পারে সে সম্পর্কে এখন আমাদের কাছে একটি দৃঢ় সংক্ষিপ্ত বিবরণ রয়েছে, আসুন এটি রেলগুলিতে প্রয়োগ করি৷
রেলের মধ্যে আশাবাদী লকিং
দুর্দান্ত খবর হল যে Rails আশাবাদী লকিং আউট-অফ-দ্য-বক্স অফার করে — আমরা ActiveRecord::Locking::Optimistic
দ্বারা প্রদত্ত বৈশিষ্ট্যটি ব্যবহার করতে পারি। . যখন আপনি lock_version
যোগ করেন কলাম (বা অন্য যা কিছু আপনি চান, যদিও লকিং কলামটি সংজ্ঞায়িত করার জন্য মডেল স্তরে অতিরিক্ত ঘোষণার প্রয়োজন হয়), ActiveRecord প্রতিটি পরিবর্তনের পরে এটিকে বৃদ্ধি করবে এবং বর্তমানে নির্ধারিত সংস্করণটি প্রত্যাশিত কিনা তা পরীক্ষা করবে। এটি বাসি হলে, ActiveRecord::StaleObjectError
আপডেট/ডিস্ট্রয় প্রয়াসে ব্যতিক্রম উত্থাপিত হবে।
আমাদের API-এ আশাবাদী লকিং পরিচালনা করার সবচেয়ে সহজ উপায় হল lock_version
থেকে মান ব্যবহার করা একটি ETag হিসাবে। আমাদের অনুমানমূলক RentalsController
-এর প্রথম ধাপ হিসেবে এটি করা যাক :
class RentalsController
after_action :assign_etag, only: [:show]
def show
@rental = Rental.find(params[:id])
respond_with @rental
end
private
def assign_etag
response.headers["ETag"] = @rental.lock_version
end
end
এটি অবশ্যই কন্ট্রোলারের একটি খুব সরলীকৃত সংস্করণ কারণ আমরা আশাবাদী লকিংয়ের জন্য যা যা প্রয়োজন তাতেই আগ্রহী, প্রমাণীকরণ, অনুমোদন বা অন্যান্য ধারণা নয়। ভোক্তাদের কাছে সঠিক ETag প্রকাশ করার জন্য এটি যথেষ্ট। এখন If-Match
এর যত্ন নেওয়া যাক শিরোনাম যা গ্রাহকরা প্রদান করতে পারেন:
class RentalsController
after_action :assign_etag, only: [:show, :update]
def show
@rental = Rental.find(params[:id])
respond_with @rental
end
def update
@rental = Rental.find(params[:id])
@rental.update(rental_params)
respond_with @rental
end
private
def assign_etag
response.headers["ETag"] = @rental.lock_version
end
def rental_params
params
.require(:rental)
.permit(:some, :permitted, :attributes).merge(lock_version: lock_version_from_if_match_header)
end
def lock_version_from_if_match_header
request.headers["If-Match"].to_i
end
end
এবং যে আশাবাদী লকিং কাজ করার ন্যূনতম সংস্করণ আছে আসলে যথেষ্ট! যদিও, স্পষ্টতই, কোনো দ্বন্দ্ব থাকলে আমরা 500টি প্রতিক্রিয়া ফেরত দিতে চাই না। আমরা If-Match
করব যেকোনো আপডেটের জন্যও প্রয়োজন:
class RentalsController
before_action :ensure_if_match_header_provided, only: [:update]
after_action :assign_etag, only: [:show, :update]
rescue_from ActiveRecord::StaleObjectError do
head 412
end
def show
@rental = Rental.find(params[:id])
respond_with @rental
end
def update
@rental = Rental.find(params[:id])
@rental.update(rental_params)
respond_with @rental
end
private
def ensure_if_match_header_provided
request.headers["If-Match"].present? or head 428 and return
end
def assign_etag
response.headers["ETag"] = @rental.lock_version
end
def rental_params
params
.require(:rental)
.permit(:some, :permitted, :attributes)
.merge(lock_version: lock_version_from_if_match_header)
end
def lock_version_from_if_match_header
request.headers["If-Match"].to_i
end
end
এবং যে সমস্ত কার্যকারিতা বাস্তবায়নের জন্য প্রয়োজনীয় সবকিছুই যা আমরা আগে আলোচনা করেছি। আমরা আরও কিছু উন্নতি করতে পারি — যেমন, শুধুমাত্র প্রতিক্রিয়া কোড ছাড়াও কিছু অতিরিক্ত ত্রুটির বার্তা প্রদান করে — কিন্তু তা এই নিবন্ধের সুযোগের বাইরে হবে৷
র্যাপ-আপ:Rails API-তে আশাবাদী লকিংয়ের গুরুত্ব
REST API ডিজাইন করার সময় একযোগে সুরক্ষা প্রায়ই উপেক্ষা করা হয়, যা গুরুতর পরিণতির দিকে নিয়ে যেতে পারে।
তথাপি, Rails API-এ আশাবাদী লকিং প্রয়োগ করা বেশ সোজা — এই নিবন্ধে দেখানো হয়েছে — এবং সম্ভাব্য জটিল সমস্যাগুলি এড়াতে সাহায্য করবে৷
মজা করুন কোডিং!
পি.এস. আপনি যদি রুবি ম্যাজিক পোস্টগুলি প্রেস থেকে বের হওয়ার সাথে সাথে পড়তে চান তবে আমাদের রুবি ম্যাজিক নিউজলেটারে সাবস্ক্রাইব করুন এবং একটি পোস্টও মিস করবেন না!