اگر مهندس بکاند هستید و از سرهم کردن کرونجابها، صفها و جداول وضعیت برای مطمئن شدن از اجرای کارهای پسزمینه خستهاید، معماری شما قرار است بهشدت ساده شود. در ۵ ژوئن ۲۰۲۶، طبق اعلام رسمی در مخزن گیتهاب، مایکروسافت افزونهی pg_durable را منتشر کرد تا امکان اجرای مقاوم را مستقیماً در دل دیتابیس فراهم کند.
سالهاست صنعت بر الگوی «اجرای مقاوم» (Durable Execution) تکیه کرده است؛ سیستمی که در آن یک فرآیند میتواند با ذخیره وضعیت خود، در برابر کرشها و ریاستارتها دوام بیاورد. معمولاً این کار نیازمند یک استک سنگین خارجی است. شما احتمالاً از Temporal، Airflow یا AWS Step Functions برای مدیریت منطق استفاده میکنید و دیتابیس صرفاً نتیجه نهایی را ذخیره میکند. این وضعیت باعث ایجاد شکافی میان محل پردازش و محل ذخیرهی داده میشود.
pg_durable این شکاف را میبندد. این ابزار به شما اجازه میدهد یک گردشکار (Workflow) را به صورت گرافهایی از مراحل SQL تعریف کنید. سیستم هر مرحله را پس از تکمیل، ثبت (Checkpoint) میکند. اگر دیتابیس کرش کند یا یک مرحله شکست بخورد، اجرا از آخرین نقطهی ثبتشده ادامه مییابد و نیازی به ریاستارت دستی یا اجرای مجدد کل عملیات نیست. همانطور که در تحلیلهای قبلی ما دربارهی استراتژیهای کاهش تأخیر در دیتابیسها اشاره کردیم، این اقدام بخشی از مأموریت بزرگتر برای نزدیک کردن پردازش به داده است.
سازوکارهای اصلی و معماری
این سیستم به عنوان یک افزونهی PostgreSQL و با استفاده از pgrx ساخته شده است. عملکرد آن بر دو کتابخانهی Rust استوار است:
- duroxide: یک چارچوب وظایف مقاوم که محیط اجرای ارکستراسیون را فراهم میکند. این کتابخانه بازپخشهای قطعی (Deterministic Replay)، نقاط ثبت (Checkpoints)، تایمرها و زیر-ارکستراسیونها را مدیریت میکند.
- duroxide-pg: یک تأمینکنندهی وضعیت مبتنی بر PostgreSQL برای duroxide. این بخش وضعیت محیط اجرا — شامل تاریخچه، صفهای کاری و نمونهها — را در یک اسکیمای اختصاصی به نام
duroxide.*که متعلق به افزونه است، ذخیره میکند.
بر اساس مستندات فنی، توسعهدهندگان از یک زبان توصیفی (DSL) در SQL برای ساخت این توابع با استفاده از عملگرهای ترکیبپذیر استفاده میکنند. برای مثال، عملگرهای ~> و |=> اجازه میدهند مراحل را به هم زنجیر کنید. یک گردشکار معمول با df.start() آغاز میشود که یک شناسه منحصربهفرد برای ردیابی برمیگرداند. محیط اجرا سپس هر مرحله را به صورت مقاوم و با ثبت وضعیت بین آنها اجرا میکند. کاربران میتوانند وضعیت و نتایج را در حالی که گردشکار هنوز در حال اجراست یا پس از تکمیل آن، مستقیماً از PostgreSQL استعلام کنند.
جزئیات پیادهسازی
برای درک جریان داخلی، لایههای ساختاری افزونه را بررسی میکنیم:
- لایه SQL DSL: رابط سطح بالایی که در آن کاربران گرافها را با استفاده از ساختارهایی مثل
sql|=>name~>sql2تعریف میکنند. این لایه شامل توابع اولیه سطح بالایی مانندdf.if()،df.join()وdf.loop()است. - Worker پسزمینه: این فرآیند، محیط اجرای duroxide را به صورت در-فرآینگی (in-process) در داخل سرور PostgreSQL میزبانی میکند و نیاز به زیرساختهای سرویس خارجی را کاملاً حذف میکند.
- لایه وضعیت: سیستم دو ناحیه مجزا را مدیریت میکند؛ اسکیمای
df.*برای مدیریت گرافهای DSL (گرهها، نمونهها و متغیرها) و اسکیمایduroxide.*برای مدیریت وضعیت خام محیط اجرا.
اگر توسعهدهندهای ترجیح دهد توابع مقاوم را در Rust، Python یا Node بنویسد اما همچنان وضعیت را در PostgreSQL ذخیره کند، میتواند مستقیماً از duroxide و duroxide-pg استفاده کند. pg_durable در واقع لایهی نویسندگی SQL است که روی این دو کتابخانه بنا شده است.
موارد کاربرد عملی
به گزارش مایکروسافت، این ابزار برای مهندسان داده، مدیران دیتابیس (DBA) و متخصصان SRE که در حال اتوماسیون کتابهای راهنما (Runbooks) هستند، بسیار کاربردی است:
- خط لولههای بردار معنایی (Vector Embedding Pipelines): شما میتوانید دادهها را تکهتکه کنید، یک API بردارساز را فراخوانید و نتایج را در pgvector ذخیره کنید — همه در یک زنجیره مقاوم.
- خط لولههای جذب داده (Ingest Pipelines): مدیریت مراحل آمادهسازی (Staging)، حذف تکراریها و تبدیل دستههای بزرگ داده بدون ریسک بروز انحراف ناشی از شکستهای جزئی.
- نگهداری زمانبندی شده: اتوماسیون کتابهای راهنما برای شناسایی تورم دادهها (Bloat)، اطلاعرسانی به یک انسان، انتظار برای تأیید و سپس اجرای اصلاحات.
- تجمیع توزیعی (Fan-out Aggregation): اجرای موازی پرسوجوهای مستقل و سپس ترکیب نتایج آنها.
- گردشکارهای API خارجی: سادهسازی غنیسازی و طبقهبندی دادهها از طریق فراخوانیهای سبک Webhook که مستقیماً از داخل SQL تحریک میشوند.
جایگزینهای فعلی: «روش دستی»
پیش از pg_durable، تیمها این گردشکارها را با ابزارهای پراکنده مدیریت میکردند. الگوهای رایج عبارت بودند از:
- ترکیب
pg_cronبا یک جدول سفارشی برای وظایف، ستونهای وضعیت، شمارندههای تلاش مجدد (Retry counters) و یک Worker برای نظارت (Polling). - استفاده از ارکستراتورهای خارجی (مثل Airflow یا Argo) که دوباره به Postgres متصل میشدند.
- استقرار صفها و Workerهایی با یک جدول وضعیت مجزا برای هماهنگی تکمیلهای جزئی.
- نوشتن رویههای
plpgsqlکه تا زمان وقوع یک کرش یا تراکنشهای طولانی کار میکردند و سپس نیاز به ریاستارت کامل داشتند.
این روشهای قدیمی نقاط درد زیادی ایجاد میکنند. ریاستارت در میانه یک Job طولانی اغلب به معنای اجرای مجدد کارهایی است که قبلاً با موفقیت انجام شدهاند. یک فراخوانی ناموفق API میتواند منجر به پاکسازی دستی و عدم قطعیت در بازپخش شود. علاوه بر این، تراکنشهای طولانی باعث قفل شدن دادهها و رشد فایل Write-Ahead Log (WAL) میشوند که کارهای دستهای را در مقیاس بالا شکننده میکند. همچنین، انجام کارهای موازی در لایهی اپلیکیشن، فرصتهای بیشتری برای بروز خطاها و ناهماهنگی در شکستهای جزئی ایجاد میکند.
استقرار و یکپارچهسازی
pg_durable در حال حاضر در وضعیت پیشنمایش است و در Azure HorizonDB (سرویس ابری جدید PostgreSQL مایکروسافت) ادغام شده است. برای کسانی که کلاسترهای خود را مدیریت میکنند، مایکروسافت بستههای Debian برای نسخههای ۱۷ و ۱۸ PostgreSQL روی معماری amd64 ارائه داده است. این بستهها با نام pg-durable-postgresql-<PG major>_<pg_durable version>-1_<arch>.deb عرضه شدهاند و کتابخانه افزونه، فایل کنترل و فایلهای ارتقای SQL را نصب میکنند.
نصب این ابزار مستلزم افزودن pg_durable به shared_preload_libraries و ریاستارت سرور است. پس از ریاستارت، کاربران باید دستور CREATE EXTENSION pg_durable; را در دیتابیس پیکربندی شده (که پیشفرض آن postgres است) اجرا کنند. داراییهای انتشار (Release assets) همچنین شامل آرشیوهای سورس برای کسانی است که ترجیح میدهند افزونه را از روی کد منبع بسازند.
امنیت و تنظیمات چندکاربری
امنیت از طریق ترکیبی از نقشها و امنیت سطح ردیف (RLS) مدیریت میشود. دستور CREATE EXTENSION به طور پیشفرض به PUBLIC دسترسی نمیدهد و مدیر باید صراحتاً دسترسی را اعطا کند. این کار از طریق SELECT df.grant_usage('app_role'); یا ایجاد یک نقش مشترک مانند pg_durable_user و اعطای عضویت به نقشهای اپلیکیشن انجام میشود:
CREATE ROLE pg_durable_user NOLOGIN;
SELECT df.grant_usage('pg_durable_user');
GRANT pg_durable_user TO app_backend, etl_service;
محدودیتهای عملیاتی و امنیتی کلیدی عبارتند از:
- نقش Worker: نقش پسزمینه (که توسط GUC
pg_durable.worker_roleتعریف شده و پیشفرض آنpostgresاست) باید Superuser باشد تا بتواند RLS را دور بزند و تمام نمونههای کاربران را مدیریت کند. - دسترسیهای کاربر: کاربران دسترسی
SELECTوINSERTرویdf.instancesوdf.nodesدریافت میکنند. همچنین دسترسیUPDATEدر سطح ستون برایstatusوupdated_atداده میشود تا امکان استفاده ازdf.cancel()فراهم شود. - جداسازی: ستون هویت
submitted_byتوسط کاربران قابل تغییر نیست. علاوه بر این،df.varsاز طریق یک ستون مالکیت و RLS دارای محدودیت هر-کاربر است. Superuserها RLS را دور میزنند، اما توابع DSL همچنان از طریق فیلترهای صریح، دسترسیها را به کاربر فراخواننده محدود میکنند. - ارتقا: پس از اجرای
ALTER EXTENSION pg_durable UPDATEتوسط مدیر، باید مجدداًdf.grant_usageاجرا شود، زیراGRANT EXECUTEتنها روی توابعی اعمال میشود که در زمان اعطای دسترسی وجود داشتهاند.
توسعه و تست
پیشنیازهای توسعه برای کسانی که روی این پروژه کار میکنند شامل PostgreSQL ۱۷ یا ۱۸، نسخه Nightly زبان Rust و cargo-pgrx 0.16.1 است. پروژه از VS Code Dev Container و اسکریپت pg-start.sh برای بوتاسترپ دایرکتوریهای داده محلی با یک Superuser پستگرس و یک نقش متناظر با کاربر سیستمعامل استفاده میکند.
تستها به دو دسته اصلی تقسیم میشوند:
- تستهای pg_regress: تستهای سریع و قطعی برای بررسی عملکردهای اصلی DSL با استفاده از چارچوب استاندارد PostgreSQL. این تستها از طریق
make test-regressیاmake installcheckاجرا میشوند. - تستهای E2E: تستهای جامع سناریو با استفاده از PostgreSQL در
pgrxکه شامل مراحل خاص ریاستارت و پیکربندی است و از طریق./scripts/test-e2e-local.shاجرا میگردد. تستهای خاص، مانند اجرای موازی، از طریق./scripts/test-e2e-local.sh 04_parallelقابل اجرا هستند.
یکپارچهسازی مستمر (CI) مستلزم آن است که تمام Pull Requestها از بررسی فرمت (cargo fmt --check)، Clippy، تستهای واحد و تستهای E2E تعریف شده در .github/workflows/ci.yml عبور کنند.
تغییرات عملیاتی
این ابزار ردپای زیرساختی شما را بهطور بنیادی تغییر میدهد. به جای یک نمونه Redis مجزا یا ارکستراتور پیچیده، وضعیت تلاش مجدد و ردیابی پیشرفت به جداول Postgres مانند df.instances منتقل میشود. این یعنی منطق گردشکار و دادههای شما، مدل احراز هویت و پشتیبانگیری یکسانی دارند.
با این حال، مایکروسافت اشاره میکند که در موارد زیر از pg_durable استفاده نکنید:
- اگر Job شما در حال حاضر تنها یک دستور ساده
INSERT ... SELECTیا یک عبارت SQL معمولی است. - اگر به پاسخهای همزمان (Synchronous) در سطح زیر یک میلیثانیه نیاز دارید.
- اگر امکان نصب افزونه یا اجرای Worker در محیط شما وجود ندارد.
- اگر گردشکار شما سیستمهای متنوع و ناهمگونی را در خارج از Postgres در بر میگیرد.
- اگر به منطق برنامهنویسی دلخواه (SDKهای غیر HTTP یا جریانهای پیچیده در حافظه) نیاز دارید که با مراحل SQL، حلقهها یا شاخهبندیها سازگار نیست. در چنین مواردی، منطق باید در یک تابع SQL پیچیده شود یا از طریق یک Endpoint برای
df.http()در دسترس قرار گیرد.
تحلیل: چرخش به سمت پردازش درون-دیتابیسی
این انتشار سیگنالی از یک حرکت گستردهتر به سمت «آوردن پردازش به نزدیکی دادهها» است. مایکروسافت با جاسازی ارکستراتور در دیتابیس، در حال مقابله با مشکل «وضعیت توزیعشده» (Distributed State) است. وقتی ارکستراتور خارجی است، یک اختلال شبکه بین Worker و دیتابیس میتواند منجر به اجراهای «شبحی» یا وضعیتهای ناسازگار شود.
برای متخصصان، این امر «بار شناختی» (Cognitive Load) استک تکنولوژی را کاهش میدهد. شما دیگر نیازی به نگهداری یک داشبورد مجزا برای ارکستراتور و یک کلاینت مجزا برای دیتابیس ندارید. دیتابیس تبدیل به تنها منبع حقیقت (Single Source of Truth) هم برای دادهها و هم برای فرآیندی میشود که آنها را تغییر میدهد.
این حرکت ابزارهای ارکستراسیون مستقل را تحت فشار قرار میدهد تا یا عمیقتر با لایه ذخیرهسازی ادغام شوند یا سربار خود را توجیه کنند. برای اکثر خط لولههای AI متوسط، توانایی مدیریت اجرای مقاوم در سطح هر-ردیف بدون نیاز به یک کلاستر مجزا، یک دستاورد قابل توجه در بهرهوری است.
برای شروع، توسعهدهندگان میتوانند سورس کد را در گیتهاب بررسی کنند یا یک کلاستر در Azure HorizonDB مستقر نمایند تا افزونه را در یک محیط مدیریتشده تست کنند.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو