7.1 কেন এটি প্রয়োজনীয়

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

আমি আপনাকে কিভাবে একটি লেনদেন ব্যবস্থাপক তৈরি করতে হয় সে সম্পর্কে একটি সম্পূর্ণ নির্দেশিকা দিতে চাই না, কারণ এটি খুব বড় এবং জটিল, এবং আমি শুধুমাত্র কয়েকটি মৌলিক কৌশল কভার করতে চাই। যদি আমরা বিতরণ করা অ্যাপ্লিকেশনগুলির বিষয়ে কথা না বলি, তবে আপনার যদি এসিআইডি গ্যারান্টিগুলির প্রয়োজন হয় তবে আমি অ্যাপ্লিকেশনের দিকে এসিআইডি সম্পূর্ণরূপে প্রয়োগ করার চেষ্টা করার কোনও কারণ দেখি না - সর্বোপরি, একটি প্রস্তুত সমাধান নেওয়া প্রতিটি অর্থে সহজ এবং সস্তা হবে ( অর্থাৎ, ACID সহ একটি ডাটাবেস)।

কিন্তু আমি আপনাকে কিছু কৌশল দেখাতে চাই যা আপনাকে আবেদনের দিকে লেনদেন করতে সাহায্য করবে। সর্বোপরি, এই কৌশলগুলি জানা আপনাকে বিভিন্ন পরিস্থিতিতে সাহায্য করতে পারে, এমনকি যেগুলি অগত্যা লেনদেনের সাথে জড়িত নয় এবং আপনাকে আরও ভাল বিকাশকারী করে তুলতে পারে (আমি আশা করি)।

7.2 লেনদেন প্রেমীদের জন্য মৌলিক সরঞ্জাম

আশাবাদী এবং হতাশাবাদী ব্লকিং। এটি একই সময়ে অ্যাক্সেস করা যেতে পারে এমন কিছু ডেটাতে দুটি ধরণের লক।

আশাবাদীঅনুমান করে যে একযোগে অ্যাক্সেসের সম্ভাবনা এত বেশি নয়, এবং তাই এটি নিম্নলিখিতগুলি করে: পছন্দসই লাইনটি পড়ে, এর সংস্করণ নম্বর মনে রাখে (বা টাইমস্ট্যাম্প, বা চেকসাম / হ্যাশ - যদি আপনি ডেটা স্কিমা পরিবর্তন করতে না পারেন এবং সংস্করণের জন্য একটি কলাম যুক্ত করতে পারেন) অথবা টাইমস্ট্যাম্প), এবং এই ডেটার জন্য ডাটাবেসে পরিবর্তন লেখার আগে, এই ডেটার সংস্করণ পরিবর্তিত হয়েছে কিনা তা পরীক্ষা করে। যদি সংস্করণটি পরিবর্তিত হয়ে থাকে, তবে আপনাকে কোনওভাবে তৈরি হওয়া দ্বন্দ্বের সমাধান করতে হবে এবং ডেটা আপডেট করতে হবে ("কমিট"), বা লেনদেনটি রোল ব্যাক করতে হবে ("রোলব্যাক")। এই পদ্ধতির অসুবিধা হল যে এটি "টাইম-অফ-চেক টু টাইম-অফ-ব্যবহারের" নাম সহ একটি বাগের জন্য অনুকূল পরিস্থিতি তৈরি করে, যাকে সংক্ষেপে TOCTOU বলা হয়: চেক এবং লেখার মধ্যে সময়ের মধ্যে অবস্থা পরিবর্তিত হতে পারে। আশাবাদী লকিং নিয়ে আমার কোন অভিজ্ঞতা নেই,

