چارچوب اشتباه: یک دکمه واگرد
درخواست «افزودن دکمه واگرد» به یک ایجنت، مدلی را فرض میکند که برقرار نیست. فرض میکند یک تراکنش بازگشتپذیر واحد وجود دارد، همانطور که یک ویرایشگر متن با یک فشردن کلید رفتار میکند. اجرای یک ایجنت یک تراکنش نیست. دنبالهای از نوشتنهای ناهمگون در سیستمهایی با مدلهای سازگاری متفاوت است: ردیفی که در پایگاه داده درج میشود، پیامی که در صف منتشر میشود، فایلی که در ذخیرهسازی شیء نوشته میشود، فراخوانی HTTP که یک گردشکار پاییندستی را فعال میکند، ایمیلی که از طریق ارائهدهندهای ارسال میشود که بایتها را به یک MTA راه دور سپرده است. هر یک از اینها تصور خود را از معنای پس گرفتن نوشتن دارد و چند مورد از آنها اصلاً چنین تصوری ندارند.
دکمه واگرد یک قابلیت رابط کاربری است که یک مسئله وضعیت را پنهان میکند. در یک برنامه تککاربره با یک مخزن داده، مسئله وضعیت بهقدری کوچک است که پنهان شود. در ایجنتی که چند سیستم را در بر میگیرد، مسئله وضعیت همان مسئله طراحی است. دکمه چیزی است که پس از حل آن باقی میماند، نه چیزی که برای حل آن میسازید.
اثرها یکنواخت نیستند
کمک میکند اثرها را بر اساس هزینه بازگرداندنشان جدا کنیم، نه بر اساس نحوه ظاهرشان در یک رد.
برخی اثرها بازگرداندنشان ارزان است. یک سند پیشنویس، یک نوشتن در محیط آزمایشی، یک ردیف در یک اسکیمای موقت، یک feature flag که در محیط تست تغییر کرده است. بازگرداندن، نوشتن دیگری از همان نوع، علیه همان مرجع است و هزینه محدود است.
برخی گران هستند. ایمیلی که ارسال شده را نمیتوان پس گرفت؛ فقط میتوان با یک تصحیح دنبالش کرد که خودش ارتباطی جدید با مخاطب و پیامدهای خاص خود است. یک پرداخت را میتوان بازپرداخت کرد، اما بازپرداخت تراکنشی جدید با کارمزد، زمانبندی و پیامدهای تطبیق است. یک رکورد حذفشده گاهی از پشتیبان قابل بازیابی است، اما بازیابی عملیاتی جزئی و نقطهای است که ممکن است تغییرات نامرتبط را از بین ببرد.
برخی غیرممکن هستند. یک راز افشاشده را نمیتوان افشانشده کرد. زمانی که یک اعتبارنامه، یک رکورد مشتری یا یک سند داخلی از مرز اعتماد خارج شد، هیچ عملیات جبرانی جهان را به وضعیت پیشین بازنمیگرداند. کنترل مربوطه پیشگیری است، نه بازگرداندن.
بنابراین پرسش طراحی این نیست که آیا واگرد ارائه شود. این است که کدام ابزارها میتوانند بدون مسیر بازگرداندن فراخوانی شوند و تحت چه پیششرطهایی.
طبقهبندی اثرهای ابزار

