فرض کریں کہ آپ کے پاس ایک اور تصویر بنانے کے لیے آپ کی AI ایپ میں کافی کریڈٹ باقی ہیں۔ آپ ایک براؤزر ٹیب میں درخواست جمع کراتے ہیں، اور پھر پہلی ٹیب مکمل ہونے سے پہلے دوسرے ٹیب میں دوسری درخواست جمع کراتے ہیں۔
ایپ دونوں کی اجازت دیتی ہے۔
اس کے بعد، ہر درخواست نے کریڈٹ چیک پاس کیا۔ تاہم، درخواست نے بیلنس کی ادائیگی سے زیادہ کام کرنے کی اجازت دی۔ ہم نے ابھی جس رجحان کا تجربہ کیا ہے اسے ریس کی حالت کہا جاتا ہے۔
ڈویلپرز کے لیے دو سوال پیدا ہوتے ہیں:
اس گائیڈ میں ہم ان سوالات کو دریافت کرنے کے لیے ایک چھوٹا Django کریڈٹ سسٹم بنائیں گے۔ بگ کو دوبارہ پیش کریں، اسے ڈیٹا بیس ٹرانزیکشنز اور قطار لاکنگ کے ساتھ ٹھیک کریں، اور جانچیں کہ جب دو درخواستیں آخری کریڈٹ کے لیے مقابلہ کرتی ہیں تو کیا ہوتا ہے۔
جس کا ہم احاطہ کریں گے۔
یہ گائیڈ کس کے لیے ہے۔
یہ گائیڈ ان ڈویلپرز کے لیے ہے جو بنیادی Django کو سمجھتے ہیں لیکن ہم آہنگی کے لیے نئے ہیں۔
ماڈلز، نقل مکانی اور آراء سے واقفیت آپ کو مثالوں کی پیروی میں مدد کرے گی۔ یہاں دکھائے گئے سیٹ اپ کے لیے Python 3.12 اور Docker with Compose کی ضرورت ہے۔ چونکہ SQLite قطار لاکنگ کو لاگو نہیں کرتا ہے جسے ہم استعمال کریں گے، ہم Django 5.2، Django REST Framework 3.16، اور PostgreSQL 17 استعمال کرتے ہیں۔
مجھے آپ کے کوڈ پر لاگو کرنے سے پہلے ہم آہنگی کے تصور کی وضاحت کرنے دیں۔
تصویر کی تخلیق پورے ٹیوٹوریل میں نقلی ہے۔
ہمارا API فوری طور پر قبول کرتا ہے اور نقلی نتائج واپس کرتا ہے۔ کوئی AI فراہم کنندہ اکاؤنٹ یا ادا شدہ API کلید کی ضرورت نہیں ہے۔ یہ عمل کو کریڈٹ کے فیصلوں اور ڈیٹا بیس کی تبدیلیوں پر مرکوز رکھتا ہے۔
آپ تصویر کی درخواستوں کی ادائیگی کے بغیر مکمل مثال کے ساتھ پیروی کر سکتے ہیں۔
ہم آہنگی اور نسل کے حالات کا کیا مطلب ہے۔
کنکرنسی کیا ہے؟
ہم آہنگی کا مطلب ہے دو یا زیادہ آپریشنز وقت کے اوور لیپنگ ادوار میں ہو رہے ہیں۔
مثال کے طور پر، جب درخواست A ڈیٹا بیس کے جواب کا انتظار کر رہی ہے، سرور درخواست B پر کارروائی شروع کر سکتا ہے۔ کمانڈز کو بیک وقت چلانے کی ضرورت نہیں ہے۔ ایک ایونٹ لوپ انتظار کے دوران کاموں کے درمیان سوئچ کر سکتا ہے، یہ ایک ایسا طریقہ ہے جس سے Python ہم آہنگی کے کاموں کو سپورٹ کرتا ہے۔
ایک اہم تفصیل یہ ہے کہ پہلا کام مکمل ہونے سے پہلے دوسرے کام آگے بڑھ سکتے ہیں۔
ریس کی شرط کیا ہے؟
دوڑ کی حالت ایک ایسا نقص ہے جس میں نتیجہ کی درستگی کا انحصار سمورتی کارروائیوں کے وقت یا ترتیب پر ہوتا ہے۔
ایسا اس وقت ہو سکتا ہے جب کام ڈیٹا کا اشتراک کرتے ہیں اور ایپلیکیشن رسائی کو مناسب طریقے سے مربوط نہیں کرتی ہے۔ ہر عمل اپنے طور پر درست معلوم ہو سکتا ہے، لیکن یہ ان معلومات کی بنیاد پر کارروائیاں بھی کر سکتا ہے کہ دوسرے اعمال پہلے ہی تبدیل ہو چکے ہیں۔ اگر پھانسی کا حکم مختلف ہے تو یہ غلط نتائج کا باعث بن سکتا ہے۔
ہم آہنگی نقل کی صلاحیت پیدا کرتی ہے، اور دوڑ کے حالات ایسے کیڑے ہیں جو کسی ایپلیکیشن کے نقل کو سنبھالنے کے طریقے سے پیدا ہوسکتے ہیں۔
کیا ریس کی حالت کہیں اور ہو سکتی ہے؟
ریس کے حالات کاؤنٹرز، صارف کے اکاؤنٹس، پس منظر کے کاموں، اور براؤزر میں دکھائے جانے والے نتائج کو متاثر کر سکتے ہیں۔
ایک شریک ایک شخص ہو سکتا ہے، دو مختلف صارفین کی طرف سے دو درخواستیں، یا ایک خودکار کام ہو سکتا ہے جس میں صارف کی کوئی کارروائی نہیں ہو سکتی۔ ایک مشترکہ وسیلہ ڈیٹا بیس کی قطار، فائل، ان میموری ویلیو، یا کسی صفحہ کی موجودہ حالت ہو سکتی ہے۔ ذیل کی مثالیں کئی طریقے دکھاتی ہیں جن میں ترتیب اہم ہو سکتی ہے۔
حالت کا اشتراک کریں اور ایسے کاموں کی تلاش کریں جو ان میں سے کسی ایک کو مکمل ہونے سے پہلے روک سکتے ہیں۔
ویو کاؤنٹر اپ ڈیٹس کھو دیتا ہے۔
ہم کہتے ہیں کہ ہمارے پاس دو درخواستیں ہیں جہاں ہم ایک پوسٹ پر ملاحظات کی تعداد 100 سے بڑھانا چاہتے ہیں۔
وہ دونوں 100 پڑھتے ہیں اس سے پہلے کہ ان میں سے کوئی بھی اپنی تبدیلیاں محفوظ کر لے۔ ہر ایک 1 کا اضافہ کرتا ہے اور 101 لکھتا ہے۔ کاؤنٹر کو 102 تک پہنچنا چاہیے، اس لیے ایک اپ ڈیٹ ضائع ہو جاتا ہے۔
یہ ایک گمشدہ اپ ڈیٹ ہے اور اس میں خریداری یا محدود اسٹاک شامل نہیں ہے۔
دو سبسکرپشنز ایک ہی صارف نام کا دعوی کرتے ہیں۔
اگر آپ صرف ممکنہ صارف ناموں کی تصدیق پر انحصار کرتے ہیں، تو آپ کا رجسٹریشن فارم ایک ریس بن سکتا ہے۔
دونوں درخواستیں ایک ہی نام کی جانچ کرتی ہیں اور دونوں دستیاب پائی جاتی ہیں۔ پھر ہر ایک اس نام سے ایک اکاؤنٹ بنانے کی کوشش کرتا ہے۔ ڈیٹا بیس کی انفرادیت کے اصولوں کے بغیر، آپ کی ایپلیکیشن ڈپلیکیٹ صارف نام تیار کر سکتی ہے۔
یہاں تحفظ میں پچھلے چیکوں پر انحصار کرنے کے بجائے ڈیٹا بیس کی انفرادیت کو نافذ کرنا شامل ہے۔
دو کارکن ایک ہی کام کا انتخاب کرتے ہیں۔
بیک گراؤنڈ ورکر نادانستہ طور پر اسی زیر التواء کام پر کارروائی کر سکتا ہے۔
دونوں کارکنوں نے درخواست کرنے سے پہلے اس حالت کو پڑھا۔ ہر شخص فیصلہ کرتا ہے کہ کام ممکن ہے اور اس پر کام شروع کر دیتا ہے۔ اس کے بعد ایپلیکیشن دو بار اطلاعات بھیج سکتی ہے یا ایک ہی رپورٹ دو بار تیار کر سکتی ہے۔
ٹاسک بلنگ میکانزم کو کام شروع ہونے سے پہلے ملکیت کو مربوط کرنا چاہیے۔
پرانے تلاش کے جوابات نئے تلاش کے جوابات کی جگہ لے لیتے ہیں۔
چونکہ جوابات ترتیب سے نہیں آتے، اس لیے آپ کا براؤزر غلط تلاش کے نتائج دکھا سکتا ہے۔
"Jjango” ٹائپ کریں اور پھر پہلا جواب آنے سے پہلے تلاش کو "Jjango ٹرانزیکشن” میں تبدیل کریں۔ دوسرا جواب پہلے آتا ہے اور اب مطلوبہ نتیجہ دکھاتا ہے۔ اگر پہلا جواب بعد میں آتا ہے اور بغیر تصدیق کے اسے بدل دیتا ہے، تو صفحہ پچھلی استفسار کے نتائج دکھائے گا۔
ڈیٹا بیس قطار کو لاک کرنا کوئی متعلقہ حل نہیں ہے، کیونکہ درخواست کے شناخت کنندگان یا باسی جوابات کی جانچ کرنا اس مسئلے کو حل کرتا ہے۔
کریڈٹ چیک کو کیسے ناکام بنایا جائے۔
سب سے پہلے، آئیے اس ٹیوٹوریل کے شروع میں مثال کے اصولوں کی وضاحت کرتے ہیں۔ ہر تصویر کی درخواست پر 1 کریڈٹ لاگت آتی ہے۔ کریڈٹ ایپ کے آپ کے اجازت یافتہ استعمال کی نمائندگی کرتے ہیں۔ یہ ماڈل کے عمل کے ٹوکنز سے الگ ہے۔
یہ اس ٹیوٹوریل کے لیے انتخاب کا اصول ہے۔ اگر آپ کا اکاؤنٹ 10 کریڈٹ کے ساتھ شروع ہوتا ہے، تو آپ 9 درخواستوں کی منظوری کے بعد 1 مزید استعمال کر سکتے ہیں۔
درخواست قبول کرنے کے لیے، پسدید کو:
-
اپنے اکاؤنٹ کا بیلنس پڑھیں۔
-
یقینی بنائیں کہ آپ کے پاس کم از کم 1 کریڈٹ باقی ہے۔
-
اپنے کریڈٹ کو کم کریں اور اپنا بیلنس بچائیں۔
-
قبول شدہ درخواستوں کو ریکارڈ کریں۔
ایک وقت میں ایک درخواست کی جانچ کرنا اس عمل کے درست ہونے کی نشاندہی کر سکتا ہے۔ پہلی درخواست پر، بیلنس کو 0 کے طور پر محفوظ کیا جاتا ہے۔ اس کے بعد یہ 0 پڑھتا ہے اور رک جاتا ہے۔
اگلا مرحلہ یکساں کاموں کی جانچ پڑتال کرنا ہے جب ان کا عمل اوورلیپ ہوجاتا ہے۔
اگر دو درخواستیں اوورلیپ ہو جائیں تو کیا ہوتا ہے؟
فرض کریں درخواست A اکاؤنٹ پڑھتا ہے اور ایک کریڈٹ تلاش کرتا ہے۔ ڈیٹا بیس کو اپ ڈیٹ کرنے سے پہلے، درخواست B اسی اکاؤنٹ کو پڑھتا ہے۔ ایک کریڈٹ بھی تلاش کریں۔
اب ہر درخواست کے پاس بیلنس کی اپنی کاپی ہے۔ دونوں چیک پاس کرتے ہیں اور دونوں گنتے ہیں۔ 1 - 1 = 0. A کی درخواست 0 اسٹور کرتا ہے اور نسل کو ریکارڈ کرتا ہے۔ درخواست B پھر 0 کو اسٹور کرتا ہے اور دوسری نسل کو لاگ کرتا ہے۔
حتمی بیلنس 0 ہے، لیکن درخواست نے دو درخواستیں قبول کیں۔ اگر آپ منفی بیلنس کی جانچ کرتے ہیں، تو آپ اس خاص غلطی سے محروم ہو سکتے ہیں۔
غلطی کسی قیمت پر بھروسہ کرنا ہے جب دوسری درخواست کو اسے تبدیل کرنے کا موقع ملا ہے۔ اسے عملی طور پر دیکھنے کے لیے، آئیے پہلے اس غلطی کے ساتھ ایک ورژن بنائیں۔
جینگو پروجیکٹ کو کیسے ترتیب دیا جائے۔
ایک پروجیکٹ ڈائرکٹری اور ورچوئل ماحول بنائیں۔ ذیل میں ایکٹیویشن کمانڈ لینکس اور میک او ایس کے لیے ہیں۔
mkdir django-credit-demo
cd django-credit-demo
python3.12 -m venv .venv
source .venv/bin/activate
یہ کمانڈ اپنی ڈائرکٹری اور ازگر کے ماحول کے ساتھ مثال فراہم کرتی ہے۔
mkdir ایک ڈائریکٹری بنائیں cd یہ آپ کو اس میں منتقل کرتا ہے۔ کہ venv کمانڈ ایک الگ تھلگ ماحول پیدا کرتا ہے: .venv. کہ source کمانڈ کو چلانے سے بعد میں پیکیج کی تنصیبات اس پروجیکٹ سے تعلق رکھنے کے قابل ہو جائیں گی۔
نیچے دی گئی کمانڈ کو چلاتے ہوئے اس ماحول کو فعال رکھیں۔
ونڈوز میں py -3.12 -m venv .venv ماحول بنائیں، .venv\Scripts\Activate.ps1 پاور شیل سے فعال کرنے کے لیے
بنانا requirements.txt:
Django>=5.2,<5.3
djangorestframework>=3.16,<3.17
psycopg[binary]>=3.2,<3.3
اس فائل میں پروجیکٹ کے لیے درکار تین پیکجوں کی فہرست ہے۔
Django ماڈل اور ڈیٹا بیس ٹولز فراہم کرتا ہے، اور Django REST Framework اختتامی نکات کو سنبھالتا ہے۔ Psycopg PostgreSQL کنیکٹیویٹی فراہم کرتا ہے اور [binary] پہلے سے تیار کردہ نفاذ کی درخواست کریں۔ ہر ورژن کی حد درج ذیل سیریز کو چھوڑ کر، منتخب ریلیز سیریز کے اندر اپ ڈیٹس کی اجازت دیتی ہے:
ایک ضروریات کی فائل کا استعمال گائیڈ کی پیروی کرنے والے ہر فرد کے لیے انحصار کو واضح کرتا ہے۔
پیکیج انسٹال کریں، ایک پروجیکٹ بنائیں، credits:
python -m pip install -r requirements.txt
python -m django startproject config .
python manage.py startapp credits
یہ کمانڈز انحصار کو انسٹال کرتے ہیں اور ایپلیکیشن ڈھانچہ بناتے ہیں۔
pip install -r پیکیج کی فہرست پڑھیں۔ requirements.txt. کہ startproject حکم ہے۔ config پیکج اور manage.pyآخری ڈاٹ موجودہ ڈائریکٹری کو منتخب کرتا ہے۔ کہ startapp حکم ہے۔ credits یہ وہ پیکج ہے جہاں ماڈلز، سروس فنکشنز، ویوز اور ٹیسٹ رہیں گے۔
اب آپ کے پاس ایک Django پروجیکٹ ہے جو آپ کے ڈیٹا بیس سے جڑنے کے لیے تیار ہے۔
PostgreSQL شروع کریں۔
بنانا compose.yaml کے پاس manage.py:
services:
db:
image: postgres:17
environment:
POSTGRES_DB: credit_demo
POSTGRES_USER: credit_demo
POSTGRES_PASSWORD: local-demo-only
ports:
- "127.0.0.1:5433:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U credit_demo -d credit_demo"]
interval: 2s
timeout: 5s
retries: 15
یہ ترتیب مقامی ترقی کے لیے ایک PostgreSQL کنٹینر کی وضاحت کرتی ہے۔
image PostgreSQL 17 کو منتخب کریں اور ماحولیاتی اقدار کا ڈیٹا بیس اور مقامی اسناد سیٹ کریں۔ پورٹ میپنگ ڈیٹا بیس کو صرف پورٹ پر کمپیوٹر کے لوپ بیک ایڈریس پر ظاہر کرتی ہے۔ 5433. صحت کی جانچ کی جائے گی۔ pg_isready ہر دو سیکنڈ میں پانچ سیکنڈ کی اجازت ہے، اور کنٹینر کو غیر صحت بخش کے طور پر نشان زد کرنے سے پہلے 15 دوبارہ کوششوں کی اجازت ہے۔
جینگو اپنی سیٹنگز میں کنکشن کی ان ہی تفصیلات کو استعمال کرتا ہے۔
کے ساتھ شروع کریں:
docker compose up -d --wait
یہ کمانڈ اس میں بیان کردہ ڈیٹا بیس کو شروع کرتی ہے: compose.yaml.
up سروس بنائیں اور شروع کریں۔ کہ -d جھنڈا اسے پس منظر میں چلنے دیتا ہے، جس سے آپ ٹرمینل کا استعمال جاری رکھ سکتے ہیں۔ کہ --wait جھنڈا صحت کی جانچ کی بنیاد پر سروس کے صحت مند ہونے تک انتظار کرتا ہے۔
اپنے ڈیٹا بیس کنٹینر کو پورے ٹیوٹوریل میں چلاتے رہیں۔
جینگو کنفیگریشن
تبدیلی config/settings.py اس کم سے کم ترتیب کے ساتھ:
import os
SECRET_KEY = "local-tutorial-only-do-not-use-in-production"
DEBUG = True
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "testserver"]
INSTALLED_APPS = [
"django.contrib.auth",
"django.contrib.contenttypes",
"rest_framework",
"credits",
]
MIDDLEWARE = []
ROOT_URLCONF = "config.urls"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
USE_TZ = True
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ.get("DB_NAME", "credit_demo"),
"USER": os.environ.get("DB_USER", "credit_demo"),
"PASSWORD": os.environ.get("DB_PASSWORD", "local-demo-only"),
"HOST": os.environ.get("DB_HOST", "127.0.0.1"),
"PORT": os.environ.get("DB_PORT", "5433"),
"OPTIONS": {"options": "-c lock_timeout=5000 -c statement_timeout=10000"},
}
}
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.BasicAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
}
یہ کنفیگریشن فائل ایک چھوٹی ایپلی کیشن کے حصوں کو آپس میں جوڑتی ہے۔
INSTALLED_APPS Django، Django REST Framework اور میں یوزر سپورٹ credits ایپ ROOT_URLCONF جبکہ یہ راستے کی تعریف کی طرف اشارہ کرتا ہے۔ DEFAULT_AUTO_FIELD اور USE_TZ خودکار شناخت کنندگان اور ٹائم زون سے آگاہی کی تاریخیں ترتیب دیں۔ خالی MIDDLEWARE فہرست اس API مثال کو کم سے کم کرتی ہے اور ALLOWED_HOSTS مقامی پتوں کی اجازت دیں اور کلائنٹ کے میزبانوں کی جانچ کریں۔
یہ ترتیبات اس ٹیوٹوریل کے اختتامی نکات کے مطابق بنائی گئی ہیں۔
کہ DATABASES یہ سیکشن آپ کو بتاتا ہے کہ Django PostgreSQL تک کیسے رسائی حاصل کرتا ہے۔
ENGINE PostgreSQL پسدید کو منتخب کریں۔ ہر ایک os.environ.get() اختیاری ماحولیاتی متغیرات کو مماثل کنٹینر اقدار کے ساتھ پڑھا اور تبدیل کیا جاتا ہے۔ اختیارات 5 سیکنڈ کا لاک ٹائم آؤٹ اور 10 سیکنڈ کا اسٹیٹمنٹ ٹائم آؤٹ مقرر کرتے ہیں، لہذا اگر آپریشن میں خلل پڑتا ہے، تو غیر معینہ مدت تک انتظار کرنے کے بجائے ایک غلطی پیدا ہو جاتی ہے۔
ٹائم آؤٹ ایک غلطی کا راستہ ہے اور یہ چھوٹا اختتامی نقطہ ان کے لیے حسب ضرورت جواب فراہم نہیں کرتا ہے۔
کہ REST_FRAMEWORK سیکشن کو ایک مستند صارف کی ضرورت ہے۔
BasicAuthentication درخواست کے ساتھ فراہم کردہ اسناد پڑھیں۔ IsAuthenticated نقطہ نظر پرامپٹ کو قبول کرنے سے پہلے گمنام کال کرنے والوں کو مسترد کرتا ہے۔ نمونہ خفیہ کلید اور DEBUG = True یہ مقامی ترقیاتی سیٹ اپ ہے۔
اپنی تعیناتی کے لیے پروڈکشن کے راز، مناسب توثیق، اور انکرپٹڈ کنکشن ترتیب دیں۔
تبدیلی config/urls.py روٹ لسٹ اب خالی ہے۔ کریڈٹ منطق میں ترمیم کرنے کے بعد، ہم ایک اختتامی نقطہ شامل کریں گے۔
urlpatterns = []
یہ خالی فہرست عارضی طور پر جیانگو کو درخواست کا کوئی راستہ فراہم نہیں کرتی ہے۔
جینگو پڑھتا ہے۔ urlpatterns نامی ماڈیول میں ROOT_URLCONF. چونکہ یہ کم سے کم کنفیگریشن مینجمنٹ ایپ کو فعال نہیں کرتی ہے، اس لیے میں نے بنائے گئے انتظامی راستے کو ہٹا دیا۔ ڈیٹا بیس کمانڈز اختتامی نقطہ کے موجود ہونے سے پہلے بھی عمل درآمد جاری رکھ سکتے ہیں۔
محفوظ منظر تیار ہونے کے بعد میں اس فہرست کو بدل دوں گا۔
ماڈل کی تخلیق
اس ماڈل کو اس میں شامل کریں: credits/models.py:
from django.conf import settings
from django.db import models
class CreditAccount(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
balance = models.PositiveIntegerField(default=0)
class Generation(models.Model):
account = models.ForeignKey(CreditAccount, on_delete=models.CASCADE)
prompt = models.CharField(max_length=500)
status = models.CharField(max_length=20, default="reserved")
created_at = models.DateTimeField(auto_now_add=True)
CreditAccount صارف سے وابستہ بیلنس کو اسٹور کرتا ہے۔
settings.AUTH_USER_MODEL پروجیکٹ کے لیے کنفیگر کردہ صارف ماڈل کی نمائندگی کرتا ہے۔ کہ OneToOneField ہم فی صارف زیادہ سے زیادہ ایک اکاؤنٹ کی اجازت دیتے ہیں۔ on_delete=models.CASCADE جب آپ اس صارف کو حذف کرتے ہیں، تو آپ Django کو اکاؤنٹ ہٹانے کو کہتے ہیں۔ PositiveIntegerField(default=0) 0 کی ابتدائی قدر کے ساتھ ایک غیر منفی عددی توازن ذخیرہ کرتا ہے۔
بیلنس فیلڈ ایک الاؤنس کی نشاندہی کرتا ہے، لیکن یہ قسم اکیلے فی کریڈٹ ایک نسل کو نافذ نہیں کرتی ہے۔
Generation ایپلیکیشن منظور شدہ اعمال کو محفوظ کرتی ہے۔
غیر ملکی چابیاں ہر ریکارڈ کو ادائیگی کے اکاؤنٹ سے جوڑتی ہیں اور اکاؤنٹ کو متعدد نسلیں رکھنے کی اجازت دیتی ہیں۔ prompt 500 حروف تک ایڈجسٹ کر سکتے ہیں۔ created_at جب قطار بنتی ہے تو لاگ ان ہوتا ہے۔ status یہ اس طرح شروع ہوتا ہے: reservedاس کا مطلب ہے کہ ہماری سروس نے درخواست لاگ ان کر دی ہے لیکن نقلی تصویری آپریشن مکمل نہیں کیا ہے۔
گھریلو ریکارڈ رکھنے سے آپ کو یہ دیکھنے کی اجازت ملتی ہے کہ آپ نے کتنا کریڈٹ کٹوایا ہے وہ اصل میں آپ نے ادا کیا ہے۔
ہجرتیں بنائیں اور لاگو کریں۔
python manage.py makemigrations credits
python manage.py migrate
یہ کمانڈ ماڈل کی تعریف کو ڈیٹا بیس ٹیبل میں تبدیل کرتی ہے۔
makemigrations credits ایک منتقلی فائل بنائیں جو ماڈل کی تبدیلیوں کو بیان کرے۔ migrate زیر التواء منتقلی کا اطلاق کریں، بشمول Django کے صارف کی میز اور دو نئی میزیں۔ کنکشن ڈیٹا بیس کی ترتیبات سے آئے گا جو آپ نے ابھی ترتیب دی ہے۔
ڈیٹا بیس اب اکاؤنٹس اور تخلیق کی درخواستوں کو ذخیرہ کرنے کے لیے تیار ہے۔
نسل کی حالت کو دوبارہ کیسے تیار کیا جائے۔
بنانا credits/services.py درج ذیل جان بوجھ کر غیر محفوظ خصوصیات شامل کریں:
from .models import CreditAccount, Generation
class InsufficientCredits(Exception):
pass
def reserve_generation_unsafe(user_id, prompt):
account = CreditAccount.objects.get(user_id=user_id)
if account.balance < 1:
raise InsufficientCredits
account.balance -= 1
account.save(update_fields=["balance"])
return Generation.objects.create(account=account, prompt=prompt)
یہ فنکشن کنکرنسی کے تحفظ کے بغیر کریڈٹ چیکنگ کو لاگو کرتا ہے۔
objects.get() اکاؤنٹس تلاش کریں۔user_idاور raise InsufficientCredits اگر بیلنس 1 سے کم ہے تو یہ کام کرنا بند کر دے گا۔ گھٹاؤ ایک ازگر آبجیکٹ کو تبدیل کرتا ہے اور پھر save(update_fields=["balance"]) اس قدر کو ڈیٹا بیس میں لکھیں۔ آخر کار Generation.objects.create() قبول شدہ درخواست داخل کرتا ہے اور متعلقہ ماڈل آبجیکٹ واپس کرتا ہے۔
پڑھنا اور لکھنا الگ الگ کام رہتا ہے، لہذا دوسری درخواستیں درمیان میں کام کر سکتی ہیں۔
InsufficientCredits کال کرنے والے کو ایک مخصوص پروسیسنگ ناکامی فراہم کرتا ہے۔
Python کی استثنائی کلاس سے اخذ کردہ ایک حسب ضرورت استثناء کلاس۔ Exception. کہ pass بیان کا مطلب ہے کہ یہ اس طبقے میں کوئی رویہ شامل نہیں کرتا ہے۔ بعد میں، منظر اس استثناء کو پکڑتا ہے اور ایک مفید جواب دیتا ہے۔
جان بوجھ کر غیر محفوظ خصوصیات کو موازنہ کے قابل رکھیں، لیکن ان کے ذریعے اختتامی مقامات کو روٹ نہ کریں۔
لکھنے سے پہلے دونوں پڑھنے کو فعال کریں۔
دو براؤزر ٹیبز کو کھولنا بگ کو دوبارہ پیدا کرنے کا ایک قابل اعتماد طریقہ نہیں ہے۔ ایک درخواست دوسری درخواست کے بیلنس پڑھنے سے پہلے مکمل ہو سکتی ہے۔ اس کے بجائے، یہ دو جینگو شیل استعمال کرتا ہے اور ہر ایک کے اکاؤنٹ کو پڑھنے کے بعد توقف کرتا ہے۔
اپنی پروجیکٹ ڈائرکٹری میں ٹرمینل کھولیں، ورچوئل ماحول کو چالو کریں، اور شیل شروع کریں۔
python manage.py shell
یہ کمانڈ آپ کے جیانگو پروجیکٹ کے لوڈ ہونے کے ساتھ ایک ازگر کا شیل کھولتا ہے۔
اس سے متعلق ترتیبات کا استعمال کریں: manage.py. آپ ماڈلز درآمد کر سکتے ہیں اور ترتیب شدہ ڈیٹا بیس سے براہ راست استفسار کر سکتے ہیں۔ ہر ٹرمینل جسے آپ کھولنا چاہتے ہیں تجربات کے لیے ایک الگ شیل فراہم کرتا ہے۔
ہم ان شیلوں کو ڈیٹا بیس کے آپریشنز کے آرڈر کو کنٹرول کرنے کے لیے استعمال کریں گے۔
ایک نئے صارف اور 1 کریڈٹ کے ساتھ ایک اکاؤنٹ بنائیں:
from django.contrib.auth import get_user_model
from credits.models import CreditAccount, Generation
user = get_user_model().objects.create_user(username="race-demo")
CreditAccount.objects.create(user=user, balance=1)
account_a = CreditAccount.objects.get(user__username="race-demo")
print(account_a.balance)
یہ بلاک 1 کے توازن کے ساتھ پہلا آپریشن تیار کرتا ہے۔
get_user_model() کنفیگر شدہ یوزر کلاس کو بازیافت کریں اور create_user() ڈیمو صارف داخل کریں۔ جب ایک اکاؤنٹ بنتا ہے، صارف کو 1 کریڈٹ تفویض کیا جاتا ہے۔ تلاش کا استعمال کرتا ہے: user__username صارف کے تعلقات کی پیروی کریں اور نتیجے میں اکاؤنٹ آبجیکٹ کو محفوظ کریں۔ account_a.
آپ کو ایک پرنٹ آؤٹ دیکھنا چاہئے۔1. اپنے اکاؤنٹ کو اپ ڈیٹ کیے بغیر اس شیل کو کھلا رہنے دیں۔
دوسرے ٹرمینل میں اسی ماحول کو چالو اور چلائیں۔ python manage.py shell دوبارہ وہ کہانی بھی پڑھیں:
from credits.models import CreditAccount, Generation
account_b = CreditAccount.objects.get(user__username="race-demo")
print(account_b.balance)
دوسرا شیل اسی ڈیٹا بیس کی قطار کو ایک اور ازگر آبجیکٹ کی طرح پڑھتا ہے۔
account_b یہ اس خول سے تعلق رکھتا ہے اور اس سے الگ ہے: account_a. چونکہ پہلے شیل نے کٹوتی کو ذخیرہ نہیں کیا تھا، اس لیے اس سوال کو 1 کا بیلنس بھی لوٹانا چاہیے۔ اگر آپ بعد میں اسے کسی اور شیل میں تبدیل کرتے ہیں تو یہ آبجیکٹ خود بخود تازہ نہیں ہوگا۔
اب آپ کے پاس کریڈٹس کی ایک کاپی ہے جو آپ دونوں کاموں کے لیے استعمال کرنا چاہتے ہیں۔
اپنے پہلے شیل پر واپس جائیں اور چلائیں:
if account_a.balance >= 1:
account_a.balance -= 1
account_a.save(update_fields=["balance"])
Generation.objects.create(account=account_a, prompt="A garden")
اب پہلا شیل بیلنس کاپی چیک کرے گا اور استعمال کرے گا۔
کہ if کیونکہ شرط گزر جاتی ہے۔ account_a.balance یہ ایک ہے۔ جب منہا کیا جائے تو یہ 0 میں بدل جاتا ہے، save() اکاؤنٹ کی قطار میں 0 لکھیں۔ آخری لائن گارڈن پرامپٹ کے لیے ایک نسل بناتی ہے۔
ٹرمینلز کو تبدیل کرنے سے پہلے بلاک کو مکمل کرنے کے لیے خالی لائن پر Enter دبائیں۔
پھر اس بلاک کو دوسرے شیل میں چلائیں۔
if account_b.balance >= 1:
account_b.balance -= 1
account_b.save(update_fields=["balance"])
Generation.objects.create(account=account_b, prompt="A beach")
دوسرا شیل فیصلے کرنے کے لیے پہلے سے بھری ہوئی اشیاء کا استعمال کرتا ہے۔
کہ if حالت اب بھی ایک کو ظاہر کرتی ہے کیونکہ اسے تازہ نہیں کیا گیا ہے۔ account_b. 1 کو گھٹائیں اور 0 کو ذخیرہ کریں، اسی قدر کے ساتھ بیلنس کو اوور رائٹ کریں جو آپ نے پہلے آپریشن میں لکھا تھا۔ پھر بیچ پرامپٹ کے لیے ایک الگ تخلیق بنائیں۔
دوسرے کام کے لیے، آپ نے کریڈٹس کا استعمال کرتے ہوئے ٹاسک کو قبول کیا جو آپ پہلے کام پر استعمال کر چکے تھے۔
آخر میں، ہم ڈیٹا بیس کو دوسرے شیل میں چیک کرتے ہیں۔
account_b.refresh_from_db()
print(account_b.balance)
print(Generation.objects.filter(account=account_b).count())
یہ بلاک دونوں کارروائیوں کے ذخیرہ شدہ نتائج کو چیک کرتا ہے۔
refresh_from_db() اکاؤنٹ کو دوبارہ لوڈ کریں تاکہ پرنٹنگ ڈیٹا بیس کی اقدار کی عکاسی کرے۔ فلٹر count() صرف اس اکاؤنٹ سے منسلک گھرانوں کو شمار کیا جاتا ہے۔ آپ کو 0 کریڈٹس اور 2 تخلیق کے ریکارڈ دیکھنے چاہئیں۔
گھرانوں کی تعداد ان ناکامیوں کو ظاہر کرتی ہے جنہیں صرف توازن کے ذریعے چھپایا نہیں جا سکتا۔
دو خولوں کا استعمال آپ کو جان بوجھ کر غیر محفوظ ترتیب کو دوبارہ پیش کرنے کی اجازت دیتا ہے۔
یہ ہر پڑھنے کے بعد رک گیا اور پھر دونوں لکھنے کی اجازت دی۔ درخواستوں کے اوورلیپ ہونے پر سرور ایک جیسے آرڈر تیار کر سکتے ہیں۔ یہاں تک کہ اگر آپ ہر بار کوشش کرنے پر ایسا نہیں کرتے ہیں۔ اگر آپ تجربہ دہرانا چاہتے ہیں تو ایک نیا صارف نام اور اکاؤنٹ بنائیں تاکہ آپ کی پچھلی تاریخ آپ کی گنتی کو متاثر نہ کرے۔
اب ہم ان عین خالی جگہوں کے ارد گرد تحفظ بنا سکتے ہیں جن کا ہم نے مشاہدہ کیا ہے۔
اپنے توازن کی حفاظت کیسے کریں۔
اس فنکشن کے ساتھ ڈیٹا بیس کے دو مسائل ہیں: کریڈٹ کٹوتی اور تخلیق کے ریکارڈ کو کامیاب ہونے کے لیے مل کر کام کرنا چاہیے۔ مسابقتی درخواستوں کے لیے اکاؤنٹس کی تصدیق اور اپ ڈیٹ کرنے کے لیے ایک مربوط طریقہ کی بھی ضرورت ہوتی ہے۔
میں اس ترتیب میں ان کا احاطہ کروں گا۔
ایک لین دین میں متعلقہ تبدیلیاں رکھیں
کوئی راستہ نہیں ڈیٹا بیس ٹرانزیکشن کاموں کو ٹاسک یونٹوں میں گروپ کریں۔ جینگو کا transaction.atomic() اگر بلاک کامیابی سے مکمل ہو جاتا ہے، تو ہم تبدیلیوں کا ارتکاب کرتے ہیں اور اگر کوئی استثناء بلاک چھوڑ دیتا ہے تو ہم واپس لوٹ جاتے ہیں۔
یہ اہم ہے کیونکہ غیر محفوظ فنکشن اپنی تخلیق کی تاریخ بنانے سے پہلے توازن کو محفوظ کرتا ہے۔ اگر ریکارڈ کی تخلیق ناکام ہو جاتی ہے تو، کٹوتیوں کو مجاز تخلیق کے بغیر برقرار رکھا جا سکتا ہے۔
مثالی مقاصد کے لیے، کام کو سمیٹنے کا طریقہ یہاں ہے:
from django.db import transaction
@transaction.atomic
def reserve_generation_atomic_only(user_id, prompt):
account = CreditAccount.objects.get(user_id=user_id)
if account.balance < 1:
raise InsufficientCredits
account.balance -= 1
account.save(update_fields=["balance"])
return Generation.objects.create(account=account, prompt=prompt)
ڈیکوریٹر فنکشن کے ڈیٹا بیس کی کارروائیوں کو ایٹمی لین دین کے اندر رکھتا ہے۔
from django.db import transaction جیانگو کے لین دین کے ٹولز فراہم کرتا ہے۔ فنکشن اب بھی اکاؤنٹ پڑھتا ہے، بیلنس چیک کرتا ہے، کٹوتیوں کو اسٹور کرتا ہے، اور اس ترتیب میں نسلیں تخلیق کرتا ہے۔ اگر ریکارڈ بنانے کے دوران کوئی استثناء ہوتا ہے، تو لین دین سے کٹوتی بھی واپس لے لی جاتی ہے۔
یہ کٹوتیوں اور تخلیق کے ریکارڈ کے درمیان تعلق کی حفاظت کرتا ہے۔
ایک کریڈٹ چیک اب بھی باقاعدہ اکاؤنٹ انکوائری میں سامنے آئے گا۔
PostgreSQL ریڈ کمٹ کو اپنی ڈیفالٹ آئسولیشن لیول کے طور پر استعمال کرتا ہے۔ اس کے نیچے، ٹرانزیکشن کے اندر نارمل ریڈز دیگر ٹرانزیکشنز کو قطار پڑھنے سے پہلے انتظار نہیں کرے گی۔ لہذا دونوں فنکشنز اپ ڈیٹ کرنے سے پہلے ایک کریڈٹ پڑھ سکتے ہیں۔
توازن کے فیصلے کرنے سے پہلے رسائی کو ایڈجسٹ کرنے کی ضرورت ہے۔
بیلنس چیک کرنے سے پہلے اکاؤنٹ لاک کریں۔
قطار بند کرنا ایک ڈیٹا بیس میکانزم ہے۔ منتخب قطاروں پر متضاد کارروائیوں کو محدود کرتا ہے جب کہ لین دین میں تالا ہوتا ہے۔
اس مثال میں، قطار ایک کریڈٹ اکاؤنٹ کے لیے ذخیرہ شدہ ریکارڈ ہے۔ کوئی راستہ نہیں SELECT FOR UPDATE تالے مسابقتی اپ ڈیٹس بناتے ہیں، اور متضاد لاک درخواستیں تالے کے جاری ہونے کا انتظار کرتی ہیں۔ نارمل ریڈز جاری رہ سکتی ہیں اور ٹرانزیکشنز اکاؤنٹ کی دوسری قطاروں پر چل سکتی ہیں۔
یہ آپ کو بیلنس چیک کرنے اور تبدیل کرنے کے دوران ایک اکاؤنٹ کی حفاظت کرنے کی اجازت دیتا ہے۔
شامل کریں from django.db import transaction سب سے اوپر credits/services.py. موازنہ کے لیے، غیر محفوظ خصوصیات کو رکھیں اور پھر اگلا محفوظ ورژن شامل کریں۔
@transaction.atomic
def reserve_generation(user_id, prompt):
account = CreditAccount.objects.select_for_update().get(user_id=user_id)
if account.balance < 1:
raise InsufficientCredits
account.balance -= 1
account.save(update_fields=["balance"])
return Generation.objects.create(account=account, prompt=prompt)
یہ فنکشن بیلنس چیک کرنے سے پہلے اکاؤنٹ کے لیے ایک قطار لاک حاصل کرتا ہے۔
select_for_update() ایک تالا کی درخواست کریں .get(user_id=user_id) ٹرانزیکشن کے اندر اس اکاؤنٹ کے لیے استفسار پر عمل کریں۔ تالا کو پکڑنے کے بعد، یہ فنکشن بیلنس چیک کرتا ہے اور رقم اکٹھا کرتا ہے۔ InsufficientCredits اگر ضروری ہو تو۔ بصورت دیگر، آپ کٹوتی کو بچائیں گے اور لین دین مکمل ہونے سے پہلے ایک نسل بنائیں گے۔
فیصلے اور ڈیٹا بیس میں تبدیلیاں اب اس وقت ہوتی ہیں جب آپ کا اکاؤنٹ محفوظ ہو۔
اس فنکشن میں مسابقتی کالز کو لاک کے سوال پر انتظار کرنا ہوگا۔
ایک بار جب پہلی کال کٹوتی کا ارتکاب کرتی ہے، ویٹنگ کال اپ ڈیٹ شدہ بیلنس کے لیے Read Committed سیٹنگ کو چیک کرتی ہے۔ اگر پہلی کال واپس کر دی جاتی ہے، تو اس کٹوتی کو برقرار نہیں رکھا جائے گا۔ ایک ترتیب شدہ ٹائم آؤٹ ایک خرابی کے ساتھ انتظار کو روکنے کا سبب بن سکتا ہے۔
1 کریڈٹ اور ایک کامیاب پہلی کمٹ کے لیے، دوسری کال 0 پڑھتی ہے اور درخواست کو مسترد کرتی ہے۔
اس تحفظ کو ان تمام راستوں کا احاطہ کرنا چاہیے جن کے ذریعے توازن استعمال کیا جاتا ہے۔
پچھلا فنکشن آپ کو لاک کی درخواست کیے بغیر اکاؤنٹ پڑھنے کی اجازت دیتا ہے۔ مستقبل کے اپ ڈیٹس اس وقت تک انتظار کریں گے جب تک کہ تالا بند رہے گا، لیکن پرانی اقدار کا استعمال کرتے ہوئے بیلنس کو اوور رائٹ کر سکتے ہیں۔ لہذا، انتظامی ہم آہنگی اور پس منظر کے کاموں کے لیے بھی ایک محفوظ اپ ڈیٹ کی حکمت عملی کی ضرورت ہے۔
ایک محفوظ فنکشن کسی غیر محفوظ مصنف کو دوسری جگہ تبدیل نہیں کر سکتا۔
لین دین سے باہر تصویر کی تخلیق کو برقرار رکھیں
لین دین میں کریڈٹ ریزرویشن شامل ہونا چاہیے۔ ریموٹ امیج کی درخواستوں میں ڈیٹا بیس کی کارروائیوں کے مقابلے میں زیادہ وقت لگ سکتا ہے، لہذا انہیں لین دین کے اندر رکھنے سے مسابقتی درخواستوں کو غیر ضروری طور پر انتظار کرنا پڑتا ہے۔
ڈیمو کے لیے، اس فعالیت کو اس میں شامل کریں: credits/services.py:
def simulate_generation(generation):
# No external AI request is made in this tutorial.
generation.status = "completed"
generation.save(update_fields=["status"])
return "Simulated image generation completed."
یہ فنکشن ایک منظور شدہ تخلیق کی تکمیل کو نقل کرتا ہے۔
فراہم کردہ تعمیر شدہ آبجیکٹ کے مواد کو تبدیل کرتا ہے۔ status کو completed. کہ save() کال صرف اس فیلڈ کو محفوظ رکھتی ہے۔ کیونکہ واپسی کی قیمت ایک پیغام ہے، فنکشن نہ تو کوئی تصویر بناتا ہے اور نہ ہی کوئی بیرونی درخواست کرتا ہے۔
ہم اسے شیڈولنگ فنکشن کی واپسی کے بعد کال کریں گے۔
کال کی ترتیب تصویری کارروائیوں کو شیڈولنگ ٹرانزیکشن سے باہر رکھتی ہے۔
ان ترتیبات اور ایک شامل ٹرانزیکشن کے بغیر، ریزرویشن سمولیشن شروع ہونے سے پہلے کی جائے گی۔ اگر آپ اس پوائنٹ کے بعد ناکام ہو جاتے ہیں، تو آپ کا کریڈٹ خود بخود بحال نہیں ہو گا۔ حقیقی فراہم کنندہ کے انضمام کے لیے ان کی اپنی دوبارہ کوشش یا رقم کی واپسی کی پالیسی کی ضرورت ہوگی۔
جب ہم خود ریزرویشن کا تجربہ کر لیں گے تو ہم اس حد تک واپس آ جائیں گے۔
تصویری درخواستوں کو قبول کرنے اور جانچنے کا طریقہ
اب آپ اپنے محفوظ فنکشن کو اپنے اینڈ پوائنٹ سے جوڑ سکتے ہیں۔ اس کوڈ کو اس میں شامل کریں: credits/views.py:
from rest_framework import serializers, status
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import CreditAccount
from .services import InsufficientCredits, reserve_generation, simulate_generation
class GenerationInput(serializers.Serializer):
prompt = serializers.CharField(max_length=500)
class GenerateView(APIView):
def post(self, request):
serializer = GenerationInput(data=request.data)
serializer.is_valid(raise_exception=True)
try:
generation = reserve_generation(
request.user.pk, serializer.validated_data["prompt"]
)
except CreditAccount.DoesNotExist:
return Response({"detail": "Credit account not found."}, status=404)
except InsufficientCredits:
return Response({"detail": "Not enough credits."}, status=409)
result = simulate_generation(generation)
return Response(
{"id": generation.pk, "status": generation.status, "result": result},
status=status.HTTP_201_CREATED,
)
سیریلائزر پرامپٹس کی جانچ کرتا ہے اس سے پہلے کہ ویو کریڈٹ استعمال کرے۔
GenerationInput 500 حروف کی زیادہ سے زیادہ لمبائی کے ساتھ مطلوبہ ٹیکسٹ فیلڈ کا اعلان کریں۔ is_valid(raise_exception=True) توثیق کا جواب غائب، خالی، یا غلط ان پٹ کو مسترد کرتا ہے۔ منظر پھر صاف شدہ اقدار کو پڑھتا ہے یہاں سے: validated_data اور اسے منتقل کریں request.user.pkریزرویشن فنکشن میں مستند صارف کے ڈیٹابیس شناخت کنندہ کو شامل کریں۔
کلائنٹ ایک پرامپٹ فراہم کرتا ہے جب کہ سرور ایک تصدیق شدہ صارف سے اکاؤنٹ منتخب کرتا ہے۔
نقطہ نظر ریزرویشن کے نتائج کو جواب میں بدل دیتا ہے۔
گمشدہ اکاؤنٹس بنائے گئے ہیں۔404اور InsufficientCredits پیداوار 409یہ توازن تنازعات کے لیے ہمارا منتخب جواب ہے۔ اگر کامیاب ہو جاتا ہے تو، ریزرویشن کی واپسی کے بعد منظر نقلی کو کال کرتا ہے۔ یہ بھیجتا ہے 201 ریکارڈ شناخت کنندہ، تکمیل کی حیثیت، اور نقلی نتائج شامل ہیں۔
یہ شاخیں کال کرنے والے کو منظور شدہ کارروائیوں اور مسترد شدہ درخواستوں کے درمیان فرق کرنے کی اجازت دیتی ہیں۔
تبدیلی config/urls.py کے ساتھ:
from django.urls import path
from credits.views import GenerateView
urlpatterns = [
path("api/generate/", GenerateView.as_view()),
]
یہ راستہ درخواست کے پتے کو منظر سے جوڑتا ہے۔
path() معاہدہ api/generate/ یہ ایڈریس کا حصہ ہے۔ GenerateView.as_view() کلاس پر مبنی نظاروں کو کال کے قابل نظاروں سے تبدیل کریں جن کے ساتھ Django گزر سکتا ہے۔ کا نظارہ post() طریقہ اس راستے پر POST کی درخواستوں کو ہینڈل کرتا ہے۔
اختتامی نقطہ اب استعمال کے لیے تیار ہے۔ /api/generate/ جب سرور چلتا ہے۔
براہ کرم ایک وقت میں ایک درخواست آزمائیں۔
جینگو شیل کھولیں اور اینڈ پوائنٹ ڈیمو کے لیے الگ صارف بنائیں۔
from django.contrib.auth import get_user_model
from credits.models import CreditAccount
user = get_user_model().objects.create_user(
username="api-demo",
password="local-example-password",
)
CreditAccount.objects.create(user=user, balance=1)
یہ بلاک توثیق شدہ اختتامی نقطہ مثال کے لیے صارف بناتا ہے۔
create_user() صارف نام کو اسٹور کرتا ہے اور فراہم کردہ پاس ورڈ کو ہیش کرتا ہے۔ CreditAccount.objects.create() ہم نئے صارفین کو 1 کریڈٹ فراہم کرتے ہیں۔ الگ الگ صارف نام اس چیک کو 2-شیل تجربے میں استعمال کیے گئے اکاؤنٹ سے آزاد رکھتے ہیں۔
ذیل کی اسناد صرف اس مقامی ڈیمو اکاؤنٹ سے تعلق رکھتی ہیں۔
شیل سے باہر نکلیں اور ڈویلپمنٹ سرور شروع کریں۔
python manage.py runserver
یہ کمانڈ جینگو کے ڈویلپمنٹ سرور کو شروع کرتی ہے۔
اگر پتہ کی کوئی دلیل نہیں ہے، تو یہ اس سے حاصل کرتا ہے: 127.0.0.1:8000. اس پتے سے درخواستیں روٹ کنفیگریشن سے گزریں گی جسے آپ نے ابھی شامل کیا ہے۔ دوسرے ٹرمینل سے درخواستیں بھیجتے وقت اس ٹرمینل کو کھلا رکھیں۔
یہ سرور صرف مقامی ڈیمو استعمال کے لیے ہے۔
دوسرے ٹرمینل سے درخواست بھیجیں۔
curl -i -u api-demo:local-example-password \
-H "Content-Type: application/json" \
-d '{"prompt": "A garden at sunrise"}' \
http://127.0.0.1:8000/api/generate/
یہ کمانڈ اینڈ پوائنٹ پر ڈیمو صارف کی اسناد پر مشتمل ایک پرامپٹ جمع کراتی ہے۔
-i جوابی عنوانات شامل کریں۔ -u بنیادی تصدیق کے لیے صارف نام اور پاس ورڈ فراہم کریں۔ -H جاوا اسکرپٹ آبجیکٹ نوٹیشن کا اعلان کریں (JSON) درخواست کے باڈی فارمیٹ کے طور پر۔ -d وہ باڈی فراہم کریں اور کرل کر کے اشارہ کردہ پتے پر پوسٹ کی درخواست بھیجیں۔
پہلی درخواست واپس آنی چاہئے۔ 201 تخروپن کی تکمیل کے نتیجے میں۔
اپنا آرڈر دوبارہ بھیجیں اور ہم پہلی کٹوتی کے بعد آپ کے اکاؤنٹ کی تصدیق کریں گے۔
اگلی درخواست میں صفر کریڈٹ ہونا چاہیے۔ ایک نقطہ نظر واپس کیا جانا چاہئے 409 کے ساتھ "detail": "Not enough credits.". چونکہ ہم نے درخواستوں کے درمیان انتظار کیا تھا، اس لیے ہم کوئی موافقت نہیں چلا رہے ہیں، ہم صرف عام آرڈرنگ کی جانچ کر رہے ہیں۔
اگلا، آئیے نیسٹڈ آپریشنز کی جانچ کرتے ہیں۔
خودکار ٹیسٹ سیٹ اپ
تبدیلی credits/tests.py درج ذیل امپورٹ اور مددگار کلاسز کا استعمال کریں: ہم اگلے مرحلے میں ٹیسٹ کا طریقہ شامل کریں گے۔
from concurrent.futures import ThreadPoolExecutor
from threading import Barrier
from unittest.mock import patch
from django.contrib.auth import get_user_model
from django.db import (
OperationalError, close_old_connections, connection, connections, transaction,
)
from django.test import TransactionTestCase
from rest_framework.test import APIClient
from .models import CreditAccount, Generation
from .services import InsufficientCredits, reserve_generation, reserve_generation_unsafe
class ConcurrencyTests(TransactionTestCase):
def setUp(self):
if connection.vendor != "postgresql":
self.skipTest("Run these concurrency tests on PostgreSQL.")
self.user = get_user_model().objects.create_user(username="parallel-reader")
self.account = CreditAccount.objects.create(user=self.user, balance=1)
def run_two(self, action):
def worker():
close_old_connections()
try:
return action()
finally:
connections.close_all()
with ThreadPoolExecutor(max_workers=2) as pool:
futures = [pool.submit(worker) for _ in range(2)]
return [future.result(timeout=15) for future in futures]
ٹیسٹ کلاس ہر کنکرنسی ٹیسٹ سے پہلے ایک نیا اکاؤنٹ تیار کرتی ہے۔
setUp() اگر آپ کا کنکشن PostgreSQL نہیں ہے، تو ٹیسٹ کو چھوڑ دیں۔ پھر ایک کریڈٹ کے ساتھ صارف اور اکاؤنٹ بنائیں۔ TransactionTestCase باقاعدہ لین دین کے برعکس، یہ حقیقی لین دین کی حدود کی اجازت دیتا ہے۔TestCaseاس پر مشتمل لین دین لاک کے استعمال کی غلطیوں کو چھپا سکتا ہے۔
SQLite کے ذریعے چھوڑے گئے ٹیسٹ PostgreSQL کے لاکنگ رویے کی جانچ نہیں کرتے ہیں۔
کہ run_two() ایک مددگار دونوں ورکر تھریڈز کو ایک جیسا کام فراہم کرتا ہے۔
ThreadPoolExecutor(max_workers=2) ہر ایک کو کارکن فراہم کریں۔ pool.submit(worker) ایک کال کا شیڈول بنائیں۔ مستقبل اس کال کے حتمی نتیجے کی نمائندگی کرتا ہے۔ future.result(timeout=15) اس کے لیے غلطیوں کا پتہ لگاتا ہے یا بڑھاتا ہے: ہر کارکن آپریشن سے پہلے پچھلے ناقابل استعمال کنکشنز کو صاف کرتا ہے اور اپنے کنکشنز کو بند کر دیتا ہے، بشمول آپریشن کے ناکام ہونے پر۔
یہ دونوں کاموں کو الگ الگ کنکشن کے ذریعے ایک ہی ڈیٹا بیس تک پہنچنے کی اجازت دیتا ہے۔
غیر محفوظ سلوک کی جانچ کریں۔
اس طریقہ کو اندر شامل کریں:ConcurrencyTestsاسی انڈینٹیشن کی سطح پر run_two():
def test_reproduce_unsafe_spending(self):
both_have_read = Barrier(2)
original_get = CreditAccount.objects.get
def read_then_wait(*args, **kwargs):
account = original_get(*args, **kwargs)
both_have_read.wait(timeout=5)
return account
with patch(
"credits.services.CreditAccount.objects.get",
side_effect=read_then_wait,
):
self.run_two(
lambda: reserve_generation_unsafe(self.user.pk, "A garden").pk
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, 0)
self.assertEqual(Generation.objects.count(), 2)
یہ ٹیسٹ دو میں سے ایک غیر محفوظ آپریشن ہونے سے پہلے پڑھنے پر مجبور کرتا ہے۔
original_get حقیقی خیالات کا ٹریک رکھیں read_then_wait() انتظار کرنے سے پہلے مجھے کال کریں۔ Barrier(2). بیریئر کارکنوں کو دونوں کے پہنچنے کے بعد ہی رہا کرتا ہے، یا انتظار کا وقت ختم ہونے پر کوئی غلطی پیدا کرتا ہے۔ patch() عارضی طور پر سروس کی تلاش کو اس ریپر کے ساتھ بلاک کے دورانیے کے لیے تبدیل کریں۔
ڈیٹا بیس ریڈز لائیو رہتے ہیں جب کہ ٹیسٹ کسی بھی بعد کے وقفوں کو کنٹرول کرتا ہے۔
حتمی دلیل کے دستاویزات جان بوجھ کر غلط نتائج دیتے ہیں۔
لٹل لیمبڈا ایک غیر محفوظ فنکشن کو کال کرتا ہے اور ہر کارکن کے لیے بنائے گئے ریکارڈ کا شناخت کنندہ واپس کرتا ہے۔ ان دونوں کے واپس آنے کے بعد refresh_from_db() اپنا اکاؤنٹ دوبارہ لوڈ کریں۔ دعویٰ یہ ہے کہ اس الگ تھلگ ٹیسٹ کا نتیجہ صفر توازن اور دو نسلوں کا ریکارڈ ہوگا۔
یہاں سے گزرنا بگ پنروتپادن کو یقینی بناتا ہے، غیر محفوظ افعال کی درستگی کو نہیں۔
محفوظ اختتامی پوائنٹس کی تصدیق کریں۔
اسی کلاس میں درج ذیل طریقہ شامل کریں:
def concurrent_api_requests(self):
ready = Barrier(2)
def send_request():
client = APIClient()
client.force_authenticate(self.user)
ready.wait(timeout=5)
return client.post(
"/api/generate/",
{"prompt": "A garden"},
format="json",
).status_code
return self.run_two(send_request)
def test_last_credit_accepts_only_one_request(self):
self.assertEqual(sorted(self.concurrent_api_requests()), [201, 409])
self.account.refresh_from_db()
self.assertEqual(self.account.balance, 0)
self.assertEqual(Generation.objects.count(), 1)
def test_two_credits_accept_both_requests(self):
self.account.balance = 2
self.account.save(update_fields=["balance"])
self.assertEqual(self.concurrent_api_requests(), [201, 201])
self.account.refresh_from_db()
self.assertEqual(self.account.balance, 0)
self.assertEqual(Generation.objects.count(), 2)
درخواست کا مددگار محفوظ منظر کے ذریعے دو تصدیق شدہ درخواستیں بھیجتا ہے۔
ہر کارکن اپنی تخلیق کرتا ہے۔APIClientاور force_authenticate() پاس ورڈ کے تبادلے کے بغیر اسے صارفین کی جانچ کے لیے فراہم کریں۔ ایک دیوار سامنے ہے۔ post() دونوں کارکن ایک ساتھ شروع کی درخواست تک پہنچ جاتے ہیں۔ ہر کال کو جوابی کوڈ واپس کرتا ہے: run_two().
یہ تصدیق کے طریقہ کار کی جانچ کیے بغیر اختتامی نقطہ کے اعتماد کے رویے کی جانچ کرتا ہے۔
دونوں ٹیسٹ کے طریقے ناکافی اور کافی مشترکہ کریڈٹس کو حل کرتے ہیں۔
ایک کریڈٹ ٹیسٹ کوڈ کو سیدھا کرتا ہے کیونکہ یہ توقع کرتا ہے کہ کارکنان میں سے ایک اسے پہلے مکمل کرے گا اور پھر ایک۔ 201 اور ایک 409. یہ اس بات کو بھی یقینی بناتا ہے کہ آپ کے بقیہ کریڈٹس 0 ہیں اور یہ کہ آپ کے پاس ریکارڈز کی بالکل 1 نسل ہے۔ ایک 2-کریڈٹ ٹیسٹ آپ کے ابتدائی بیلنس کو تبدیل کرتا ہے اور 2 کامیابیوں، 2 ہٹس، اور 0 کے بیلنس کی توقع کرتا ہے۔
آپ کے ریکارڈ اور جوابات کی جانچ پڑتال آپ کو ان گھرانوں کی شناخت کرنے میں مدد کرے گی جنہیں غلط طریقے سے منظور کیا گیا تھا۔
بیریئر کوآرڈینیٹ کی درخواست بغیر کسی ڈیٹا بیس کی کارروائیوں کو کنٹرول کیے شروع کی جاتی ہے۔
ایک درخواست دوسری سے زیادہ تیزی سے آگے بڑھ سکتی ہے۔ تالے کو حاصل کرنے کے بعد رکاوٹ کو منتقل کرنے سے پہلے کارکن کو دوسرے کارکن کا انتظار کرنا پڑتا ہے، جو تالا حاصل نہیں کر سکتا۔ موجودہ انتظام مصنوعی تعطل کو روکتا ہے، لیکن عمل درآمد کے تمام ممکنہ احکامات کو ثابت نہیں کرتا۔
اس ریگریشن ٹیسٹ کو کنٹرولڈ ری پروڈکشن اور دستاویزی ڈیٹا بیس کے رویے کے ساتھ استعمال کریں۔
اس بات کا تعین کریں کہ کیا سروس کو زیر التواء لاک کا سامنا کرنا پڑا
آپ بیلنس چیک کرنے سے پہلے یہ بھی جانچ سکتے ہیں کہ آیا شیڈولنگ فنکشن لاک کا انتظار کر رہا ہے۔
نیچے دیے گئے ٹیسٹ صفر کریڈٹ کے ساتھ شروع ہوتے ہیں، لہذا بیلنس چیک کرتے وقت غیر محفوظ ریڈز کو فوری طور پر مسترد کر دیا جاتا ہے۔ ڈیفالٹ کنکشن ورکر کو شروع کرنے سے پہلے اکاؤنٹ لاک آؤٹ حاصل کرتا ہے۔ وہ کارکن 1 سیکنڈ کے لاک ٹائم آؤٹ کے ساتھ اصل شیڈولنگ فنکشن کو کال کرتا ہے۔
اس طریقہ کو اندر شامل کریں: ConcurrencyTests:
def test_reservation_waits_for_account_lock(self):
self.account.balance = 0
self.account.save(update_fields=["balance"])
user_id = self.user.pk
def attempt_reservation():
close_old_connections()
try:
try:
with transaction.atomic():
with connection.cursor() as cursor:
cursor.execute("SET LOCAL lock_timeout="1s"")
reserve_generation(user_id, "A garden")
except OperationalError as error:
return error.__cause__.sqlstate
except InsufficientCredits:
return "insufficient_credits"
return "accepted"
finally:
connections.close_all()
with ThreadPoolExecutor(max_workers=1) as pool:
with transaction.atomic():
CreditAccount.objects.select_for_update().get(pk=self.account.pk)
result = pool.submit(attempt_reservation).result(timeout=5)
self.assertEqual(result, "55P03")
result = pool.submit(attempt_reservation).result(timeout=5)
self.assertEqual(result, "insufficient_credits")
self.account.refresh_from_db()
self.assertEqual(self.account.balance, 0)
self.assertEqual(Generation.objects.count(), 0)
یہ ٹیسٹ کنٹرول کرتا ہے جب کوئی کنٹینشن لاک موجود ہو۔
کارکن کے نتائج کا انتظار کرتے ہوئے بنیادی کنکشن لین دین کو کھلا رکھتا ہے۔ کارکن کے لین دین کے اندر، SET LOCAL عارضی طور پر لاک ٹائم آؤٹ کو مختصر کرتا ہے۔ کارکن کو PostgreSQL واپس کرنا ہوگا۔ 55P03 ایرر کوڈ کا مطلب ہے: lock_not_availableتالے کے بعد انتظار کا وقت ختم ہو گیا۔
اس مقام پر بیلنس میں کمی اس بات کی نشاندہی کرتی ہے کہ سروس نے تصدیق سے پہلے آپ کے اکاؤنٹ کے لاک ہونے کا انتظار نہیں کیا۔
بنیادی لین دین کے لاک کو جاری کرنے کے بعد دوسری کال اسی آپریشن کی تصدیق کرتی ہے۔
اس بار کارکن اکاؤنٹ لاک حاصل کر سکتا ہے اور زیرو بیلنس پڑھ سکتا ہے۔ واپس کرنا ضروری ہے insufficient_credits حتمی دعوی، جو کہ ڈیٹا بیس کی غلطی نہیں ہے، یہ ہے کہ دونوں کوششوں کے درمیان کوئی کریڈٹ یا گھریلو ریکارڈ تبدیل نہیں کیا گیا۔
دو کالیں تنازعات کی جانچ کرتی ہیں اور دو اوور لیپنگ درخواستوں پر انحصار کیے بغیر ایک ساتھ رہا کرتی ہیں۔
صفر بیلنس جان بوجھ کر ہے اور ٹیسٹ کو مزید مخصوص بناتا ہے۔
اگر ایک کریڈٹ استعمال ہو چکا ہے، تب بھی ایک لاک ہو سکتا ہے جب غیر محفوظ فنکشن بالآخر قطار کو اپ ڈیٹ کرنے کی کوشش کرتا ہے۔ اگر کریڈٹ 0 ہے، تو فنکشن اپ ڈیٹ سے پہلے درخواست کو مسترد کر دیتا ہے اور متوقع ٹائم آؤٹ دعوے کو ناکام بنا دیتا ہے۔ لہٰذا ٹیسٹ سروس کے پہلے سے تصدیق شدہ لاکنگ رویے کی تصدیق کرتا ہے، جبکہ پچھلا اینڈ پوائنٹ ٹیسٹ اخراجات کے نتائج کی تصدیق کرتا ہے۔
یہ کنٹرول شدہ لاکنگ ٹیسٹ ایک ساتھی پروجیکٹ کے PostgreSQL ٹیسٹ سوٹ کے حصے کے طور پر پاس کیا گیا تھا۔
ناکامی کے بعد رول بیک چیک کریں۔
باہر ConcurrencyTests ایک اور کلاس شامل کریں:
class CreditRollbackTests(TransactionTestCase):
def test_failed_record_creation_restores_credit(self):
user = get_user_model().objects.create_user(username="rollback-reader")
account = CreditAccount.objects.create(user=user, balance=1)
with patch(
"credits.services.Generation.objects.create",
side_effect=RuntimeError("Simulated record creation failure"),
):
with self.assertRaises(RuntimeError):
reserve_generation(user.pk, "A garden")
account.refresh_from_db()
self.assertEqual(account.balance, 1)
self.assertEqual(Generation.objects.count(), 0)
تخلیق ریکارڈ کی تخلیق کے دوران یہ ٹیسٹ جان بوجھ کر ایک استثناء پھینکتا ہے۔
پہلے، 1 کریڈٹ کے ساتھ ایک اکاؤنٹ بنائیں۔ پیچ بناتے ہیں Generation.objects.create() اٹھانا RuntimeErrorاور assertRaises() تصدیق کریں کہ ریزرویشن فنکشن خرابی کی وجہ سے ختم ہو گیا ہے۔ ٹرانزیکشن کو رول بیک کرنے کے بعد، ریفریش شدہ اکاؤنٹ میں اب بھی 1 کریڈٹ ہونا چاہیے اور تخلیق کی تاریخ نہیں ہونی چاہیے۔
یہ اس بات کو یقینی بناتا ہے کہ ناکام بکنگ کی وجہ سے کوئی کٹوتی باقی نہیں رہتی ہے۔
PostgreSQL کا استعمال کرتے ہوئے اپنے ٹیسٹ چلائیں، جو اب بھی دستیاب ہے۔
python manage.py test credits -v 2
یہ کمانڈ اس پر ٹیسٹ چلاتی ہے: credits ایپ
-v 2 ہم Django سے ہر ایک ٹیسٹ اور اس کے نتائج دکھانے کو کہتے ہیں۔ جینگو ایک الگ ٹیسٹ ڈیٹا بیس بناتا ہے، اس لیے ڈیٹا بیس صارف کو اسے بنانے کے لیے اجازت درکار ہوتی ہے۔ مقامی کنٹینر میں صارفین کو یہ اجازتیں حاصل ہوں گی، لیکن موجودہ ڈیٹا بیس کی ترتیبات کو کنفیگریشن کی ضرورت ہو سکتی ہے۔
مطلوبہ PostgreSQL سیٹ اپ کے لیے پانچ پاسز متوقع ہیں۔ براہ کرم چلائیں اور ان پر بھروسہ کرنے سے پہلے مثالوں کو چیک کریں۔
عام غلطیاں اور اگلے اقدامات
اب، یہ مثال آپ کے کریڈٹ فیصلوں کی حفاظت کرتی ہے، لیکن جب آپ اسے لاگو کرتے ہیں، تو کچھ تفصیلات سے محروم رہنا آسان ہے۔
لاک کے اندر موجود اکاؤنٹ کو پڑھیں
اگر آپ ٹرانزیکشن سے پہلے اپنا اکاؤنٹ درآمد کرتے ہیں اور اس چیز کو استعمال کرنا جاری رکھتے ہیں، تو آپ کے پاس باسی بیلنس رہ سکتے ہیں۔ محفوظ خصوصیات جان بوجھ کر اکاؤنٹس کا پتہ لگاتی ہیں بذریعہ: select_for_update() فیصلہ کرنے سے پہلے.
اپنے کوڈ کو دوسری سروسز یا اینڈ پوائنٹ پر منتقل کرتے وقت اس ترتیب کو برقرار رکھیں۔ صارف کے شناخت کنندہ کو پاس کرنے سے فنکشن خود سے نئے لاکڈ ریڈ کا چارج لے لیتا ہے۔
ڈیٹا بیس کو مشترکہ کوآرڈینیشن پوائنٹ کے طور پر استعمال کریں۔
جمع کروائیں بٹن کو غیر فعال کرنے سے حادثاتی کلکس کم ہو سکتے ہیں، لیکن پھر بھی دوسرے ٹیب یا دوسرے کلائنٹس سے درخواستیں بھیج سکتے ہیں۔ Python تھریڈ لاکنگ صرف کوآرڈینیٹ کوڈ کرتا ہے جو اسی عمل میں لاک کو شیئر کرتا ہے۔
متعدد ایپلیکیشن ورکرز چلاتے وقت، کریڈٹ کے فیصلوں کو اب بھی مشترکہ ڈیٹا بیس میں محفوظ کرنے کی ضرورت ہوتی ہے۔ یہ ماننے کے بجائے کہ یہ اختتامی نقطہ واحد مصنف ہے، انتظامی ایڈجسٹمنٹ سمیت دیگر بیلنس اپ ڈیٹس کا جائزہ لیں۔
آسان کاؤنٹرز کے لیے مشروط اپ ڈیٹس پر غور کریں۔
قطار بند کرنا ایک طریقہ ہے۔ سادہ استدلال کے لیے، جینگو ایک ڈیٹا بیس اپ ڈیٹ میں بیلنس کی حالت اور گھٹاؤ کا بھی اظہار کر سکتا ہے: F() اظہار
حالات اہم ہیں۔ غیر مشروط گھٹاؤ کافی کریڈٹ لاگو نہیں ہوتا ہے۔ اگر آپ تخلیق کا ریکارڈ بھی بناتے ہیں تو کٹوتی اور ریکارڈ تخلیق کو ایک لین دین کے طور پر رکھیں۔
پڑھنے، فیصلوں اور اپ ڈیٹس کو آسان بنانے کے لیے ہم نے یہاں واضح لاکنگ کا استعمال کیا۔ ایک بار جب آپ یہ سمجھ لیں کہ آپ کے کام کو کس چیز کی حفاظت کی ضرورت ہے، آپ مشروط اپ ڈیٹس کو دریافت کر سکتے ہیں۔
ہم آہنگی کی درخواستیں اور دوبارہ کوششیں الگ کریں۔
ہماری مثال میں، ہم دو گذارشات کو دو مختلف درخواستوں کے طور پر پروسیس کرتے ہیں۔ اگر صارف کے پاس دو کریڈٹ ہیں، تو دونوں کو کامیاب ہونا چاہیے۔
دوبارہ کوششوں پر مختلف تقاضے لاگو ہوتے ہیں۔ ایپ تخلیق کو قبول کرے گی، لیکن کلائنٹ کے موصول ہونے سے پہلے جواب ضائع ہو سکتا ہے۔ اگر کلائنٹ وہی منطقی درخواست دوبارہ بھیجتا ہے، تو اصل نتیجہ بغیر کسی اضافی قیمت کے واپس کیا جا سکتا ہے۔
یہ ضروری ہے بے حسی: دہرائی جانے والی کارروائیوں کی نشاندہی کرنے اور ان کے اثرات کو دوبارہ لاگو ہونے سے روکنے کا ایک طریقہ۔ ایک عام ڈیزائن درخواست کی کلید، ڈیٹا بیس کی انفرادیت کے اصول، اور ذخیرہ شدہ نتائج کا استعمال کرتا ہے۔ اکیلے بیلنس لاکنگ ڈپلیکیٹ ارادے کی شناخت نہیں کر سکتا۔
سپلائر کی ناکامی کے لیے منصوبہ بندی کرنا
ایک بار آپ کی بکنگ کی تصدیق ہو جانے کے بعد، فراہم کنندہ کی ناکامیوں کی وجہ سے آپ کا کریڈٹ خود بخود بحال نہیں ہوگا۔ آپ کو اس بارے میں پالیسی کی ضرورت ہے کہ آیا تخلیق کی دوبارہ کوشش کرنی ہے، اسے واپس کرنا ہے، یا اسے بازیابی کے لیے روکنا ہے۔
اگر درخواست کو کریڈٹ ریزرو کرنے کے بعد لیکن کام شروع کرنے سے پہلے روک دیا جاتا ہے تو ایک عملی نفاذ بھی بحال ہونا چاہیے۔ کہ reserved تاریخ کچھ ایسا فراہم کرتی ہے جس سے باخبر رہیں، لیکن یہ ٹیوٹوریل پائیدار کام کی قطاروں یا بحالی کے کارکنوں کو لاگو نہیں کرتا ہے۔
جب آپ مثال کو بڑھاتے ہیں تو ان خدشات کو مرئی رکھیں۔ ایک ساتھ زیادہ خرچ کرنے سے بچنا ایک مکمل کریڈٹ سسٹم کا حصہ ہے۔
خلاصہ اور اگلے اقدامات
اس گائیڈ کے آغاز پر، دونوں درخواستیں ہر ایک پاس بیلنس چیک کریں گی اور ایک ہی کریڈٹ استعمال کریں گی۔ ناکامیوں کو نظر انداز کرنا آسان تھا کیونکہ بیلنس 0 پر ختم ہوا۔
ہم نے ترتیب کو دوبارہ تیار کیا اور پھر اپنے فیصلوں کی حفاظت کے لیے لین دین اور قطار کے تالے کا استعمال کیا۔ ہم نے جنریشنز کی تعداد بھی چیک کی، ٹیسٹ کیا جب دونوں درخواستوں میں کافی کریڈٹس تھے، اور ریکارڈ بنانے میں ناکامی کے لیے رول بیک ٹیسٹ شامل کیا۔
اپنی درخواست میں اسی طرح کی فعالیت کا جائزہ لیتے وقت:
-
اعداد و شمار کو پورا کرنے کے اصولوں کی وضاحت کرتا ہے، جیسے کہ 1 کریڈٹ فی گھرانہ کی اجازت ہے۔
-
شناخت کرتا ہے کہ جہاں علیحدہ درخواستیں ایک ہی قدر کو پڑھ اور تبدیل کر سکتی ہیں۔
-
فیصلوں اور متعلقہ ڈیٹا بیس کی تبدیلیوں کی حفاظت کریں۔
-
حقیقی دنیا کے ڈیٹا بیس میں بے کار آپریشنز اور غلطیوں کے لیے ٹیسٹ کریں۔
یہی استدلال آپ کے آن لائن اسٹور میں آخری لمحات کی اشیاء یا اشتراک الاؤنسز پر لاگو ہوتا ہے۔ ایک کامیاب تصدیق صرف اس صورت میں مفید ہے جب درخواست محفوظ طریقے سے کارروائی کر سکے۔
حوالہ جات
دوبارہ کوششوں کے بارے میں مزید جاننے کے لیے، idempotent API کا استعمال کرتے ہوئے محفوظ دوبارہ کوششیں بنانا دیکھیں۔