وقتی از یک عملیات ساده که شامل یک سری مراحل متوالی است استفاده میکنید، الگوی تراکنش جبرانی (Compensating Transaction) میتواند مفید باشد. به طور خاص، اگر یک یا چند مرحله با شکست مواجه شد آنگاه میتوانید از الگوی Compensating Transaction برای بازگشت یا توقف به تسکهای انجام شده در هر مرحله استفاده کنید. بهطورکلی، با کمک این الگو شما عملیاتی را پیادهسازی میکنید که از مدل یکپارچگی تدریجی (eventual consistency) جهت اپلیکیشنهای مبتنی بر محیط ابری پیروی میکند که فرایندها و گردشهای کاری پیچیده را پیادهسازی میکنند.
برنامههایی که در فضای ابری اجرا میشوند اغلب دادهها را تغییر میدهند. این دادهها گاهی اوقات در منابع مختلف داده در مکانهای جغرافیایی مختلف پخش میشوند. برای جلوگیری از تداخل و بهبود کارایی در یک محیط توزیع شده، یک برنامه نباید سعی کند پایداری تراکنشهای قوی(strong transactional consistency) را ارائه دهد. در عوض، برنامه باید یکپارچگی تدریجی(eventual consistency) را اجرا کند. در مدل یکپارچگی تدریجی، یک عملیات تجاری معمولی شامل یک سری مراحل جداگانه است. درحالیکه این عملیات حال انجام است، نمای کلی وضعیت سیستم ممکن است ناپایدار باشد. اما وقتی عملیات تمام شد و تمام مراحل اجرا شدند، سیستم باید دوباره پایدار شود.
مورد Data Consistency Primer اطلاعاتی در مورد اینکه چرا تراکنشهای توزیع شده بهخوبی مقیاس (scale) نمیشوند، ارائه میدهد. این منبع همچنین اصول مدل یکپارچگی تدریجی (eventual consistency model) را بیان میکند.
یک چالش در مدل یکپارچگی تدریجی(eventual consistency model) این است که چگونه مرحلهای را که شکست میخورد مدیریت کنیم. پس از شکست، ممکن است لازم باشد تمام کارهایی را که مراحل قبلی در حین اجرای عملیات انجام دادهاند را لغو کنید. بااینحال، همیشه نمیتوانید دادهها را به عقب برگردانید، زیرا سایر نمونههای همزمان برنامه ممکن است آن را تغییر داده باشند. حتی در مواردی که نمونههای همزمان دادهها را تغییر ندادهاند، لغو یک مرحله ممکن است پیچیدهتر از بازگرداندن حالت اولیه باشد. ممکن است لازم باشد قوانین مختلفی برای یک مدل تجاری خاص اعمال شود. برای مثال، به مورد «وبسایت مسافرتی» که در بخش مثال در ادامه این مقاله توضیح میدهد، مراجعه کنید.
اگر عملیاتی که یکپارچگی تدریجی (eventual consistency) را اجرا میکند که چندین data store غیریکسان را در بر میگیرد، لغو مراحل در عملیات مستلزم بررسی از هر data store به نوبه خود است. برای جلوگیری از ناپایدار ماندن سیستم، باید کاری را که در هر data store انجام دادهاید، به طور قابلاعتمادی لغو و undo کنید.
دادههایی که تحتتأثیر این نوع عملیاتی قرار گرفته باهدف اینکه یکپارچگی تدریجی را پیادهسازی کند به طور معمول در یک پایگاهداده نگهداری نمیشوند. بهعنوانمثال، یک محیط معماری سرویسگرا (service-oriented architecture (SOA)) را در نظر بگیرید. یک عملیات SOA میتواند یک عمل را در یک سرویس فراخوانی کند و باعث تغییر در وضعیتی شود که توسط آن سرویس نگهداری میشود. برای لغو عملیات، باید این تغییر حالت را نیز لغو کنید. این فرایند میتواند شامل فراخوانی مجدد سرویس و انجام عمل دیگری باشد که اثرات اولین عملیات را معکوس میکند.
راهحل اجرای یک تراکنش جبرانی (Compensating Transaction) است. مراحل یک تراکنش جبرانی، اثرات مراحل دیگر در عملیات اصلی را خنثی میکند. یک رویکرد شهودی جایگزینی وضعیت فعلی با وضعیتی است که سیستم در شروع عملیات در آن قرار داشت. اما یک تراکنش جبرانکننده همیشه نمیتواند این رویکرد را داشته باشد؛ زیرا ممکن است تغییراتی را که سایر نمونههای همزمان (concurrent instances) یک برنامه ایجاد کردهاند را بازنویسی کند. در عوض، یک تراکنش جبرانی باید یک فرایند هوشمند باشد که هر کاری را که نمونههای همزمان انجام میدهند در نظر بگیرد. این فرایند معمولاً به یک برنامه خاص بستگی دارد و به دلیل ماهیت کاری که عملیات اصلی انجام میدهد، هدایت میشود.
یک رویکرد متداول استفاده از یک گردش کار (workflow) برای اجرای یک عملیات در یکپارچگی تدریجی (eventual consistency) است که نیاز به جبران دارد. همانطور که عملیات اصلی پیش میرود، سیستم اطلاعات مربوط به هر مرحله را ثبت میکند، از جمله نحوه لغو شدن تسک ای که هر مرحله انجام میدهد. اگر عملیات در هر نقطهای با شکست مواجه شود، گردش کار طی مراحلی که انجام داده است به عقب برمیگردد. در هر مرحله، گردش کار (workflow) کاری را انجام میدهد که آن مرحله را معکوس میکند.
دو نکته مهم عبارتاند از:
-
یک تراکنش جبرانی ممکن است مجبور نباشد کار را دقیقاً به ترتیب معکوس عملیات اصلی، لغو یا undo کند.
-
ممکن است بتوان برخی از مراحل واگرد و undo را بهصورت موازی انجام داد.
این رویکرد شبیه به راهبُرد Saga است که در وبلاگ [Clemens Vasters' blog] (https://vasters.com/archive/Sagas.html) موردبحث قرار گرفته است.
یک تراکنش جبرانی بهخودیخود نیز یک عملیات پایدار احتمالی است، بنابراین احتمال شکستخوردن آن وجود دارد. سیستم باید بتواند تراکنش جبرانی را در نقطه شکست ازسرگرفته و ادامه دهد. ممکن است لازم باشد مرحلهای را تکرار کنید که ناموفق است، بنابراین باید مراحل یک تراکنش جبرانی را بهعنوان دستورات ناتوان (idempotent commands) (@@@این رویکرد شامل طراحی دستوراتی با ناتوانی ذاتی است و اطمینان حاصل میکند که اجرای چندباره یک فرمان تأثیر اضافی پس از اولین اجرا ندارد. تأکید بر ایجاد دستوراتی است که مستقل از وضعیت فعلی سیستم هستند.) تعریف کنید. برای اطلاعات بیشتر، الگوهای Idempotency Patterns را در وبلاگ Jonathan Oliver ببینید.
در برخی موارد، مداخله به شیوه دستی ممکن است تنها راه بازیابی از مرحلهای باشد که شکستخورده است. در این شرایط، سیستم باید یک هشدار یا alert داده و تاحدامکان اطلاعات بیشتری در مورد دلیل خرابی، گزارش ارائه دهد
هنگام تصمیم گیری در مورد نحوه اجرای این الگو به نکات زیر توجه کنید:
-
تشخیص اینکه چه زمانی یک مرحله از عملیاتی که یکپارچگی تدریجی (eventual consistency) را اجرا میکند، ممکن است آسان نباشد. یک مرحله ممکن است فوراً شکست نخورد. در عوض، ممکن است مسدود یا بلاک شود. ممکن است لازم باشد مکانیزمی را برای time-out اجرا کنید.
-
تعمیمدادن منطق جبرانساز(compensation logic) کار آسانی نیست. یک تراکنش جبرانی معمولاً یک ویژگی مربوط به برنامه است. در واقع این مورد وابسته به این است که برنامه دارای اطلاعات کافی بوده که بتواند اثرات هر مرحله در یک عملیات ناموفق را لغو و undo کند.
-
شما باید مراحل یک تراکنش جبرانی را بهعنوان دستورات ناتوان (idempotent commands) تعریف کنید. اگر این کار را انجام دهید، در صورت شکست و fail شدن تراکنش جبرانکننده، میتوان مراحل عملیاتی را تکرار کرد.
-
زیرساختی که مراحل را انجام میدهد باید معیارهای زیر را داشته باشد:
- در عملیات اصلی و در تراکنش جبرانی انعطافپذیر باشد.
- اطلاعات لازم برای جبرانسازی یک مرحله شکستخورده را از دست ندهد.
- به طور قابلاعتمادی پیشرفت منطق جبرانساز(compensation logic) را نظارت کند.
-
یک تراکنش جبرانی لزوماً دادههای سیستم را به وضعیت اولیه خودش در شروع عملیات اصلی بر نمیگرداند. در عوض، تراکنش جبرانساز برای این کار است که عملیات با موفقیت به پایان برسد قبل از اینکه به شکست بخورد.
-
ترتیب مراحل در تراکنش جبرانی لزوماً و دقیقاً برعکس مراحل عملیات اصلی نیست. بهعنوانمثال، یک data store ممکن است نسبت به دیگری نسبت به ناهماهنگی حساستر باشد. مراحل تراکنش جبرانی که تغییرات این data store را واگرد یا undo میکند باید ابتدا انجام شود.
-
اقدامات خاصی میتواند به افزایش احتمال موفقیت کلی عملیات کمک کند. به طور خاص، میتوانید قفل کوتاهمدت و مبتنی بر تایم اوت (short-term, time-out–based lock) را روی هر منبعی که برای تکمیل یک عملیات لازم است، قرار دهید. همچنین میتوانید این منابع را از قبل رزرو کنید. سپس، کار را فقط پس از بهدستآوردن تمام منابع لازم برای شروع عملیات انجام دهید. تمام اقدامات را قبل از منقضی شدن قفلها نهایی کنید.
-
تلاش مجدد یا Retry به شیوهای که کمککنندهتر از حد معمول است میتواند بهحداقلرساندن شکستهایی که باعث اجاری تراکنش جبرانی میشود کمک کند. اگر مرحلهای از عملیاتی که یکپارچگی تدریجی را اجرا میکند با شکست مواجه شد، سعی کنید شکست را بهعنوان یک استثنا گذرا مدیریت کنید و مرحله را تکرار کنید. همینطور بهتر است در شرایطی عملیات را متوقف کنید و یک تراکنش جبرانی را شروع کنید که یک مرحله مکرراً با شکست مواجه شود یا نمیتوان آن را بازیابی کرد.
-
هنگامی که یک تراکنش را پیادهسازی میکنید، با بسیاری از چالشهای مشابهی روبرو میشوید که هنگام اجرای یکپارچگی تدریجی(eventual consistency) با آن مواجه میشوید. برای کسب اطلاعات بیشتر، به بخش 'ملاحظات برای اجرای یکپارچگی تدریجی' در Data Consistency Primer مراجعه کنید.
از این الگو فقط برای عملیاتی استفاده کنید که در صورت شکست باید لغو یا undo شوند. در صورت امکان، راهحلهایی طراحی کنید تا از پیچیدگی نیاز به تراکنشهای جبرانی جلوگیری شود.
مشتریان از یک وبسایت مربوط به سفر برای رزرو برنامههای مسافرتی استفاده میکنند. یک برنامه مسافرتی ممکن است شامل یک سری پرواز و هتل باشد. مشتری که از سیاتل به لندن و سپس به پاریس سفر میکند، ممکن است مراحل زیر را هنگام ایجاد یک برنامه سفر انجام دهد:
۱- در پرواز F۱ از سیاتل به لندن صندلی رزرو کنید. ۲- در پرواز F۲ از لندن به پاریس صندلی رزرو کنید. ۳- در پرواز F۳ از پاریس به سیاتل صندلی رزرو کنید. ۴- اتاقی را در هتل H۱ لندن رزرو کنید. ۵- رزرو اتاق در هتل H۲ در پاریس.
این مراحل یک عملیات در پایدار احتمالی(compensating transaction) را تشکیل میدهند، اگرچه هر مرحله یک عمل جداگانه است. علاوه بر انجام این مراحل، سیستم باید شمارنده عملیات را نیز برای لغو هر مرحله ثبت کند. درصورتیکه مشتری برنامه مسافرتی را لغو کند به این اطلاعات نیاز است. مراحلی که برای انجام شمارنده عملیات لازم است میتوانند بهعنوان یک تراکنش جبرانی اجرا شوند.
ممکن است مراحل تراکنش جبرانساز دقیقاً برعکس مراحل اولیه نباشد. همچنین، منطق هر مرحله در تراکنش جبرانی باید قوانین خاص کسبوکار خود را در نظر بگیرد. بهعنوانمثال، لغو رزرو پرواز ممکن است به مشتری حق بازپرداخت کامل را نداشته باشد.
شکل زیر مراحل یک تراکنش طولانیمدت برای رزرو برنامه مسافرتی را نشان میدهد. همچنین میتوانید مراحل تراکنش جبرانساز را که تراکنش را خنثی یا واگرد میکند، مشاهده کنید.
توجه داشته باشید
بسته به نحوه طراحی منطق جبرانکننده برای هر مرحله، ممکن است بتوانید مراحل تراکنش جبرانساز را بهصورت موازی انجام دهید.
در بسیاری از راهحلهای تجاری، شکست یک مرحله بهتنهایی همیشه مستلزم عقبنشینی سیستم با استفاده از تراکنش جبرانساز نیست. بهعنوانمثال، سناریوی وبسایت مسافرتی را در نظر بگیرید. فرض کنید مشتری پروازهای F۱، F2 و F۳ را رزرو میکند اما نمیتواند اتاقی در هتل H۱ رزرو کند. ترجیحاً بهجای لغو پروازها، اتاقی در هتل دیگری در همان شهر به مشتری پیشنهاد دهید. مشتری همچنان میتواند تصمیم به لغو را بگیرد. در آن صورت، تراکنش جبرانساز اجرا میشود و رزرو پروازهای F۱، F2 و F۳ را لغو میکند، اما مشتری باید این تصمیم را بگیرد نه سیستم.
-
Data Consistency Primer. الگوی تراکنش جبرانساز اغلب برای خنثیسازی عملیاتی که مدل یکپارچگی تدریجی را پیادهسازی میکند، استفاده میشود. این مورد زیرساخت اطلاعاتی در مورد مزایا و معاوضههای یکپارچگی تدریجی ارائه میدهد.
-
Idempotency Patterns. در یک تراکنش جبرانساز بهتر است از دستورات ناتوان (idempotent commands) استفاده کنید. این پست در این وبلاگ، فاکتورهایی را توضیح میدهد که باید هنگام پیادهسازی idempotency در نظر بگیرید.
-
الگوی Scheduler Agent Supervisor pattern. این مقاله نحوه پیادهسازی سیستمهای انعطافپذیر را شرح میدهد که عملیات تجاری (business operations) را انجام میدهند که از سرویسها و منابع توزیع شده استفاده میکنند. در این سیستمها، گاهی اوقات لازم است از یک تراکنش جبرانکننده برای لغو کاری که یک عملیات انجام میدهد استفاده کنید.
-
الگوی Retry pattern. تراکنشهای جبرانی میتواند از نظر محاسباتی پیچیده باشد. میتوانید با استفاده از الگوی Retry برای اجرای یک سیاست مؤثر برای تلاش مجدد روی عملیات ناموفق، استفاده از آنها را به حداقل برسانید.
-
الگوی Saga distributed transactions pattern. این مقاله نحوه استفاده از الگوی Saga را برای مدیریت پایداری دادهها در میان میکروسرویسها در سناریوهای تراکنش توزیع شده توضیح میدهد. الگوی Saga بازیابی شکست(failure recovery) را با تراکنشهای جبرانی مدیریت میکند.
-
الگوی Pipes and Filters pattern. این مقاله الگوی لولهها و فیلترها(Pipes and Filters) را توضیح میدهد که میتوانید از آن برای تجزیه یک کارپردازشی پیچیده به یک سری از عناصر قابلاستفاده مجدد استفاده کنید. میتوانید از الگوی Pipes and Filters با الگوی Compensating Transaction بهعنوان جایگزینی برای اجرای تراکنشهای توزیع شده استفاده کنید.
* Design for self healing. این راهنما نحوه طراحی برنامههای خوددرمانی(self-healing) را توضیح میدهد. میتوانید از تراکنشهای جبرانی بهعنوان بخشی از رویکرد خوددرمانی استفاده کنید.