មាតិកា
នេះគឺសម្រាប់កម្មវិធីដែលដំណើរការ Django ជាមួយ PSQL ឬ SQL ស្រដៀងគ្នាផ្សេងទៀត (SQLite, MySQL ។ល។) ដែលមានមូលដ្ឋានលើមូលដ្ឋានទិន្នន័យ (DBs); ច្បាប់មូលដ្ឋានជុំវិញប្រតិបត្តិការអាចមិនអនុវត្តចំពោះ DBs ផ្សេងទៀត។ នៅក្រោមក្រណាត់ លេខកូដគ្រប់គ្រងប្រតិបត្តិការរបស់ Django ធ្វើដូចខាងក្រោម (នេះត្រូវបានចម្លងពី ឯកសារ របស់ Django - រមូរចុះក្រោម- ជាមួយនឹងចំណុចគ្រាប់កាំភ្លើងផ្ទាល់ខ្លួនរបស់ខ្ញុំសម្រាប់ការបញ្ជាក់):
ទទួល ឬបង្កើតការតភ្ជាប់ទៅ DB ហើយកំណត់វាជាប្រតិបត្តិការ។
រាល់ប្រតិបត្តិការរបស់ DB ទាមទារការចាក់សោ — ការចាក់សោរមួយចំនួនគឺរលុង ហើយនឹងអនុញ្ញាតឱ្យការតភ្ជាប់ផ្សេងទៀតដើម្បីប្រតិបត្តិប្រតិបត្តិការនៅលើធនធានដូចគ្នា ប៉ុន្តែការចាក់សោផ្សេងទៀតមានភាពតឹងរ៉ឹងជាង។ ពួកវាគឺផ្តាច់មុខ ហើយនឹងរារាំងទាំងប្រតិបត្តិការអាន និងសរសេរ។ ការបង្កើត ការអាប់ដេត និងការលុបកំណត់ត្រា ក៏ដូចជាការផ្លាស់ប្តូរតារាង និងជួរឈរ នឹងទទួលបានការចាក់សោដ៏តឹងរ៉ឹងជាងមុន ដែលនឹងរារាំងការតភ្ជាប់ផ្សេងទៀតពីការប្រតិបត្តិប្រតិបត្តិការ។ ការចាក់សោទាំងនេះត្រូវបានរក្សាទុករហូតដល់ចុងបញ្ចប់នៃប្រតិបត្តិការ។
វាត្រូវបានធ្វើដោយស្វ័យប្រវត្តិនៅពេលដែលឧទាហរណ៍ ការបញ្ចូលកំណត់ត្រាទៅក្នុងតារាង — ប្រតិបត្តិការគំរូមួយចំនួននឹងរុំដោយស្វ័យប្រវត្តិនៅក្នុងប្លុកអាតូមិក។
នេះអាចត្រូវបានធ្វើដោយដៃដោយហៅមុខងារមួយដែលត្រូវបានរុំដោយអាតូមិក() ឬប្រើអាតូមិក() នៅក្នុងសេចក្តីថ្លែងការណ៍មួយ (ជាអ្នកគ្រប់គ្រងបរិបទ)។
Savepoints នឹងប្រើការតភ្ជាប់ដូចគ្នាទៅនឹងប្រតិបត្តិការខាងក្រៅ — ប្រសិនបើអ្នកក្រឡេកមើលលេខកូដ Django សម្រាប់ savepoints វាគ្រាន់តែជាការហៅសារឡើងវិញដើម្បីបង្កើតប្រតិបត្តិការមួយផ្សេងទៀត។
Savepoints អាចត្រូវបានដាក់នៅក្នុង savepoints — ជង់រក្សាដាននៃ savepoints។
បន្ទាប់ពីការបញ្ចូល ចំណុចរក្សាទុកត្រូវបានដកចេញដោយការធ្វើការផ្លាស់ប្តូរ និងធ្វើឱ្យវាមានសម្រាប់ប្រតិបត្តិការដែលនៅសល់ (ប៉ុន្តែមិនមែនជាការតភ្ជាប់ផ្សេងទៀត) ឬដោយការត្រឡប់ការផ្លាស់ប្តូរវិញ។
ការចាក់សោនៅលើ DB នៅតែត្រូវបានរក្សាខណៈពេលដែលតក្កវិជ្ជាដែលមិនមែនជា DB ត្រូវបានប្រតិបត្តិឧទាហរណ៍ ប្រសិនបើយើងផ្ញើសំណើ HTTP នៅក្នុងតួនៃប្រតិបត្តិការ នោះការចាក់សោនឹងត្រូវបានរក្សាទុករហូតដល់សំណើនោះត្រូវបានផ្ញើ ហើយការឆ្លើយតបនឹងត្រលប់មកវិញ។ Django មិនបញ្ចេញសោនៅពេលយើងឈប់ដំណើរការប្រតិបត្តិការ DB ។ វារក្សាសោរហូតដល់ប្រតិបត្តិការខាងក្រៅត្រូវបានបញ្ចប់។
ការចាក់សោរដែលទទួលបាននៅក្នុងប្រតិបត្តិការដែលបានដាក់ជាប់គ្នាក៏ត្រូវបានរក្សារហូតដល់ប្រតិបត្តិការខាងក្រៅត្រូវបានបញ្ចប់ ឬប្រតិបត្តិការដែលជាប់គាំងត្រូវបានរំកិលត្រឡប់មកវិញ។
ប្រតិបត្តិការត្រូវបានបញ្ឈប់ វិលត្រឡប់មកវិញ ហើយសោត្រូវបានដោះលែង។
ប្រសិនបើមានបញ្ហាកើតឡើងក្នុងប្រតិបត្តិការដែលជាប់គ្នា នោះប្រតិបត្តិការខាងក្រៅមិនប៉ះពាល់ទេ។ ការត្រលប់មកវិញកើតឡើង ប៉ុន្តែប្រតិបត្តិការខាងក្រៅអាចបន្តដោយគ្មានការរំខាន (ដរាបណាកំហុសដែលបានលើកឡើងត្រូវបានចាប់ និងដោះស្រាយ)។
ប្រតិបត្តិការមិនចាប់កំហុសទេ ប៉ុន្តែវាអាស្រ័យទៅលើពួកគេ ដើម្បីដឹងថាពេលណាត្រូវបង្វិលការផ្លាស់ប្តូរឡើងវិញ។ ដូច្នេះ ចូរកុំរុំប្រតិបត្តិការ DB នៅក្នុងការសាកល្បង/លើកលែងតែ ផ្ទុយទៅវិញ រុំពួកវាក្នុងប្រតិបត្តិការមួយ ហើយបន្ទាប់មករុំប្រតិបត្តិការដោយសាកល្បង/លើកលែងតែ។ ការធ្វើដូច្នេះអនុញ្ញាតឱ្យប្រតិបត្តិការ DB បរាជ័យ និងត្រូវបានបង្វិលត្រឡប់មកវិញដោយស្វ័យប្រវត្តិដោយមិនប៉ះពាល់ដល់ប្រតិបត្តិការខាងក្រៅ។
ជាចុងក្រោយ ប្រតិបត្តិការទាំងមូលត្រូវបានប្តេជ្ញា ធ្វើឱ្យការផ្លាស់ប្តូរមានសម្រាប់អ្នកប្រើប្រាស់/ការតភ្ជាប់ DB ទាំងអស់។ ឬវាត្រូវបានរំកិលត្រឡប់មកវិញដោយទុកឱ្យ DB ស្ថិតក្នុងស្ថានភាពដូចពេលដែលប្រតិបត្តិការត្រូវបានជួបប្រទះ។
នៅក្នុងប្លុកបន្ទាប់ ខ្ញុំនឹងសរសេរអំពីអ្វីដែលអ្នកគួរ ឬមិនគួរដាក់ក្នុងប្រតិបត្តិការ ដើម្បីជៀសវាងការដាច់ភ្លើងដែលបណ្តាលមកពីការអត់ឃ្លាន។
ប្រតិបត្តិការគឺជាយន្តការដែលអនុញ្ញាតឱ្យ ACID DBs (PSQL, SQL, MongoDB ចុងក្រោយបង្អស់ ។ នេះមានន័យថារាល់ការសរសេរទៅកាន់ DB ត្រូវបានធ្វើជាប្រតិបត្តិការតែមួយ - មិនថាស្មុគស្មាញយ៉ាងណានោះទេ។ ប្រសិនបើការសរសេរជោគជ័យ ការផ្លាស់ប្តូរទាំងអស់ចំពោះ DB នៅតែបន្ត ហើយមានសម្រាប់ការតភ្ជាប់ទាំងអស់ក្នុងពេលដំណាលគ្នា (មិនមានការសរសេរដោយផ្នែក)។ ប្រសិនបើការសរសេរបរាជ័យ ការផ្លាស់ប្តូរទាំងអស់នឹងត្រលប់មកវិញ — ជាថ្មីម្តងទៀត មិនមានការផ្លាស់ប្តូរផ្នែកណាមួយឡើយ។
ប្រតិបត្តិការធានានូវច្បាប់ទាំងនេះ៖
ច្បាប់ទាំងនេះអនុញ្ញាតឱ្យអ្នកប្រើ DB បាច់ប្រតិបត្តិការស្មុគស្មាញជាមួយគ្នា ហើយប្រតិបត្តិវាជាប្រតិបត្តិការតែមួយ។ ឧទាហរណ៍ ប្រតិបត្តិការផ្ទេរប្រាក់ពីគណនីធនាគារមួយទៅគណនីធនាគារមួយទៀត។ ប្រសិនបើយើងមិនមានប្រតិបត្តិការទេនោះ នៅពាក់កណ្តាលនៃប្រតិបត្តិការសរសេរទាំងពីរនេះ វាអាចមានការដកប្រាក់ ឬសូម្បីតែប្រតិបត្តិការគណនីបិទដែលធ្វើឱ្យការផ្ទេរមិនត្រឹមត្រូវ។ ប្រតិបត្តិការអនុញ្ញាតឱ្យអ្នកប្រើទម្រង់មួយចំនួននៃការគ្រប់គ្រងចរាចរណ៍។ យើងអាចបិទរាល់ប្រតិបត្តិការដែលមានជម្លោះផ្សេងទៀត ខណៈប្រតិបត្តិការកំពុងដំណើរការ។
ប្រតិបត្តិការត្រូវបានរារាំងតាមរយៈការចាក់សោរជាបន្តបន្ទាប់នៅលើតុ និងជួរ។ នៅក្នុង PSQL និងវ៉ារ្យ៉ង់ SQL ផ្សេងទៀត ប្រតិបត្តិការត្រូវបានបង្កើតជាមួយ BEGIN; បន្ទាប់មកការចាក់សោត្រូវបានទទួលនៅពេលដែលប្រតិបត្តិការដូចជាជ្រើសរើស/បញ្ចូល/លុប/ផ្លាស់ប្តូរត្រូវបានដំណើរការ ហើយប្រតិបត្តិការបញ្ចប់ដោយ COMMIT ឬ ROLLBACK ។ ការចាក់សោត្រូវបានបញ្ចេញនៅពេលដែល COMMIT ឬ ROLLBACK ត្រូវបានប្រតិបត្តិ។ ជាសំណាងល្អ Django អនុញ្ញាតឱ្យយើងបង្កើតប្រតិបត្តិការដោយមិនចាំបាច់ប្រើសេចក្តីថ្លែងការណ៍ទាំងបីនេះ (ប៉ុន្តែយើងនៅតែត្រូវព្រួយបារម្ភអំពីការចាក់សោ។ បន្ថែមទៀតនៅលើវានៅក្នុងការប្រកាសបន្ទាប់) ។
-- 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 ដោយស្វ័យប្រវត្តិនៅក្នុង ប្រតិបត្តិការ មួយសម្រាប់ពួកយើង នៅពេលដំណើរការក្នុងរបៀប autocommit (បន្ថែមលើវាខាងក្រោម)។ ប្រតិបត្តិការច្បាស់លាស់ អាចត្រូវបានបង្កើតដោយប្រើ atomic() decorator ឬ context manager (with atomic()) — នៅពេលដែល atomic() ត្រូវបានប្រើ ប្រតិបត្តិការ 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()
យើងក៏អាចដាក់សំបុកប្រតិបត្តិការដោយហៅមុខងារផ្សេងទៀតដែលត្រូវបានរុំដោយ atomic() ឬប្រើកម្មវិធីគ្រប់គ្រងបរិបទក្នុងប្រតិបត្តិការមួយ។ នេះអនុញ្ញាតឱ្យយើងបង្កើតចំណុចរក្សាទុក ដែលយើងអាចព្យាយាមប្រតិបត្តិការប្រកបដោយហានិភ័យដោយមិនប៉ះពាល់ដល់ប្រតិបត្តិការដែលនៅសល់ - នៅពេលដែលប្រតិបត្តិការខាងក្នុងត្រូវបានរកឃើញ ចំណុចរក្សាទុកត្រូវបានបង្កើត ប្រតិបត្តិការខាងក្នុងត្រូវបានប្រតិបត្តិ ហើយប្រសិនបើប្រតិបត្តិការខាងក្នុងបរាជ័យ ទិន្នន័យនឹងវិលត្រលប់ទៅចំណុចរក្សាទុកវិញ ហើយប្រតិបត្តិការខាងក្រៅនៅតែបន្ត។ ប្រសិនបើប្រតិបត្តិការខាងក្រៅបរាជ័យ ប្រតិបត្តិការខាងក្នុងទាំងអស់នឹងវិលត្រឡប់មកវិញ រួមជាមួយនឹងប្រតិបត្តិការខាងក្រៅ។ ការដាក់សំបុកប្រតិបត្តិការអាចត្រូវបានរារាំងដោយការកំណត់ជាប់លាប់=ពិតនៅក្នុងអាតូមិក() — វានឹងធ្វើឱ្យប្រតិបត្តិការកើនឡើង RuntimeError ប្រសិនបើប្រតិបត្តិការខាងក្នុងណាមួយត្រូវបានរកឃើញ។
អ្វីដែលអ្នកត្រូវចងចាំអំពីប្រតិបត្តិការដែលជាប់គាំង៖
DATABASES = { 'default': { # True by default 'AUTOCOMMIT': False, # ...rest of configs } }
តាមលំនាំដើម Django ដំណើរការក្នុងរបៀប autocommit មានន័យថារាល់ប្រតិបត្តិការ DB ត្រូវបានរុំក្នុងប្រតិបត្តិការ ហើយដំណើរការភ្លាមៗ (បានប្តេជ្ញា ដូច្នេះ autocommit) លើកលែងតែប្រតិបត្តិការត្រូវបានរុំយ៉ាងច្បាស់លាស់នៅក្នុងប្រតិបត្តិការដោយអ្នកប្រើប្រាស់។ នេះធ្វើឱ្យរំខានដល់តក្កវិជ្ជាប្រតិបត្តិការនៃ DB មូលដ្ឋាន — DBs មួយចំនួនដូចជា PSQL រុំប្រតិបត្តិការទាំងអស់ដោយស្វ័យប្រវត្តិក្នុងប្រតិបត្តិការ ខណៈដែលអ្នកផ្សេងទៀតមិនមាន។ ប្រសិនបើយើងមាន Django ធ្វើវាសម្រាប់យើង យើងមិនចាំបាច់ព្រួយបារម្ភអំពីតក្កវិជ្ជា DB មូលដ្ឋាននោះទេ។
យើងអាចបិទមុខងារ autocommit ប៉ុន្តែវានឹងទុកឱ្យយើងពឹងផ្អែកលើ DB ដើម្បីគ្រប់គ្រងប្រតិបត្តិការ និងធានាថាពួកវាជា ATOMIC ដែលប្រថុយប្រថាន និងរអាក់រអួលសម្រាប់អ្នកអភិវឌ្ឍន៍។ វានឹងមិនរីករាយទេ ប្រសិនបើនៅពេលបង្កើត API យើងត្រូវរកឱ្យឃើញថាតើប្រតិបត្តិការសរសេរណាមួយនៅក្នុងប្រតិបត្តិការ ហើយដូច្នេះវានឹងត្រូវបានសរសេរសរុប និងមិនមែនដោយផ្នែក។
ទោះជាយ៉ាងណាក៏ដោយ យើងប្រហែលជាចង់ បិទមុខងារ autocommit ប្រសិនបើយើងដឹងពីចំនួននៃការតភ្ជាប់នៅលើ DB និងលំដាប់នៃប្រតិបត្តិការដែលពួកគេកំពុងប្រតិបត្តិ។ ប្រសិនបើយើងរកវិធីបំបាត់ជម្លោះទាំងនេះ នោះយើងអាចបិទមុខងារស្វ័យប្រវត្តិ និងទទួលបានប្រសិទ្ធភាពមួយចំនួន៖
គុណវិបត្តិគឺហានិភ័យខ្ពស់នៃអំពើពុករលួយទិន្នន័យ។ វាមិនមានតម្លៃទេ ប៉ុន្តែប្រហែលជាមានករណីប្រើប្រាស់ដែលខ្ញុំគិតមិនដល់។