ساخت Depot CI با Lambda durable functions
خلاصهٔ کاملتر
تیم Depot در این مقاله توضیح میده که چطور سیستم CI خودشون رو روی Lambda durable functions پیادهسازی کردن. یه Lambda durable مثل یه Lambda معمولی AWSه، با این تفاوت که میتونه بین رویدادها به حالت تعلیق بره و از آخرین checkpoint ادامه بده، به همین خاطر محدودیت ۱۵ دقیقهای نداره و میتونه تا یه سال ادامه داشته باشه.
وقتی یه CI run شروع میشه — چه از GitHub webhook باشه، چه از cron scheduler یا CLI — یه پیام SQS ساخته میشه که وظیفهاش فقط اجرا کردن Lambda دائمیه. این الگو دو مزیت داره: اگر invoke شکست بخوره SQS دوباره تلاش میکنه، و هیچ محدودیت زمانی ۱۵ دقیقهای روی اجرا نداریم. هر execution یه نام منحصربهفرد داره که از run ID و workflow ID ساخته میشه؛ این ایدهپرداز idempotency جلوی ساخته شدن ارکستراتورهای تکراری رو میگیره.
معماری به دو لایه تقسیم شده: Run Lambda که ارکستراتور سطح بالاست، مخزن رو clone میکنه، فایلهای YAML رو به یه representation میانی (IR) تبدیل میکنه، و یه DAG (گراف جهتدار بدون حلقه) از job ها میسازه. بعد هر workflow رو به یه Workflow Lambda جداگانه میسپاره. دلیل این تقسیمبندی اینه که هر durable execution سقف ۳۰۰۰ عملیات داره؛ اگه همه workflow ها در یه Lambda باشن، رسیدن به این سقف کل run رو مختل میکنه.
حلقه ارکستراسیون در Workflow Lambda به شکل یه state machine طراحی شده که مدام میپرسه: چی آمادهست؟ چی شکست خورده؟ چی باید لغو بشه؟ تموم شدیم؟ این حلقه یه while loop سادهست، نه یه برنامه از پیش مشخصشده:
while (still work to do):
state = load current state from the DB # durable step (ctx.step(...))
figure out what's ready, what failed, what to cancel, and whether we're done
if we're done:
break
for each job that's newly ready:
start it in its own child context (ctx.runInChildContext(...))
give it a callback to fire on completion (ctx.createCallback(...))
wait for any of the outstanding callbacks (ctx.promise.any(label, promises))
# wake, look at what happened, loop
در هر iteration، Lambda بعد از dispatch کردن کارها به خواب میره و منتظر callback میمونه. وقتی یه job تموم شد، یه callback به orchestrator میفرسته که Lambda رو بیدار میکنه. این رویکرد جلوی polling رو میگیره: ارکستراتور وقتی کاری نداره صرفاً suspendه و هیچ computeای نمیسوزه. Callback ها برای موارد زیادی استفاده میشن: اتمام job، لغو، retry، و wake شدن از یه concurrency group.
مهمترین چیزی که تیم یاد گرفتن اینه که باید به جای یه اجرای خطی، به شکل replay فکر کنن. هر side effect خارج از یه durable step ) میتونه روی replay دوباره اجرا بشه. علاوه بر این، هم تعداد step ها و هم حجم دادهای که checkpoint میشه باید با دقت مدیریت بشه تا به سقف عملیاتی نرسن.
نکات کلیدی:
- Lambda های durable میتونن تا یه سال ادامه داشته باشن چون بین رویدادها suspend و checkpoint میشن
- SQS به عنوان واسط جلوی duplicate execution رو میگیره چون execution ها نام منحصربهفرد دارن
- معماری دولایهای (Run + Workflow) محدودیت ۳۰۰۰ عملیات رو در سطح هر workflow ایزوله میکنه
- همه چیز callback-drivenه؛ نه polling، نه سرور همیشهروشن
- هر callback یه timeout داره تا اگه job گیر کرد، ارکستراتور بینهایت معطل نشه
- مهمترین چالش: تشخیص اینکه چه کدی باید داخل ctx.step(...) باشه تا replay-safe بمونه




