تحديث محتوى CloudFront: كيفية إنشاء Invalidation لمسح الكاش الفوري

استبدلت ملفاً في S3 لكن CloudFront لا يزال يعرض النسخة القديمة — هذا السيناريو يحدث في كل بيئة إنتاج تعتمد على CDN. المشكلة ليست في S3، بل في طبيعة عمل تحديث محتوى CloudFront: الـ edge nodes تحتفظ بنسخة مخزنة مؤقتاً حتى تنتهي مدة TTL، وبدون Invalidation صريح، المستخدمون يرون محتوى منتهي الصلاحية.

ملخص سريع (TL;DR) — تحديث محتوى CloudFront

الخطوة الإجراء متى تستخدمه
1 إنشاء Invalidation عبر AWS Console أو CLI بعد تحديث ملف موجود في S3 مباشرةً
2 استخدام مسار /* لمسح كامل الكاش عند تحديث نشر كامل (full deployment)
3 استخدام مسارات محددة مثل /index.html لتقليل التكلفة عند تحديث ملفات بعينها
4 اعتماد versioning في أسماء الملفات كحل دائم لتجنب الحاجة للـ Invalidation في كل مرة

كيف يعمل كاش CloudFront — الآلية الداخلية

CloudFront شبكة توزيع محتوى (CDN) تعتمد على نقاط حضور موزعة جغرافياً تُسمى edge locations. عندما يطلب مستخدم ملفاً، الـ edge location تتحقق أولاً من كاشها المحلي. إذا وجدت نسخة صالحة (لم تنتهِ مدة TTL)، تُعيدها مباشرةً دون الرجوع إلى S3. هذا هو سبب المشكلة: استبدال الملف في S3 لا يُخطر الـ edge locations تلقائياً.

graph LR User(["المستخدم"]) Edge["Edge Location
(CloudFront)"] S3[("S3 Bucket")] Cache{"في الكاش؟"} User -->|"طلب الملف"| Edge Edge --> Cache Cache -->|"نعم (Cache Hit)"| User Cache -->|"لا (Cache Miss)"| S3 S3 -->|"الملف الجديد"| Edge Edge -->|"تخزين + إرسال"| User style Cache fill:#f0ad4e,color:#000 style S3 fill:#3f8624,color:#fff style Edge fill:#1a6496,color:#fff
  1. الطلب الأول: المستخدم يطلب الملف، الـ edge location لا تجد نسخة في الكاش (Cache Miss)، فتجلبه من S3 وتخزنه.
  2. الطلبات التالية: الـ edge location تُعيد النسخة المخزنة مباشرةً (Cache Hit) حتى تنتهي مدة TTL.
  3. بعد تحديث S3: الـ edge location لا تعلم بالتغيير — تستمر في تقديم النسخة القديمة.
  4. بعد الـ Invalidation: CloudFront يُعلم جميع الـ edge locations بحذف النسخة المخزنة، فتجلب النسخة الجديدة من S3 عند الطلب التالي.
الـ Invalidation لا يُحذف الملف من S3 — فقط يُجبر الـ edge locations على التخلي عن النسخة المخزنة لديها والجلب من المصدر مجدداً.

إنشاء Invalidation عبر AWS Console

أسرع طريقة لمسح كاش تحديث محتوى CloudFront يدوياً:

  1. افتح AWS Console وانتقل إلى خدمة CloudFront.
  2. اختر الـ Distribution المرتبط بـ S3 bucket.
  3. انتقل إلى تبويب Invalidations.
  4. اضغط Create invalidation.
  5. أدخل المسار: /* لمسح كل شيء، أو مسار محدد مثل /images/logo.png.
  6. اضغط Create invalidation وانتظر حتى تتغير الحالة إلى Completed.

إنشاء Invalidation عبر AWS CLI — تحديث محتوى CloudFront برمجياً

في بيئات CI/CD أو عند الحاجة للأتمتة، الـ CLI هو الخيار الأمثل. الأمر التالي يمسح مساراً محدداً:

aws cloudfront create-invalidation \
  --distribution-id E1EXAMPLE \
  --paths "/index.html" "/css/main.css"

لمسح كامل الكاش دفعة واحدة:

aws cloudfront create-invalidation \
  --distribution-id E1EXAMPLE \
  --paths "/*"

للتحقق من حالة الـ Invalidation:

aws cloudfront get-invalidation \
  --distribution-id E1EXAMPLE \
  --id INVALIDATION_ID

لعرض قائمة جميع الـ Invalidations الخاصة بـ Distribution معين:

aws cloudfront list-invalidations \
  --distribution-id E1EXAMPLE

صلاحيات IAM المطلوبة لإنشاء Invalidation

المستخدم أو الـ Role المنفذ للأمر يحتاج الصلاحية التالية كحد أدنى. لاحظ أن cloudfront:CreateInvalidation تدعم تحديد الـ Distribution ARN كـ resource:

🔽 عرض IAM Policy المطلوبة
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cloudfront:CreateInvalidation",
        "cloudfront:GetInvalidation",
        "cloudfront:ListInvalidations"
      ],
      "Resource": "arn:aws:cloudfront::123456789012:distribution/E1EXAMPLE"
    }
  ]
}

استبدل 123456789012 بـ Account ID الفعلي وE1EXAMPLE بـ Distribution ID الخاص بك. CloudFront خدمة عالمية (global)، لذا لا يوجد region في الـ ARN.

أتمتة الـ Invalidation في CI/CD Pipeline

في الواقع، معظم فرق الإنتاج لا تُنشئ الـ Invalidation يدوياً — بل تُدمجه في pipeline النشر مباشرةً. مثال على خطوة في GitHub Actions:

🔽 عرض مثال GitHub Actions
- name: Invalidate CloudFront Cache
  run: |
    aws cloudfront create-invalidation \
      --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
      --paths "/*"
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    AWS_DEFAULT_REGION: us-east-1

التشخيص: لماذا لا يزال المحتوى القديم يظهر بعد الـ Invalidation؟

هنا يقع معظم المهندسين في فخ التشخيص الخاطئ. الـ Invalidation اكتمل، لكن المستخدم لا يزال يرى النسخة القديمة — الافتراض الأول دائماً أن الـ Invalidation لم يعمل. في الغالب، المشكلة في مكان آخر تماماً.

graph TD Start(["المحتوى القديم لا يزال يظهر"]) Q1{"هل الـ Invalidation
في حالة Completed؟"} Q2{"هل X-Cache
= Miss؟"} Q3{"هل الملف في S3
محدّث فعلاً؟"} Q4{"هل Cache-Control
في المتصفح مرتفع؟"} FixInv["انتظر اكتمال
الـ Invalidation"] FixS3["أعد رفع الملف
إلى S3"] FixBrowser["افتح Incognito
أو امسح كاش المتصفح"] Done(["المشكلة محلولة ✓"]) Start --> Q1 Q1 -->|"لا"| FixInv Q1 -->|"نعم"| Q2 Q2 -->|"لا"| FixInv Q2 -->|"نعم"| Q3 Q3 -->|"لا"| FixS3 Q3 -->|"نعم"| Q4 Q4 -->|"نعم"| FixBrowser Q4 -->|"لا"| Done FixInv --> Done FixS3 --> Done FixBrowser --> Done style Done fill:#3f8624,color:#fff style FixInv fill:#1a6496,color:#fff style FixS3 fill:#1a6496,color:#fff style FixBrowser fill:#1a6496,color:#fff

الخطوة 1: تحقق من حالة الـ Invalidation

قبل أي شيء، تأكد أن الـ Invalidation وصل إلى حالة Completed وليس InProgress. الـ Invalidation يستغرق وقتاً للانتشار عبر جميع الـ edge locations.

aws cloudfront get-invalidation \
  --distribution-id E1EXAMPLE \
  --id INVALIDATION_ID \
  --query 'Invalidation.Status'

الخطوة 2: تحقق من Browser Cache

المتصفح يملك كاشه الخاص المستقل عن CloudFront. افتح الصفحة في وضع Incognito أو نفذ:

curl -I https://your-distribution.cloudfront.net/index.html

تحقق من header الاستجابة: X-Cache: Hit from cloudfront يعني أن الـ edge لا تزال تُعيد النسخة المخزنة. X-Cache: Miss from cloudfront يعني أنها جلبت من S3. إذا رأيت Miss لكن المحتوى لا يزال قديماً، المشكلة في S3 نفسه — تأكد أن الملف الجديد رُفع بشكل صحيح.

الخطوة 3: تحقق من Cache-Control Headers في S3

إذا كان الملف في S3 يحمل Cache-Control: max-age=31536000، المتصفح سيتجاهل الـ Invalidation ويعرض نسخته المحلية. تحقق من metadata الملف:

aws s3api head-object \
  --bucket your-bucket-name \
  --key index.html \
  --query 'CacheControl'

الخطوة 4: تحقق من إعدادات TTL في الـ Distribution

إذا كان الـ Default TTL مرتفعاً جداً في Cache Behavior، الـ edge locations لن تتحقق من المصدر بشكل متكرر. هذا لا يؤثر على الـ Invalidation مباشرةً، لكنه يُفسر سلوكاً مشابهاً في حالات أخرى:

aws cloudfront get-distribution-config \
  --id E1EXAMPLE \
  --query 'DistributionConfig.DefaultCacheBehavior.DefaultTTL'

CLI examples omitted for browser cache verification — هذه خطوة تتم عبر المتصفح مباشرةً ولا تحتاج CLI.

الحل الدائم: File Versioning بدلاً من الاعتماد على Invalidation

الـ Invalidation حل تفاعلي، ليس استراتيجية. في بيئات الإنتاج الناضجة، الفرق تعتمد على تسمية الملفات بـ hash أو version رقم:

  • main.abc123.js بدلاً من main.js
  • styles.v2.css بدلاً من styles.css

بهذا الأسلوب، كل نشر جديد يُنتج ملفات بأسماء مختلفة — CloudFront يعاملها كملفات جديدة تماماً ويجلبها من S3 فوراً. الـ index.html وحده يحتاج Invalidation لأنه يشير إلى الملفات الجديدة. هذا يُقلل تكلفة الـ Invalidation ويُلغي مشكلة الكاش القديم من جذرها.

Invalidation هو إسعاف أولي. File versioning هو الوقاية الحقيقية.

تكلفة الـ Invalidation — ما يجب معرفته

أول 1,000 invalidation path شهرياً مجانية. ما يتجاوز ذلك يُحتسب بتكلفة إضافية. المسار /* يُحتسب كـ path واحد بغض النظر عن عدد الملفات. التفاصيل الدقيقة والأسعار الحالية: راجع صفحة تسعير CloudFront الرسمية دائماً لأن الأسعار قابلة للتغيير.

الخلاصة والخطوات التالية — تحديث محتوى CloudFront

إنشاء Invalidation في CloudFront هو الحل الفوري لمشكلة عرض المحتوى القديم بعد تحديث الملفات في S3. لكن الاعتماد عليه كاستراتيجية وحيدة في بيئات الإنتاج يُضيف تعقيداً غير ضروري. الجمع بين Invalidation للحالات الطارئة وFile Versioning كممارسة افتراضية هو النهج الأكثر نضجاً.

مسرد المصطلحات

المصطلح التعريف
Invalidation طلب يُرسل إلى CloudFront لإجباره على حذف نسخة مخزنة من الكاش في جميع الـ edge locations
Edge Location نقطة حضور جغرافية في شبكة CloudFront تخزن المحتوى مؤقتاً وتُقدمه للمستخدمين القريبين منها
TTL (Time To Live) المدة الزمنية التي يحتفظ فيها الكاش بنسخة من الملف قبل إعادة جلبه من المصدر
Cache Hit / Cache Miss Cache Hit: الـ edge وجدت الملف في كاشها. Cache Miss: لم تجده وجلبته من S3
Distribution وحدة الإعداد الأساسية في CloudFront التي تربط المصدر (S3 أو غيره) بشبكة الـ CDN

تعليقات

المشاركات الشائعة من هذه المدونة

استرجاع معرّف نسخة EC2 عبر خدمة البيانات الوصفية IMDSv2

أوضاع سعة DynamoDB: متى تختار On-Demand ومتى تختار Provisioned؟

فهم مهلة الرؤية في SQS: لماذا تُعالَج رسائلك مرتين؟