یک طبقهبندی عملی سه کلاس دارد و هر اعلان ابزار باید دقیقاً یکی را حمل کند.
بازگشتپذیر. ابزار یک عملیات جبرانی با همان مرجع اصلی ارائه میدهد. جبران متقارن است: همان principal، همان دامنه، همان تضمینهای سازگاری. درج در پایگاه داده با حذف متناظر بر اساس کلید اصلی بازگشتپذیر است. استقرار در محیط آزمایشی با بازگردانی به یک مصنوع پینشده بازگشتپذیر است. آزمون این است که آیا فراخوانی جبرانی میتواند توسط همان هویت ایجنت بدون تأیید جدید صادر شود و آیا نتیجه برای اهدافی که اجرا به آن اهمیت میدهد معادل وضعیت پیش از فراخوانی است.
جبرانپذیر. بازگرداندنی وجود دارد، اما عملی جدید با عوارض جانبی و تأیید خاص خود است. بازپرداخت جبرانپذیر است: اثر اقتصادی یک هزینه را برمیگرداند، اما یک ورودی دفتر کل جدید ایجاد میکند، ممکن است کارمزد داشته باشد و ممکن است بالای یک آستانه نیاز به مجوز انسانی داشته باشد. پیامی که منتشر شده و مصرفکنندگان آن را پردازش کردهاند تنها از این جهت جبرانپذیر است که میتوان یک تصحیح منتشر کرد؛ مصرف اصلی را نمیتوان واگرد کرد. ابزارهای جبرانپذیر جایی هستند که بیشتر مهندسی جالب در آن نهفته است، زیرا جبران خودش یک عملیات درجهیک است که نیاز به پیششرط، مسیر تأیید و رکورد دارد.
بازگشتناپذیر. هیچ عملیات جبرانی وجود ندارد. ارسال ایمیل خارجی، افشای یک راز، حذف رکوردی بدون پشتیبان، فراخوانی عملی از طرف ثالث که طرف ثالث بازگرداندنی برای آن ارائه نمیدهد. اینها نیاز به پیشمجوز دارند، نه بازگردانی پسرویدادی. کنترل، دروازه پیش از فراخوانی است، نه رویه پشت آن.
طبقهبندی بهازای هر ابزار است، نه هر ایجنت. ایجنتی که عمدتاً بازگشتپذیر است همچنان یک ابزار بازگشتناپذیر خواهد داشت و همان یک ابزار سقف خودمختاری کل اجرا را تعیین میکند مگر اینکه برنامهریز برای جداسازی آن محدود شود.
تبدیل بازگشتپذیری به یک فیلد درجهیک
طبقهبندی تنها زمانی مفید است که ماشینخوان باشد. هر اعلان ابزار باید یک کلاس بازگشتپذیری و در صورت وجود، شناسه عملیات جبرانی آن را حمل کند. در یک اسکیما، این بهصورت مجموعه کوچکی از فیلدها در کنار نام ابزار، پارامترها و دامنه اختیار آن ظاهر میشود.
{
"name": "send_customer_email",
"reversibility": "irreversible",
"compensating_operation": null,
"preconditions": ["recipient_verified", "template_approved", "human_gate_passed"]
}
{
"name": "create_invoice",
"reversibility": "compensable",
"compensating_operation": "void_invoice",
"compensation_requires_approval": true
}
{
"name": "write_staging_record",
"reversibility": "reversible",
"compensating_operation": "delete_staging_record",
"comrver-rack-network-cables-6c483a-2ef0de.jpg)
دو مکانیزم از این نتیجه میشود. نخست، برنامهریز توسط کلاس محدود میشود. ابزارهای بازگشتناپذیر نمیتوانند بهصورت حدسی فراخوانی شوند، نمیتوانند در شاخههای موازی قرار گیرند که شکست یک خواهر و برادر اثری جزئی را سرگردان کند، و نمیتوانند در شکست مبهم بدون یک مرحله تطبیق صریح دوباره تلاش شوند. ابزارهای جبرانپذیر ممکن است فراخوانی شوند، اما جبران آنها خودش یک فراخوانی ابزار است که برنامهریز باید زمانبندی کند، نه یک پاکسازی ضمنی. ابزارهای بازگشتپذیر میتوانند آزادانه در دامنه اختیار خود استفاده شوند.
دوم، رکورد اجرا عملیات جبرانی را در کنار اثر ذخیره میکند. زمانی که ابزاری با عملیات جبرانی موفق میشود، گزارش اجرا شناسه اثر و شناسه جبران را همراه با آرگومانهای لازم برای فراخوانی جبران مینویسد. سپس بازگردانی به یک جستجو تبدیل میشود، نه یک بازسازی. اپراتوری که به یک حادثه پاسخ میدهد مجبور نیست استنباط کند ایجنت چه کرده و بازگردانی را حدس بزند؛ رکورد عملیات و آرگومانهایش را نام میبرد. این تفاوت بین یک رویه بازیابی محدود و یک بررسی بیپایان است.
## این چه چیزی به دست میدهد
دستاورد نخست خودمختاری نامتقارن است. میتوان به ایجنت خودمختاری بالا بر سطوح بازگشتپذیر اعطا کرد، جایی که یک اشتباه هزینهاش یک فراخوانی جبرانی است، و دروازههای تنگ بر سطوح بازگشتناپذیر، جایی که یک اشتباه هزینهاش یک افشا یا یک رابطه با مشتری است. این مفیدتر از یک سطح خودمختاری جهانی واحد است که یا کار بازگشتپذیر را بیش از حد محدود میکند یا کار بازگشتناپذیر را کمتر از حد محدود. کلاس بازگشتپذیری محوری است که خودمختاری باید در امتداد آن تغییر کند.
دستاورد دوم پاسخ به حادثه است. زمانی که هر اثر با جبرانش ثبت میشود، رویه بازیابی یک فهرست است، نه یک جستجو. اپراتور میداند کدام اثرها بازگشتپذیرند، کدام جبرانپذیر و نیازمند تأییدند، و کدام بازگشتناپذیرند و بنابراین نیازمند پاسخی کاملاً متفاوتاند: اطلاعرسانی، مهار و افشا، بهجای بازگردانی.
دستاورد سوم ارزیابی است. اگر اعلانهای ابزار بازگشتپذیری را حمل کنند، بستر ارزیابی میتواند اندازه بگیرد که یک اجرا چند وقت یکبار پیش از برآورده شدن پیششرطها به یک ابزار بازگشتناپذیر دست میزند. این یک شاخص پیشرو از خودمختاری بیش از حد مشتاق است و بدون انتظار برای یک حادثه تولیدی قابل اندازهگیری است. همچنین به ارزیابی اجازه میدهد گونههای برنامهریز را روی یک وظیفه مقایسه کند: آیا گونهای که فراخوانیهای بازگشتناپذیر را تا برآورده شدن پیششرطها به تعویق میاندازد، وظیفه را به همان اندازه اغلب کامل میکند و با چه هزینهای در گامها؟
## معاوضه صادقانه
این طراحی توان عملیاتی را هزینه میکند. محدود کردن برنامهریز به جداسازی ابزارهای بازگشتناپذیر و الزام زمانبندی جبران صریح برای ابزارهای جبرانپذیر، موازیسازی و اجرای حدسی را که یک برنامهریز کممحدودتر استفاده میکرد حذف میکند. در وظایفی که عمدتاً بازگشتپذیرند، محدودیت تقریباً رایگان است. در وظایفی که اثرهای بازگشتپذیر و بازگشتناپذیر را ترکیب میکنند، برنامهریز حول فراخوانیهای بازگشتناپذیر سریالی میشود و اجرا کندتر و طولانیتر از یک اجرای بیمحدودیت خواهد بود. معاوضه عمدی است: جایگزین یک اجرای سریع است تا زمانی که غیرقابل بازیابی شود.
هزینه دوم نگهداری است. کلاس بازگشتپذیری ویژگی ابزار در انتزاع نیست؛ ویژگی ابزار در حالت استقرار است، با اختیار فعلی، وضعیت پشتیبان فعلی و مصرفکنندگان پاییندستی فعلی. ابزاری که زمانی بازگشتپذیر بود که مخزن پشتیبانش بازیابی نقطهای داشت، زمانی که آن پنجره بازیابی کوتاه شود جبرانپذیر میشود. طبقهبندی باید در کنار ابزار بازبینی شود، نه یکبار برای همیشه در اعلان تنظیم شود.
## نتیجهگیری
بازگشتپذیری یک ویژگی وضعیت هر ابزار است، نه قابلیتی که روی ایجنت لایهبندی شود. کار این است که اثر هر ابزار را طبقهبندی کنید، عملیات جبرانی را در صورت وجود ثبت کنید، برنامهریز را بر اساس کلاس محدود کنید و خودمختاری را نامتقارن اعطا کنید. دکمه واگرد، اگر اصلاً ظاهر شود، سطح آن کار است. جایگزینی برای آن نیست.
