Skip to main content
العودة إلى المدونة
Next.jsMemory LeakDockerAWS LambdaNode.js

تسريب ذاكرة Next.js: Fetch + وضع Standalone — سنتان دون إصلاح

يقوم Next.js بترقيع دالة fetch العامة ويضيف طبقة تخزين مؤقت تتسرب منها الذاكرة مع كل طلب. في Docker/K8s يؤدي هذا إلى انهيارات OOM كل بضع ساعات. توجد العلة منذ Next.js 14 ولا تزال دون حل في 16.2.x.

نُشر 19 مارس 202610 دقيقة قراءة

TL;DR

يقوم Next.js بترقيع دالة fetch العامة ويضيف طبقة تخزين مؤقت تحتفظ بمراجع إلى بيانات الاستجابة بعد أن كان ينبغي تحريرها. كل استدعاء لـ fetch يضيف ذاكرة لا تُعاد أبدًا إلى الـ GC. في Docker/Kubernetes يؤدي هذا إلى انهيارات OOM كل بضع ساعات. توجد هذه العلة منذ Next.js 14 (أبريل 2024) ولا تزال دون حل في 16.2.x (مارس 2026). أما على Vercel فلا تظهر المشكلة بفضل دوال serverless العابرة.

كيف يعمل fetch العادي

Request → fetch → got data → response to user → GC cleans up → memory free

كيف يعمل fetch في Next.js

يعترض Next.js دالة fetch العامة ويلفّها بطبقة تخزين مؤقت/تتبّع خاصة به:

Request → Next.js fetch wrapper → creates:
  - Performance entry (speed tracking)    ← not cleaned
  - Request metadata object               ← not cleaned
  - Internal promise wrapper              ← not cleaned
  - Cache lookup entry                    ← not cleaned
  - Response body (for unique URL)        ← not cleaned
→ Response to user
→ GC sees objects are still referenced → doesn't touch them
→ Memory grows indefinitely

ماذا يحدث في بيئة الإنتاج

Self-hosted (Docker/K8s):
  Request 1:      fetch → +100KB → RAM: 100KB
  Request 2:      fetch → +100KB → RAM: 200KB
  Request 1000:   fetch → +100KB → RAM: 100MB
  Request 50,000: fetch → +100KB → RAM: 5GB → OOM Kill 💀
  → Kubernetes restarts the pod → cycle repeats

Vercel (Serverless):
  Request 1: [start] → fetch → +100KB → [process dies] → 0 KB ✅
  Request 2: [start] → fetch → +100KB → [process dies] → 0 KB ✅
  → Memory never accumulates

الإصدارات المتأثرة

Next.js — كل الإصدارات التي تستخدم App Router

VersionIssueDate
14.2.x#64212April 2024
14.x-15.x#68578August 2024
14.3.0-canary#79588May 2025
16.0.1#85914November 2025
16.1.0#88603January 2026
16.0.10#90433February 2026
16.2.0-canary.51Confirmed in #90433 commentsMarch 2026

Node.js

تم الاختبار على Node.js 20 و22 و24 و25 — تتسرب الذاكرة في جميعها.

ما الذي لا ينفع

AttemptResult
cacheMaxMemorySize: 0Doesn't help — leak is not from this cache
Disable image optimizationDoesn't help
--max-old-space-size=6144Just crashes slower
Read response via .json() / .text()Doesn't help
Remove React fetch patching (canary.45)Doesn't help
Upgrade to latest versionDoesn't help (leaks on 16.2.0-canary.51 too)

ما الذي ينفع (حلول التفافية)

WorkaroundWhy it helps
Replace fetch with axiosBypasses Next.js wrapper
Replace fetch with node-fetchSame reason
Downgrade Node.js to 20.15.1Older undici has fewer leaks (for some)
Docker image node:20-alpine3.21Helps in some cases
Deploy to Lambda (SST + OpenNext)Ephemeral functions — memory doesn't accumulate
Deploy to VercelSame — serverless

قيود الحل الالتفافي باستخدام axios

يمكنك استبدال استدعاءات الـ API الخاصة بك بـ axios. لكن Next.js يستخدم داخليًا نسخة fetch المرقّعة من أجل:

  • ISR (Incremental Static Regeneration)
  • revalidatePath / revalidateTag
  • جلب بيانات Server Components مع إزالة التكرار
  • use cache (Next.js 16)

أي حتى من دون استدعاء واحد لـ fetch في شيفرتك — يظل Next.js يستخدمه في الخفاء.

لماذا لا تُصلح Vercel المشكلة

منطق العمل التجاري

Vercel شركة تجني المال من استضافة Next.js. على منصتها لا تظهر المشكلة (serverless = عابر). العلة تؤثر فقط على الاستضافة الذاتية (Docker وK8s وVPS) — أي على من لا يدفع لـ Vercel.