একটি উদাহরণ হিসাবে, আমি একজন বিকাশকারীর দৈনন্দিন জীবন থেকে একটি প্রযুক্তি পেয়েছি যা আশাবাদী লকিংয়ের মতো কিছু ব্যবহার করে - এটি হল HTTP প্রোটোকল। প্রাথমিক HTTP GET অনুরোধের প্রতিক্রিয়াতে ক্লায়েন্ট থেকে পরবর্তী PUT অনুরোধের জন্য একটি ETag শিরোনাম অন্তর্ভুক্ত হতে পারে, যা ক্লায়েন্ট If-Match শিরোনামে ব্যবহার করতে পারে। GET এবং HEAD পদ্ধতির জন্য, সার্ভার অনুরোধকৃত রিসোর্সটি শুধুমাত্র তখনই ফেরত পাঠাবে যদি এটি তার পরিচিত ETag গুলির একটির সাথে মেলে। PUT এবং অন্যান্য অনিরাপদ পদ্ধতির জন্য, এটি শুধুমাত্র এই ক্ষেত্রেও সম্পদ লোড করবে। আপনি যদি ETag কিভাবে কাজ করে তা জানেন না, এখানে "feedparser" লাইব্রেরি ব্যবহার করে একটি ভাল উদাহরণ দেওয়া হল (যা RSS এবং অন্যান্য ফিড পার্স করতে সাহায্য করে)।


>>> import feedparser 
>>> d = feedparser.parse('http://feedparser.org/docs/examples/atom10.xml') 
>>> d.etag 
'"6c132-941-ad7e3080"' 
>>> d2 = feedparser.parse('http://feedparser.org/docs/examples/atom10.xml', etag=d.etag) 
>>> d2.feed 
{} 
>>> d2.debug_message 
'The feed has not changed since you last checked, so the server sent no data.  This is a feature, not a bug!' 

অন্যদিকে, হতাশাবাদী এই সত্য থেকে এগিয়ে যায় যে লেনদেনগুলি প্রায়শই একই ডেটাতে "সাক্ষাত" হবে এবং তার জীবনকে সহজ করার জন্য এবং অপ্রয়োজনীয় জাতিগত পরিস্থিতি এড়াতে, সে কেবল তার প্রয়োজনীয় ডেটা ব্লক করে দেয় লকিং মেকানিজম বাস্তবায়নের জন্য, আপনাকে হয় আপনার সেশনের জন্য একটি ডাটাবেস সংযোগ বজায় রাখতে হবে (কোনও পুল থেকে সংযোগ টানার পরিবর্তে - এই ক্ষেত্রে আপনাকে সম্ভবত আশাবাদী লকিংয়ের সাথে কাজ করতে হবে), অথবা লেনদেনের জন্য একটি আইডি ব্যবহার করতে হবে , যা সংযোগ নির্বিশেষে ব্যবহার করা যেতে পারে। হতাশাবাদী লকিংয়ের অসুবিধা হল যে এটির ব্যবহার সাধারণভাবে লেনদেনের প্রক্রিয়াকরণকে ধীর করে দেয়, তবে আপনি ডেটা সম্পর্কে শান্ত থাকতে পারেন এবং প্রকৃত বিচ্ছিন্নতা পেতে পারেন।

একটি অতিরিক্ত বিপদ, তবে, সম্ভাব্য অচলাবস্থার মধ্যে লুকিয়ে আছে, যেখানে বেশ কয়েকটি প্রক্রিয়া একে অপরের দ্বারা লক করা সংস্থানগুলির জন্য অপেক্ষা করে। উদাহরণস্বরূপ, একটি লেনদেনের জন্য সম্পদ A এবং B প্রয়োজন। প্রক্রিয়া 1 সম্পদ A দখল করেছে, এবং প্রক্রিয়া 2 সম্পদ B দখল করেছে। দুটি প্রক্রিয়ার কোনোটিই সম্পাদন চালিয়ে যেতে পারে না। এই সমস্যাটি সমাধান করার বিভিন্ন উপায় রয়েছে - আমি এখন বিশদে যেতে চাই না, তাই প্রথমে উইকিপিডিয়া পড়ুন, তবে সংক্ষেপে, একটি লক অনুক্রম তৈরি করার সম্ভাবনা রয়েছে। আপনি যদি এই ধারণাটি আরও বিশদে জানতে চান, তাহলে আপনাকে "ডাইনিং ফিলোসফার সমস্যা" ("ডাইনিং ফিলোসফারদের সমস্যা") নিয়ে আপনার মস্তিস্ককে তাক করার জন্য আমন্ত্রণ জানানো হচ্ছে।

উভয় লক একই পরিস্থিতিতে কীভাবে আচরণ করবে তার একটি ভাল উদাহরণ এখানে

তালা বাস্তবায়ন সংক্রান্ত. আমি বিশদে যেতে চাই না, তবে বিতরণ করা সিস্টেমের জন্য লক ম্যানেজার রয়েছে, উদাহরণস্বরূপ: ZooKeeper, Redis, etcd, Consul.

