SAVEPOINT در SQLite اجازه میدهد بخشی از کارِ یک تراکنش را برگردانیم، بدون اینکه الزاماً همهٔ تراکنش لغو شود. اما RELEASEِ نقطهٔ داخلی، تضمینِ ثبتِ پایدارِ مستقل نیست؛ rollbackِ والد هنوز میتواند آن کار را پاک کند. این تمرین با یک ورودیِ ثابت، ورودِ «همه یا هیچ» و پذیرشِ ردیفهای سالم را جدا اجرا میکند.
ورودی را بین دو سیاست عوض نکنیم
چهار ردیفِ آموزشی شناسههای۱تا۴دارند. codeهای آنها A،B،AوDاست. جدول برای codeقیدِ UNIQUEوNOT NULLدارد؛ تکرارِ Aدر ردیفِ۳عمداً خطاست. این قید در زمانِ همان INSERTبررسی میشود؛ از قیدِ خارجیِ deferredکه خطایش تاCOMMITعقب میافتد استفاده نکردهایم.
همهٔ اتصالها با :memory:ساخته میشوند. isolation_level=Noneرا صریح تنظیم کردهایم و BEGIN،COMMITوROLLBACKرا خودِ کد میفرستد؛ به آغازِ ضمنیِ تراکنشِ یک نسخهٔ Pythonتکیه نمیکنیم. نسخهٔ SQLiteدر نتیجه ثبت میشود. هیچ فایل، مشتری یا CRMواقعی واردِ آزمون نشده است.
سیاستِ اول: خطایِ یک ردیف، برگشتِ کلِ گروه
در اتصالِ اول BEGINمیزنیم و ردیفها را به همان ترتیب وارد میکنیم. شناسههای۱و۲موقتاً در تراکنش هستند؛ درجِ۳بهدلیلِ Aتکراری شکست میخورد. handlerِ نمونه ROLLBACKِ کل را اجرا میکند. در پایان جدول خالی است؛ پذیرفتهشدنِ دو دستورِ اول را با ثبتِ نهاییِ دو ردیف اشتباه نمیگیریم.
بعد از خطایِ سوم، ردیفِ۴در این سیاست اصلاً امتحان نمیشود. خروجی first_bad_id=۳وfinal_ids=[]است. اینکه خطا در ردیفِ سوم اتفاق افتاده، بهتنهایی نمیگوید دو ردیفِ پیشین باقی ماندهاند؛ باید مرزِ تراکنش و دستورِ handler را هم بخوانیم.
سیاستِ دوم: نقطهٔ برگشت برای هر ردیف
در اتصالِ دوم یک BEGINوالد داریم. قبل از هر ردیف SAVEPOINT one_rowساخته میشود. اگر INSERTموفق باشد، آن نقطه RELEASEمیشود و ردیف در تراکنشِ بازِ والد باقی میماند. اگر خطایِ قید برسد، ROLLBACK TO one_rowاثرِ بعد از نقطه را برمیگرداند و سپس RELEASEنقطه را جمع میکند.
برای ردیفِ۳،دو ROLLBACK TOپشتِ سرِ هم واقعاً اجرا میکنیم؛ دومی هم موفق است، چون دستورِ اول خودِ نقطهٔ همنام را از بین نبرده است. این آزمایش برای توضیحِ معنایِ نقطه است، نه توصیهٔ اجرای دوبارهٔ بیهدف. پس از RELEASE،ردیفِ۴هم بررسی میشود و در پایانِ والد COMMITداریم.
| سیاستِ تمرین | رفتارِ ردیفِ۳ | شناسههای نهایی |
|---|---|---|
| همه یا هیچ | خطا،rollbackوالد | هیچ |
| savepointهر ردیف | ردِ همان ردیف،ادامهٔ۴ | ۱،۲،۴پس ازcommitوالد |
RELEASEِ داخلی هنوز commitِ مستقل نیست
اتصالِ سوم همین نکته را جدا میآزماید: BEGIN،سپسSAVEPOINT inner،درجِ شناسهٔ۹وRELEASE inner. ردیفِ۹در همان اتصال دیده میشود و تراکنشِ والد هنوز باز است. بعد ROLLBACKِ والد اجرا میشود و جدول خالی میماند. پاکشدنِ ردیف واقعاً assert شده است، نه صرفاً در شرحِ متنی آمده باشد.
چون پایگاه حافظهای است، حتی COMMITِ مثال را آزمایشِ دوام روی دیسک، قطعِ برق یا crashنمینامیم. معنایِ RELEASEبرای outermost savepointبدونِ BEGINوالد هم میتواند مانندcommitباشد؛ اینجا عمداً BEGINوالد را حفظ کردهایم تا موضوعِ نقطهٔ داخلی روشن بماند.
خطایِ داده با تکرارِ درخواست یک مسئله نیست
این نسخه سیاستِ واحدِ rollbackرا نشان میدهد؛ deduplicationِ درخواست، مجوزِ ویرایش و بازیابیِ قطعِ شبکه در آن نیست. اگر یک گروه را دوباره اجرا کنید، شناسهها و قیودِ مقصد باید با قراردادِ مستقل مدیریت شوند. خروجیِ «رد شد» را هم با حذفِ سابقهٔ خطا یکی نگیرید؛ دفترِ نمونه شناسه و وضعیتِ هر ردیف را نگه میدارد.
پیشبررسیِ CSV میپرسد سامانه کلِ فایل یا ردیف را رد میکند؛ اینجا اثرِ دقیقِ دو سیاست در تراکنش اجرا شده است. رکورد و منبع در پایگاهِ برداری اتصالِ شناسه را میسنجد و بهجایِ مرزِ rollbackنیست.
در تحویلِ اتصال چه چیزی را مشخص کنیم؟
پیش از ورودِ واقعی، واحدِ پذیرش، رفتارِ خطا، دفترِ ردیفهای ردشده و امکانِ برگشت را با مسئولِ سامانه تعیین کنید. انتخابِ پذیرشِ جزئی همیشه بهتر نیست؛ بعضی گروهها باید با هم معتبر بمانند. فایلِ تمرین فقط رفتارِ همان چهار ردیفِ ساختگی را نشان میدهد و دستورِ تغییرِ دادهٔ موجود نیست.
برای بررسیِ اتصال در راهکارهای اپرا، همین قراردادِ خطا و رسیدِ نهایی را مطرح کنید. معماریِ تراکنشِ داخلیِ اپرا، استفاده ازSQLiteیا ورودِ خودکارِ داده از این مثال نتیجه نمیشود؛ هدف، روشنکردنِ انتظار از تیمِ اجراست.
فایل تمرین و منبع
کدِ کاملِ محلی و کنترلهای اصیلِ تمرین · دادهٔ ساختگی و خروجیِ اجرای واقعی
منابع در 2026-11-04 خوانده شدند؛ این تاریخ، تاریخ انتشار منبع نیست. مثالها و فایلهای این راهنما آموزشی و ساختهٔ تحریریهاند.