الموقف الرسمي

قام Tim Neutkens (أحد مشرفي Vercel) بتحليل المشكلة وأعلن أنها مشكلة undici (مكتبة fetch في Node.js) وليست مشكلة Next.js. وأُغلق Issue #90433. وذلك رغم أن:

  • axios وnode-fetch يعملان على نفس Node.js دون تسريبات
  • التسريب لا يظهر إلا عندما يمر fetch عبر غلاف Next.js
  • العلة مفتوحة منذ سنتين دون إصلاح

الأولويات

خلال هاتين السنتين أصدر فريق Next.js ما يلي:

  • Turbopack (بناء أسرع بمقدار 2-5x) — ميزة تسويقية
  • Cache Components / use cache — يقلل الحِمل على خوادم Vercel
  • proxy.ts بدلًا من middleware — يبسّط النشر على edge في Vercel
  • DevTools MCP — ضجّة الذكاء الاصطناعي

تسريب الذاكرة في الاستضافة الذاتية؟ ليس من الأولويات.

الحل: AWS Lambda (SST + OpenNext)

ما هذا

OpenNext مُحوِّل مفتوح المصدر يحوّل بناء Next.js إلى صيغة تناسب AWS Lambda. أما SST فهو إطار عمل يؤتمت البنية التحتية.

البنية المعمارية

Next.js build
  → OpenNext
    → AWS Lambda (SSR, API routes)
    → S3 (static, assets)
    → CloudFront (CDN)
    → SQS + DynamoDB (ISR revalidation)

لماذا يحل هذا تسريب الذاكرة

تعالج دوال Lambda الطلبات ثم يُعاد تدويرها بعد 5-15 دقيقة من الخمول. فلا تجد الذاكرة وقتًا لتتراكم.

النشر

npx sst@latest init
npx sst deploy --stage production

المقارنة

VercelAWS Lambda (SST)Docker self-hosted
Memory leakNot feltNot feltCritical
Cold startsYesYes (~200-500ms)No
Price (~medium traffic)$20-150/mo$5-30/mo$5-50/mo
ControlMinimalFullFull
ISR/RevalidationWorksWorks (SQS)Works (with leak)
Vendor lock-inVercelAWSNone

اعتبارات Lambda

  • Cold starts — الطلب الأول أبطأ (~200-500ms)
  • الأمان — فعّل OAC (Origin Access Control)، وإلا فإن رابط Lambda يكون علنيًا
  • OpenNext — مشروع مجتمعي وليس رسميًا من Vercel. قد تتعطل ميزات Next.js الجديدة
  • هجوم المحفظة — أثناء هجوم DDoS قد يؤدي التوسع التلقائي لـ Lambda إلى فاتورة ضخمة

لماذا يُعدّ الإصلاح الحقيقي غير واقعي

1. مشكلة معمارية

التسريب ليس علة عرضية بل نتيجة قرار تصميمي: يعترض Next.js دالة fetch العامة ويضيف فوقها التخزين المؤقت/التتبّع. ولإصلاحه يلزم إعادة تصميم طريقة تفاعل App Router مع fetch. وهذا يمسّ ISR وrevalidation وdata cache وrequest deduplication — أي نواة الإطار.

2. تضارب المصالح

Vercel غير متحفزة لإصلاح ما لا يؤثر على منصتها. الاستضافة الذاتية تنافس عملها التجاري. وكلما زادت المشكلات في الاستضافة الذاتية — زاد عدد من ينتقلون إلى Vercel.

3. تحميل المسؤولية لغيرها

الموقف الرسمي هو «إنها undici، لسنا نحن». وحتى يتغير هذا — لن يعملوا على إصلاح.

4. لا يوجد إصلاح من المجتمع

رخصة AGPL-3.0 الخاصة بـ Next.js تسمح بالتفريعات، لكن قاعدة الشيفرة ضخمة ومترابطة بإحكام مع بنية Vercel التحتية. أي طلب دمج مجتمعي لإصلاح غلاف fetch سيتطلب فهمًا عميقًا للبنية الداخلية وموافقة المشرفين — الذين أغلقوا الـ issue بالفعل.

الخلاصات

  1. إن كنت على Vercel — لا مشكلة، لا شيء تفعله
  2. إن كنت على استضافة ذاتية وتحتاج serverless — SST + OpenNext على AWS Lambda
  3. إن كنت على استضافة ذاتية بـ Docker — استبدل fetch بـ axios حيثما أمكن، وراقب الـ RAM، واضبط إعادة تشغيل تلقائية للـ pods
  4. إن كنت تبدأ مشروعًا جديدًا — فكّر في SvelteKit أو Nuxt كبدائل خالية من هذه المشكلة

المصادر