7.3 অপারেশনের অদম্যতা

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

বুদ্ধিমত্তার বিভিন্ন প্রকাশ হতে পারে। তাদের মধ্যে একটি শুধুমাত্র আপনার কোড লিখতে কিভাবে একটি সুপারিশ. আপনি কি মনে রাখবেন যে সেরা ফাংশন হল এক যে একটি জিনিস করে? এবং এই ফাংশন জন্য ইউনিট পরীক্ষা লিখতে একটি ভাল জিনিস কি হবে? আপনি যদি এই দুটি নিয়ম মেনে চলেন, তাহলে আপনি ইতিমধ্যেই আপনার ফাংশনগুলি অদম্য হওয়ার সম্ভাবনা বাড়িয়ে দিয়েছেন। বিভ্রান্তি এড়াতে, আমি স্পষ্ট করব যে অদম্য ফাংশনগুলি অগত্যা "বিশুদ্ধ" নয় ("ফাংশন বিশুদ্ধতা" অর্থে)। বিশুদ্ধ ফাংশন হল সেই ফাংশনগুলি যেগুলি শুধুমাত্র ইনপুটে প্রাপ্ত ডেটার উপর কাজ করে, সেগুলিকে কোনও উপায়ে পরিবর্তন না করে এবং প্রক্রিয়াকৃত ফলাফল ফিরিয়ে দেয়। এই ফাংশনগুলি আপনাকে কার্যকরী প্রোগ্রামিং কৌশল ব্যবহার করে আপনার অ্যাপ্লিকেশন স্কেল করতে দেয়। যেহেতু আমরা কিছু সাধারণ তথ্য এবং একটি ডাটাবেস সম্পর্কে কথা বলছি, তাই আমাদের ফাংশনগুলি বিশুদ্ধ হওয়ার সম্ভাবনা নেই,

এটি একটি বিশুদ্ধ ফাংশন:


def square(num: int) -> int: 
	return num * num 

কিন্তু এই ফাংশনটি বিশুদ্ধ নয়, কিন্তু অদম্য নয় (দয়া করে আমি কীভাবে এই টুকরোগুলি থেকে কোড লিখি সে সম্পর্কে উপসংহার টানবেন না):


def insert_data(insert_query: str, db_connection: DbConnectionType) -> int: 
  db_connection.execute(insert_query) 
  return True 

অনেক শব্দের পরিবর্তে, আমি কীভাবে অদম্য প্রোগ্রাম লিখতে হয় তা শিখতে কীভাবে বাধ্য হয়েছিলাম সে সম্পর্কে কথা বলতে পারি। আমি AWS এর সাথে অনেক কাজ করি, আপনি এখন দেখতে পাচ্ছেন, এবং AWS Lambda নামে একটি পরিষেবা রয়েছে। ল্যাম্বডা আপনাকে সার্ভারের যত্ন না নেওয়ার অনুমতি দেয়, তবে কেবল কোড লোড করে যা কিছু ইভেন্টের প্রতিক্রিয়া হিসাবে বা একটি সময়সূচী অনুসারে চলবে। একটি ইভেন্ট একটি বার্তা ব্রোকার দ্বারা বিতরণ করা হয় যে বার্তা হতে পারে. AWS-এ, এই ব্রোকার হল AWS SNS। আমি মনে করি যে যারা AWS এর সাথে কাজ করে না তাদের জন্যও এটি পরিষ্কার হওয়া উচিত: আমাদের একটি ব্রোকার আছে যে চ্যানেলগুলির মাধ্যমে বার্তা পাঠায় ("বিষয়"), এবং এই চ্যানেলগুলিতে সদস্যতা নেওয়া মাইক্রোসার্ভিসগুলি বার্তাগুলি গ্রহণ করে এবং কোনওভাবে তাদের প্রতিক্রিয়া জানায়৷

