Durable Objects؛ ورکرهای کلادفلر که حافظه دارن
خلاصهٔ کاملتر
نویسنده اول یادآوری میکنه که Cloudflare Workers یهجورایی مثل لامبدای AWS ـه ولی با تأخیر خیلی کمتر: تقریباً بدون cold start بالا میاد و بهاندازهٔ نیاز اسکیل افقی میگیره. ولی یه چیز مهم کم داره و اون state ـه. ورکری که بالا میاد، یه درخواست رو جواب میده و میمیره، ذاتاً نمیتونه وضعیت طولانیمدت نگه داره.
Durable Objects جواب کلادفلر به همین مشکله: نمونههای ورکر stateدار که استوریج ماندگار خودشون رو دارن (SQLite یا key-value کلادفلر). وقتی استفاده نشن میرن به حالت idle و با رسیدن درخواست بعدی دوباره با همون استوریج زنده میشن. کلادفلر تضمین میکنه هر نمونه در هر لحظه فقط توی یه مکان فعاله، پس میتونه state رو توی حافظه کش کنه و منبع حقیقت هم توی استوریج ماندگار بمونه. علاوه بر این، پشتیبانی از WebSocket بهصورت داخلی داخلشه.
مثال عملی نویسنده یه سبد خریده: بهازای هر کاربر یه Durable Object، تا محتوای سبد روی هر مرورگر و هر دستگاهی دنبالش بیاد. مزیتش نسبت به نگهداشتن سبد توی پستگرس اینه که با هر افزودن آیتم، از طریق وبسوکت به همهٔ تبها و دستگاههای باز پیام میره که سبد عوض شده، و محتوای سبد هم تا وقتی آبجکت فعاله توی حافظه کش میمونه و رفتوبرگشت به دیتابیس اصلی حذف میشه.
از نظر ساخت، یه کلاس جاوااسکریپتیه که از DurableObject ارث میبره، از یه entrypoint سفارشی سرور اکسپورت میشه و توی فایل Wrangler هم یه بایندینگ (مثلاً CART_DO) و یه migration با new_sqlite_classes براش تعریف میشه. متد رزروشدهٔ fetch درخواست ارتقا به وبسوکت رو میگیره و با WebSocketPair و ctx.acceptWebSocket() اتصال رو برقرار میکنه، webSocketMessage پیامهای سمت کلاینت رو میگیره، و برای پخش پیام هم روی ctx.getWebSockets() میچرخی و socket.send میزنی.
برای مصرفش هم یه هلپر ساده کافیه: با idFromName یه شناسهٔ سراسری یکتا از روی آیدی کاربر میگیری و با get() پروکسی زندهٔ همون نمونه رو برمیداری، بعد متدهاش رو مثل یه آبجکت معمولی از توی server function صدا میزنی.
const { CART_DO } = env;
const cartId = CART_DO.idFromName(user.id);
const cart = CART_DO.get(cartId);قطعهٔ آخر هم SQLite ـه که از ctx.storage.sql.exec() در دسترسه. نویسنده جدول سبد رو توی سازنده و داخل ctx.blockConcurrencyWhile() میسازه؛ توضیحش اینه که هرچند sql.exec() سینکرونه و چیزی وسطش نمیپره، ولی سازندهها معمولاً بهمرور بزرگ میشن (لود state، مهاجرتها) و این تضمین میده تا تمومشدن مقداردهی اولیه هیچ درخواستی به آبجکت نمیرسه.
نکات کلیدی:
- Durable Object یعنی ورکر stateدار با استوریج ماندگار (SQLite یا KV) و فقط یه نمونهٔ فعال در یه مکان
- وبسوکت داخلیه: WebSocketPair، ctx.acceptWebSocket()، webSocketMessage و ctx.getWebSockets()
- راهاندازی: کلاس ارثبرده از DurableObject + entrypoint سفارشی + بایندینگ و migration توی Wrangler
- idFromName() شناسهٔ یکتا از یه رشته میسازه و get() پروکسی زنده رو میده — مثلاً یه آبجکت بهازای هر کاربر
- SQLite با ctx.storage.sql.exec() و مقداردهی اولیه داخل ctx.blockConcurrencyWhile()
- کد کامل مثال توی مخزن گیتهاب نویسنده هست




