If-Match میتواند ویرایش را به نسخهای وابسته کند که ویرایشگر واقعاً دیده است. اگر دو نفر یک وضعیت را بخوانند و هر دو نسخهٔ کاملِ تغییرکرده را بدونِ شرط بنویسند،ویرایشِ دوم ممکن است تغییرِ اول را بیصدا پاک کند. در این تمرین،همین lost updateرا ایجاد میکنیم و سپس با مقایسه و نوشتنِ اتمی،نسخهٔ قدیمی را بدونِ اثرِ تازه رد میکنیم.
دو ویرایشگر،یک snapshotِ اولیه
منبعِ ساختگی دو فیلد دارد:owner=unassignedوnote=initial. هر دو ویرایشگرAوBهمین بدنه وETagِ قویِ "v1"را میخوانند. Aمالک را agent-Aمیکند؛ Bیادداشت را عوض میکند اما نسخهٔ کاملِ قدیمیِ خود را برای نوشتن دارد. هیچیک دادهٔ مشتری یا اقدامِ واقعی نیستند.
در مسیرِ بدونِ precondition،نوشتنِ Aانجام و بعد بدنهٔ Bجایگزین میشود. ownerدوبارهunassignedاست؛ تغییرِ Aاز دست رفته،هرچند دو دستورِ نوشتن موفق بودهاند. ترتیبِ آموزشی را پشتِ سرِ هم اجرا میکنیم؛ آزمایشِ همزمانیِ شبکه یا تراکنشِ پایگاه انجام نشده است.
بررسیِ نسخه و نوشتن باید یک مرز داشته باشند
در مسیرِ محافظتشده،یک lockِ محلی هم مقایسهٔ tagو هم تعویضِ بدنه و افزایشِ نسخه را در یک ناحیه نگه میدارد. اینکه ابتدا نسخه را بخوانیم،قفل را رها کنیم و بعد بنویسیم،همان تضمین را نمیدهد؛ نوشتنِ دیگری میتواند میان آن دو بیفتد. قفلِ این نمونه فقط در همان فرایندِ Pythonاست.
Aبا "v1"مینویسد و منبع به "v2"میرود. Bهنوز "v1"را میفرستد؛ قراردادِ endpointِ فرضی پاسخِ۴۱۲وapplied=falseمیدهد. کد snapshotِ قبل و بعد از این رد را کامل برابر میخواهد؛نه بدنه عوض شده و نه نسخه جلو رفته است.
| مرحله | نسخهٔ همراه | نتیجهٔ تمرین |
|---|---|---|
| نوشتنِ A | v1 | ۲۰۰؛ownerِ تازه،نسخهv2 |
| نوشتنِ قدیمیِ B | v1 | ۴۱۲؛بدونِ هیچ تغییر |
| خواندنِ دوباره و تصمیمِ B | v2 | حفظِ ownerِ Aو تغییرِ آگاهانهٔnote؛نسخهv3 |
۴۱۲را کورکورانه با tagِ تازه تکرار نکنیم
Bپس از رد،منبعِ جاری را دوباره میخواند و صریحاً تصمیم میگیرد ownerِ Aرا حفظ و فقطnoteرا عوض کند. این تصمیمِ دستسازِ همین سناریوست؛ برنامه راهحلِ عمومیِ mergeِ تعارض را کشف نکرده است. چسباندنِ tagِ تازه به بدنهٔ قدیمی،میتواند همان overwriteرا با ظاهری مجاز تکرار کند.
برای فرمِ واقعی،اختلافِ فیلدها و امکانِ بازبینی باید معلوم باشد. شاید تغییرِ دو کاربر قابلِ جمع نباشد و نیاز به انتخابِ مسئول داشته باشد. ردِ نسخهٔ قدیمی شکستِ کنترل نیست؛شاهدِ پیداشدنِ تعارض پیش از پاکشدنِ بیصدایِ تغییرِ دیگری است.
ETagِ قوی و دامنهٔ محدودِ مثال
RFC9110برای If-Matchمقایسهٔ قوی را مقرر میکند. کدِ ما فقط یک tagِقوی با شکلِ vوعدد را در گیومه میپذیرد؛ ورودیِ ضعیفِ W/"v3"را در قراردادِ محلی رد میکند. wildcard،فهرستِ tagها و دستورِ زبانِ کاملِ سرآیند پیاده نشدهاند. ETagاین مثال شمارهٔ نسخهٔ کلِ همین بدنه است،نه hashِ رمزنگاریشده یا مجوز.
همچنین نمیگوییم استاندارد برای هر شرطِ نادرست بیاستثنا۴۱۲میخواهد. RFCدر وضعیتِ تشخیصِ اعمالشدنِ قبلیِ همان تغییر،پاسخِ موفقِ مجاز را هم توضیح میدهد. endpointِ آموزشیِ ما آن مسیر را اجرا نمیکند و برای staleversionهمیشه۴۱۲میدهد؛این یک قراردادِ محدود است.
مجوز،تکرار و کش هنوز جدا هستند
کلیدِ idempotency به retryِ همان اقدام مربوط است؛ If-Matchاینجا تعارضِ دو ویرایشِ مبتنی بر نسخهٔ قبلی را میگیرد. داشتنِ نسخهٔ درست نشان نمیدهد کاربر اجازهٔ ویرایش دارد. احراز،اعتبارسنجی و قواعدِ کسبوکار باید مستقل برقرار باشند.
ابطالِ کش هم دربارهٔ تازگی و محدودهٔ استفاده از نتیجه است؛این تمرین کشِ پاسخ نمیسازد. حتی اگر داده از کش آمده باشد،مسیرِ نوشتن باید نسخهٔ منبعِ معتبر و دامنهٔ اختیار را درست کنترل کند. یک tagِ موجود در متنِ مدل،بهتنهایی شاهدِ قابلِ اعتمادِ نسخه نیست.
تمرین را پیش از اتصال ببینیم
فایلِ Pythonفقط کتابخانهٔ استاندارد دارد و هیچHTTPserver،درخواستِ شبکه یا CRMرا اجرا نمیکند. دو مسیرِ بدونِ شرط و محافظتشده جدا هستند؛در JSONمیتوانید بدنهٔ ازدسترفته،ردِ بیاثر و نسخهٔ نهاییِ تصمیمِ آگاهانه را ببینید. قفلِ یک فرایند،راهحلِ آمادهٔ چند سرور یا پایگاهِ توزیعشده نیست.
برای بررسیِ اتصال در راهکارهای اپرا،پشتیبانیِ واقعیِ کنترلِ نسخه و رفتارِ خطایِ مقصد را از تیمِ اجرا بخواهید. وجودِ If-Matchیا همین endpointدر APIاپرا ادعا نشده است؛خروجیِ این راهنما معیاری برای سؤال و آزمونِ فنیِ مشخص است.
فایل تمرین و منبع
کدِ کاملِ محلی و کنترلهای اصیلِ تمرین · دادهٔ ساختگی و خروجیِ اجرای واقعی
منابع در 2026-11-04 خوانده شدند؛ این تاریخ، تاریخ انتشار منبع نیست. مثالها و فایلهای این راهنما آموزشی و ساختهٔ تحریریهاند.