সমস্যা হল যে SNS "অন্তত একবার" ("অন্তত-একবার ডেলিভারি") বার্তা প্রদান করে। এর মানে কী? যে তাড়াতাড়ি বা পরে আপনার Lambda কোড দুবার কল করা হবে. এবং এটা সত্যিই ঘটবে. এমন অনেকগুলি পরিস্থিতি রয়েছে যেখানে আপনার ফাংশনকে অদম্য হতে হবে: উদাহরণস্বরূপ, যখন একটি অ্যাকাউন্ট থেকে অর্থ উত্তোলন করা হয়, তখন আমরা আশা করতে পারি যে কেউ একই পরিমাণ দুবার উত্তোলন করবে, কিন্তু আমাদের নিশ্চিত করতে হবে যে এটি সত্যিই 2টি স্বাধীন সময় - অন্য কথায়, এগুলি 2টি ভিন্ন লেনদেন, এবং একটির পুনরাবৃত্তি নয়।

একটি পরিবর্তনের জন্য, আমি আরেকটি উদাহরণ দেব - API-তে অনুরোধের ফ্রিকোয়েন্সি সীমিত করা ("রেট লিমিটিং")। আমাদের Lambda একটি নির্দিষ্ট user_id সহ একটি ইভেন্ট পায় যার জন্য একটি চেক করা উচিত যে সেই ID সহ ব্যবহারকারী আমাদের কিছু API-তে তার সম্ভাব্য অনুরোধের সংখ্যা শেষ করেছে কিনা। আমরা AWS থেকে করা কলের মান DynamoDB-তে সঞ্চয় করতে পারি এবং আমাদের ফাংশনে প্রতিটি কলের সাথে এটি 1 দ্বারা বৃদ্ধি করতে পারি।

কিন্তু যদি এই Lambda ফাংশন একই ঘটনা দ্বারা দুইবার বলা হয়? যাইহোক, আপনি কি lambda_handler() ফাংশনের আর্গুমেন্টগুলিতে মনোযোগ দিয়েছেন। দ্বিতীয় যুক্তি, AWS Lambda-এ প্রসঙ্গটি ডিফল্টরূপে দেওয়া হয় এবং প্রতিটি অনন্য কলের জন্য তৈরি করা request_id সহ বিভিন্ন মেটাডেটা ধারণ করে। এর মানে হল যে এখন, টেবিলে করা কলের সংখ্যা সংরক্ষণ করার পরিবর্তে, আমরা request_id-এর একটি তালিকা সংরক্ষণ করতে পারি এবং প্রতিটি কলে আমাদের Lambda প্রদত্ত অনুরোধটি ইতিমধ্যে প্রক্রিয়া করা হয়েছে কিনা তা পরীক্ষা করবে:

import json
import os
from typing import Any, Dict

from aws_lambda_powertools.utilities.typing import LambdaContext  # needed only for argument type annotation
import boto3

limit = os.getenv('LIMIT')

def handler_name(event: Dict[str: Any], context: LambdaContext):

	request_id = context.aws_request_id

	# We find user_id in incoming event
	user_id = event["user_id"]

	# Our table for DynamoDB
	table = boto3.resource('dynamodb').Table('my_table')

	# Doing update
	table.update_item(
    	Key={'pkey': user_id},
    	UpdateExpression='ADD requests :request_id',
    	ConditionExpression='attribute_not_exists (requests) OR (size(requests) < :limit AND NOT contains(requests, :request_id))',
    	ExpressionAttributeValues={
        	':request_id': {'S': request_id},
        	':requests': {'SS': [request_id]},
        	':limit': {'N': limit}
    	}
	)

	# TODO: write further logic

	return {
    	"statusCode": 200,
    	"headers": {
        	"Content-Type": "application/json"
    	},
    	"body": json.dumps({
        	"status ": "success"
    	})
	}

যেহেতু আমার উদাহরণটি আসলে ইন্টারনেট থেকে নেওয়া হয়েছে, আমি মূল উৎসের একটি লিঙ্ক ছেড়ে দেব, বিশেষ করে যেহেতু এটি একটু বেশি তথ্য দেয়।

মনে রাখবেন কিভাবে আমি আগে উল্লেখ করেছি যে শেয়ার করা ডেটা লক করতে একটি অনন্য লেনদেন আইডির মতো কিছু ব্যবহার করা যেতে পারে? আমরা এখন শিখেছি যে এটি অপারেশনগুলিকে দুর্বল করতেও ব্যবহার করা যেতে পারে। আসুন জেনে নেওয়া যাক কি কি উপায়ে আপনি নিজে এই ধরনের আইডি তৈরি করতে পারেন।