مواد
یہ PSQL یا دیگر اسی طرح کے SQL (SQLite, MySQL، وغیرہ) پر مبنی ڈیٹا بیس (DBs) کے ساتھ Django چلانے والی ایپس کے لیے ہے۔ لین دین کے بارے میں بنیادی اصول دوسرے DBs پر لاگو نہیں ہوسکتے ہیں۔ ہڈ کے نیچے، Django کا لین دین کا انتظام کوڈ مندرجہ ذیل کام کرتا ہے (یہ Django کے doc سے کاپی کیا گیا ہے - وضاحت کے لیے میرے اپنے بلٹ پوائنٹس کے ساتھ نیچے سکرول کریں):
DB سے کنکشن حاصل کرتا ہے یا بناتا ہے اور اسے لین دین کے طور پر سیٹ کرتا ہے۔
تمام DB آپریشنز کو تالے کی ضرورت ہوتی ہے — کچھ تالے ڈھیلے ہوتے ہیں اور دوسرے کنکشنز کو اسی وسائل پر آپریشن کرنے کی اجازت دیتے ہیں، لیکن دوسرے تالے زیادہ سخت ہوتے ہیں۔ وہ خصوصی ہیں اور پڑھنے اور لکھنے دونوں کاموں کو روک دیں گے۔ ریکارڈز بنانے، اپ ڈیٹ کرنے اور حذف کرنے کے ساتھ ساتھ جدولوں اور کالموں کو تبدیل کرنے سے مزید سخت تالے حاصل ہوں گے جو دوسرے کنکشنز کو آپریشن کرنے سے روک دیں گے۔ یہ تالے لین دین کے اختتام تک رکھے جاتے ہیں۔
یہ خود بخود ہو جاتا ہے جب، مثال کے طور پر، ریکارڈز کو ٹیبل میں داخل کرنا — کچھ ماڈل آپریشنز خود بخود خود کو ایٹم بلاکس میں لپیٹ لیں گے۔
یہ دستی طور پر کسی ایسے فنکشن کو کال کرکے کیا جا سکتا ہے جسے اٹامک() کے ذریعے لپیٹ دیا گیا ہو یا اٹامک() کا استعمال کرتے ہوئے بیان کے ساتھ (ایک سیاق و سباق کے مینیجر کے طور پر)۔
Savepoints وہی کنکشن استعمال کرے گا جو بیرونی لین دین کی طرح ہوتا ہے — اگر آپ سیو پوائنٹس کے لیے Django کوڈ کو دیکھیں تو یہ ایک اور ٹرانزیکشن بنانے کے لیے صرف ایک تکراری کال ہے۔
سیو پوائنٹس کو سیو پوائنٹس کے اندر اندر بنایا جا سکتا ہے - ایک اسٹیک سیو پوائنٹس کو ٹریک کرتا ہے۔
داخل کرنے کے بعد، سیو پوائنٹ کو یا تو تبدیلیاں کر کے اور باقی ٹرانزیکشن (لیکن دوسرے کنکشنز نہیں) کے لیے دستیاب کر کے یا تبدیلیوں کو واپس کر کے ہٹا دیا جاتا ہے۔
DB پر تالے ابھی بھی رکھے گئے ہیں جب کہ نان DB منطق پر عمل کیا جاتا ہے، مثلاً، اگر ہم کسی لین دین کے باڈی میں HTTP درخواست بھیجتے ہیں، تو تالے اس وقت تک رکھے جائیں گے جب تک کہ وہ درخواست نہیں بھیجی جاتی اور جواب واپس نہیں آ جاتا۔ جب ہم DB آپریشنز کو انجام دینا بند کر دیتے ہیں تو Django تالے جاری نہیں کرتا ہے۔ بیرونی لین دین ختم ہونے تک یہ تالے رکھتا ہے۔
نیسٹڈ ٹرانزیکشنز میں حاصل کیے گئے تالے بھی اس وقت تک رکھے جاتے ہیں جب تک کہ بیرونی ٹرانزیکشن مکمل نہ ہو جائے یا نیسٹڈ ٹرانزیکشن واپس نہ کر دی جائے۔
لین دین روک دیا جاتا ہے، واپس لوٹا جاتا ہے، اور تالے جاری کیے جاتے ہیں۔
اگر غلطی نیسٹڈ ٹرانزیکشن میں ہوتی ہے تو بیرونی لین دین متاثر نہیں ہوتا ہے۔ رول بیک ہوتا ہے لیکن بیرونی لین دین بلاتعطل جاری رہ سکتا ہے (جب تک کہ ابھری ہوئی غلطی کو پکڑا اور سنبھال لیا جائے)۔
لین دین غلطیاں نہیں پکڑتے ہیں، لیکن وہ ان پر انحصار کرتے ہیں تاکہ یہ جان سکیں کہ تبدیلیوں کو کب واپس لانا ہے۔ اس لیے، ڈی بی آپریشنز کو کوشش/سوائے میں لپیٹ نہ کریں، اس کے بجائے، انہیں ٹرانزیکشن میں لپیٹیں اور پھر ٹرانزیکشن کو ٹرائی/سوائے میں لپیٹیں۔ ایسا کرنے سے DB آپریشن ناکام ہوجاتا ہے اور بیرونی لین دین کو متاثر کیے بغیر خود بخود واپس چلا جاتا ہے۔
آخر میں، تمام لین دین کا عہد کیا جاتا ہے، تبدیلیاں تمام DB صارفین/ کنکشنز کے لیے دستیاب ہوتی ہیں۔ یا اسے واپس موڑ دیا جاتا ہے، ڈی بی کو اسی حالت میں چھوڑ دیا جاتا ہے جیسا کہ لین دین کا سامنا کرنے کے وقت تھا۔
اگلے بلاگ میں، میں اس کے بارے میں لکھوں گا کہ آپ کو لین دین میں کیا کرنا چاہیے یا نہیں کرنا چاہیے تاکہ لاک بھوک کی وجہ سے ہونے والی بندش سے بچا جا سکے۔
لین دین وہ طریقہ کار ہیں جو ACID DB's (PSQL, SQL, latest MongoDB ، وغیرہ) کو ATOMIC (ACID میں A) ہونے کی اجازت دیتے ہیں۔ اس کا مطلب ہے کہ DB کو لکھے گئے تمام خطوط ایک ہی آپریشن کے طور پر کیے جاتے ہیں - چاہے کتنا ہی پیچیدہ ہو۔ اگر تحریر کامیاب ہو جاتی ہے تو، DB میں تمام تبدیلیاں برقرار رہتی ہیں اور بیک وقت تمام کنکشنز کے لیے دستیاب ہوتی ہیں (کوئی جزوی نہیں لکھتا)۔ اگر تحریر ناکام ہو جاتی ہے تو، تمام تبدیلیاں واپس کر دی جاتی ہیں — دوبارہ، کوئی جزوی تبدیلیاں نہیں ہیں۔
لین دین ان اصولوں کی ضمانت دیتا ہے:
یہ قواعد DB کے صارف کو پیچیدہ آپریشنز کو ایک ساتھ باندھنے اور انہیں ایک آپریشن کے طور پر انجام دینے کی اجازت دیتے ہیں۔ مثال کے طور پر، ایک بینک اکاؤنٹ سے دوسرے اکاؤنٹ میں منتقلی پر عمل درآمد؛ اگر ہمارے پاس لین دین نہیں تھا، تو ان دو تحریری کارروائیوں کے بیچ میں، واپسی یا یہاں تک کہ ایک قریبی اکاؤنٹ آپریشن ہوسکتا ہے جو منتقلی کو غلط بنا دیتا ہے۔ لین دین صارف کو ٹریفک کنٹرول کی کسی نہ کسی شکل کی اجازت دیتا ہے۔ ٹرانزیکشن جاری ہونے کے دوران ہم دیگر تمام متضاد کارروائیوں کو روک سکتے ہیں۔
آپریشن میزوں اور قطاروں پر تالے کی ایک سیریز کے ذریعے مسدود ہیں۔ PSQL اور دیگر SQL متغیرات میں، لین دین BEGIN کے ساتھ بنائے جاتے ہیں۔ پھر تالے حاصل کیے جاتے ہیں جب ایک آپریشن جیسا کہ Select/insert/delete/alter چلایا جاتا ہے اور لین دین COMMIT یا ROLLBACK کے ساتھ ختم ہوتا ہے۔ جب COMMIT یا ROLLBACK عمل میں لایا جاتا ہے تو تالے جاری کیے جاتے ہیں۔ خوش قسمتی سے، جینگو ہمیں ان تین بیانات کو استعمال کیے بغیر لین دین کرنے کی اجازت دیتا ہے (لیکن ہمیں ابھی بھی تالے کے بارے میں فکر کرنے کی ضرورت ہے؛ اس پر مزید اگلی پوسٹ میں)۔
-- Start a new transaction BEGIN; SELECT … INSERT INTO … UPDATE … DELETE FROM … -- Save the changes COMMIT;
from django.db import transaction from app.core.models import Accounts, Fee, NotificationJob def do_migration(): overdrawn_accounts = Accounts.objects.filter(type='overdrawn') for acount in overdrawn_accounts: create_notification(acount) @transaction.atomic def create_notification(acount: Accounts): # $5 fee for overdraft - 500 because we never store money as float!!! recall = Fee.objects.create(acount=acount, description='Fee for overdraft', amount=500) NotificationJob.objects.create(recall=recall, notification_type='all') acount.status = 'awaiting_payment' acount.save() def do_migration2(): overdawn_account = Accounts.objects.filter(type='overdrawn') for account in overdawn_account: with transaction.atomic(): recall = Fee.objects.create(acount=account, description='Fee for overdraft', amount=500) NotificationJob.objects.create(recall=recall, notification_type='all') account.status = 'awaiting_payment' account.save()
جب آٹوکومٹ موڈ میں چلتا ہے تو Django ہر DB آپریشن کو خود بخود ہمارے لیے لین دین میں لپیٹ دیتا ہے (نیچے اس پر مزید)۔ ایٹم() ڈیکوریٹر یا سیاق و سباق کے مینیجر (ایٹم() کے ساتھ استعمال کرتے ہوئے واضح لین دین کی جا سکتی ہے — جب ایٹم() استعمال کیا جاتا ہے، انفرادی DB آپریشنز کسی لین دین میں لپیٹے نہیں جاتے ہیں یا DB کے ساتھ فوری طور پر پابند نہیں ہوتے ہیں (ان پر عمل درآمد کیا جاتا ہے، لیکن DB میں تبدیلیاں دوسرے صارفین کو نظر نہیں آتی ہیں)۔ اس کے بجائے، پورے فنکشن کو ٹرانزیکشن بلاک میں لپیٹ دیا جاتا ہے اور آخر میں ارتکاب کیا جاتا ہے (اگر کوئی غلطی نہیں کی جاتی ہے) — COMMIT کو عمل میں لایا جاتا ہے۔
اگر غلطیاں ہوں تو، DB تبدیلیاں واپس کر دی جاتی ہیں — ROLLBACK کو عمل میں لایا جاتا ہے۔ یہی وجہ ہے کہ لین دین آخر تک تالے میں ہی رہتا ہے۔ ہم کسی ٹیبل پر لاک جاری نہیں کرنا چاہتے، کوئی دوسرا کنکشن ٹیبل کو تبدیل کریں یا اسے پڑھیں، اور پھر جو کچھ تبدیل یا پڑھا گیا ہے اسے واپس کرنے پر مجبور کیا جائے۔
ANGRY_CUSTOMER_THRESHOLD = 3 @transaction.atomic def create_notification(acount: Accounts): recall = Fee.objects.create(acount=acount, description='Fee for overdraft', amount=500) NotificationJob.objects.create(recall=recall, notification_type='all') try: # when this completes successfully, the changes will be available to the outer transaction with transaction.atomic(): owner = acount.owner fees = Fee.objects.filter(owner=owner).count() if fees >= ANGRY_CUSTOMER_THRESHOLD: for fee in fees: fee.amount = 0 fee.save() owner.status = 'angry' owner.save() # as long as we catch the error, the outer transaction will not be rolled back except Exception as e: logger.error(f'Error while removings fees for account {acount.id}: {e}') acount.status = 'awaiting_payment' acount.save()
ہم کسی دوسرے فنکشن کو کال کرکے یا لین دین کے اندر سیاق و سباق کے مینیجر کا استعمال کرتے ہوئے لین دین کو نیسٹ کرسکتے ہیں۔ اس سے ہمیں محفوظ پوائنٹس بنانے کی اجازت ملتی ہے جہاں ہم باقی لین دین کو متاثر کیے بغیر پرخطر کارروائیوں کی کوشش کر سکتے ہیں — جب کسی اندرونی لین دین کا پتہ چل جاتا ہے، ایک سیو پوائنٹ بنایا جاتا ہے، اندرونی لین دین پر عمل درآمد ہوتا ہے، اور اگر اندرونی لین دین ناکام ہو جاتا ہے، ڈیٹا کو واپس محفوظ کرنے والے مقام پر واپس لایا جاتا ہے اور بیرونی لین دین جاری رہتا ہے۔ اگر بیرونی لین دین ناکام ہو جاتا ہے تو، تمام اندرونی لین دین بیرونی لین دین کے ساتھ واپس کر دیے جاتے ہیں۔ Durable=True in atomic() کو ترتیب دے کر ٹرانزیکشن نیسٹنگ کو روکا جا سکتا ہے - اگر کسی اندرونی لین دین کا پتہ چلا تو یہ لین دین RuntimeError کو بڑھا دے گا۔
نیسٹڈ ٹرانزیکشنز کے بارے میں آپ کو کیا یاد رکھنے کی ضرورت ہے:
DATABASES = { 'default': { # True by default 'AUTOCOMMIT': False, # ...rest of configs } }
ڈیفالٹ کے طور پر، Django آٹوکومٹ موڈ میں چلتا ہے، یعنی ہر DB آپریشن کو ٹرانزیکشن میں لپیٹ دیا جاتا ہے اور فوری طور پر چلایا جاتا ہے (پُرعزم، اس لیے آٹوکمیٹ) سوائے اس کے کہ صارف کے ذریعے کسی لین دین میں آپریشن کو واضح طور پر لپیٹ دیا گیا ہو۔ یہ بنیادی DB کی لین دین کی منطق کو مبہم کر دیتا ہے — کچھ DBs، جیسے PSQL، خود بخود تمام کارروائیوں کو لین دین میں سمیٹ دیتے ہیں جبکہ دوسرے ایسا نہیں کرتے۔ اگر ہمارے پاس Django ہمارے لیے ایسا کر رہا ہے، تو ہمیں بنیادی DB منطق کے بارے میں فکر کرنے کی ضرورت نہیں ہے۔
ہم آٹوکمیٹ موڈ کو بند کر سکتے ہیں، لیکن اس سے ہمیں آپریشنز کو منظم کرنے اور یہ یقینی بنانے کے لیے DB پر انحصار کرنا پڑے گا کہ وہ ATOMIC ہیں، جو ڈویلپرز کے لیے خطرناک اور تکلیف دہ ہے۔ یہ مزہ نہیں آئے گا اگر، ایک API بناتے وقت، ہمیں یہ معلوم کرنا پڑے کہ کون سے رائٹ آپریشنز لین دین میں ہیں اور اس لیے، کل لکھا جائے گا نہ کہ جزوی طور پر۔
تاہم، اگر ہمیں DB پر کنکشنز کی تعداد اور ان کے انجام دینے والے آپریشنز کی ترتیب معلوم ہو تو ہم آٹوکمیٹ موڈ کو بند کرنا چاہیں گے۔ اگر ہم یہ جان لیتے ہیں کہ ان آپریشنز کو کیسے ڈی کنفلیکٹ کیا جائے، تو ہم آٹو کمٹ موڈ کو آف کر سکتے ہیں اور کچھ افادیت حاصل کر سکتے ہیں:
منفی پہلو ڈیٹا بدعنوانی کے زیادہ خطرات ہیں۔ یہ اس کے قابل نہیں ہے، لیکن ہوسکتا ہے کہ استعمال کا کوئی معاملہ ہو جس کے بارے میں میں سوچ بھی نہیں سکتا۔