انتقل إلى المحتوى الرئيسي

مختبر: استكشاف أخطاء Lambda

الهدف​

تشخيص وتصحيح وظائف Lambda الفاشلة باستخدام نهج منهجي.

التمرين 1: انتهاء المهلة​

كود المشكلة​

# buggy_timeout.py
import time
import json

def lambda_handler(event, context):
"""
هذه الوظيفة لديها مشكلة انتهاء المهلة
"""
print("Starting processing...")

# محاكاة معالجة طويلة
data = event.get('data', [])

for item in data:
# معالجة بطيئة جدًا
time.sleep(2) # 2 ثانية لكل عنصر!
print(f"Processed: {item}")

return {
'statusCode': 200,
'body': json.dumps({'processed': len(data)})
}

التكوين​

  • انتهاء المهلة: 3 ثواني
  • الحمولة: {"data": ["a", "b", "c", "d", "e"]}

الأسئلة​

  1. لماذا تفشل هذه الوظيفة؟
  2. احسب الوقت الإجمالي اللازم
  3. اقترح حلين مختلفين
💡 الحل

1. لماذا:

  • 5 عناصر × 2 ثانية = 10 ثواني كحد أدنى
  • انتهاء المهلة = 3 ثواني
  • يتم إيقاف الوظيفة قبل الانتهاء

2. الوقت اللازم:

  • المعالجة: 5 × 2s = 10s
  • الحمل الزائد لـ Lambda: ~100ms
  • الإجمالي: ~10.1 ثانية

3. الحلول:

الحل أ: زيادة انتهاء المهلة

aws lambda update-function-configuration \
--function-name buggy-timeout \
--timeout 30

الحل ب: تحسين الكود

import json
from concurrent.futures import ThreadPoolExecutor

def process_item(item):
# معالجة (محاكاة مع تقليل sleep)
return f"Processed: {item}"

def lambda_handler(event, context):
data = event.get('data', [])

# معالجة متوازية
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(process_item, data))

return {
'statusCode': 200,
'body': json.dumps({'results': results})
}

التمرين 2: خطأ في الأذونات​

الكود​

# buggy_permissions.py
import json
import boto3

def lambda_handler(event, context):
"""
هذه الوظيفة تحاول القراءة من S3
"""
s3 = boto3.client('s3')

bucket = event['bucket']
key = event['key']

try:
response = s3.get_object(Bucket=bucket, Key=key)
content = response['Body'].read().decode('utf-8')

return {
'statusCode': 200,
'body': json.dumps({'content': content[:100]})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}

الخطأ المستلم​

{
"statusCode": 500,
"body": "{\"error\": \"An error occurred (AccessDenied) when calling the GetObject operation: Access Denied\"}"
}

الأسئلة​

  1. ما هو سبب الخطأ؟
  2. ما هي سياسة IAM التي يجب إضافتها؟
  3. اكتب أمر AWS CLI للتصحيح
💡 الحل

1. السبب: دور تنفيذ Lambda ليس لديه إذن s3:GetObject

2. سياسة IAM لإضافتها:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}

3. أوامر CLI:

# إنشاء السياسة
cat > s3-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}]
}
EOF

# إرفاق بالدور
aws iam put-role-policy \
--role-name lambda-role \
--policy-name S3ReadAccess \
--policy-document file://s3-policy.json

التمرين 3: نفاد الذاكرة​

كود المشكلة​

# buggy_memory.py
import json

def lambda_handler(event, context):
"""
هذه الوظيفة لديها مشكلة في الذاكرة
"""
size = event.get('size', 1000000)

# إنشاء قائمة ضخمة في الذاكرة
data = []
for i in range(size):
data.append({
'id': i,
'name': f'Item {i}',
'description': 'A' * 1000 # 1KB لكل عنصر
})

return {
'statusCode': 200,
'body': json.dumps({'count': len(data)})
}

التكوين​

  • الذاكرة: 128 MB
  • الحمولة: {"size": 100000} (100K عناصر × ~1KB = ~100MB)

الأسئلة​

  1. لماذا تفشل الوظيفة؟
  2. ما هي الذاكرة التي توصي بها؟
  3. كيف يمكن التحسين دون زيادة الذاكرة؟
💡 الحل

1. لماذا:

  • 100,000 عنصر × ~1KB = ~100MB من البيانات
  • الحمل الزائد لـ Python/Lambda: ~30-50MB
  • الإجمالي: ~130-150MB > 128MB المكونة

2. الذاكرة الموصى بها:

  • الحد الأدنى: 256 MB (مع هامش)
  • الموصى به: 512 MB (للأمان)
aws lambda update-function-configuration \
--function-name buggy-memory \
--memory-size 512

3. التحسين دون زيادة الذاكرة:

import json

def lambda_handler(event, context):
size = event.get('size', 1000000)

# معالجة بالدفعات باستخدام مولد
def process_batch(start, end):
for i in range(start, end):
yield {'id': i, 'processed': True}

# معالجة بالقطع من 10000
batch_size = 10000
total_processed = 0

for batch_start in range(0, size, batch_size):
batch_end = min(batch_start + batch_size, size)

# معالجة الدفعة (لا يحتفظ بكل شيء في الذاكرة)
for item in process_batch(batch_start, batch_end):
total_processed += 1

# تحرير الذاكرة بين الدفعات

return {
'statusCode': 200,
'body': json.dumps({'processed': total_processed})
}

التمرين 4: لم يتم العثور على المعالج​

التكوين​

  • الملف: my_function.py
  • المعالج المكون: lambda_function.handler

الخطأ​

{
"errorMessage": "Unable to import module 'lambda_function': No module named 'lambda_function'",
"errorType": "Runtime.ImportModuleError"
}

الأسئلة​

  1. ما هو خطأ التكوين؟
  2. ما هي قيمة المعالج الصحيحة؟
  3. اذكر 3 أخطاء شائعة للمعالج
💡 الحل

1. الخطأ: المعالج مكون لـ lambda_function.handler لكن الملف يسمى my_function.py

2. المعالج الصحيح:

my_function.handler

الصيغة: <nom_fichier_sans_extension>.<nom_fonction>

3. الأخطاء الشائعة:

  1. اسم الملف غير صحيح (lambda_function مقابل my_function)
  2. اسم الوظيفة غير صحيح (handler مقابل lambda_handler)
  3. الملف في مجلد فرعي (src/handler.main بدلاً من handler.main)
  4. تم تضمين الامتداد عن طريق الخطأ (handler.py.main)
  5. حالة الأحرف غير صحيحة (Handler مقابل handler)

التمرين 5: التشخيص الكامل​

السيناريو​

وظيفة Lambda تعيد بشكل متقطع أخطاء. فيما يلي المعلومات المتاحة:

سجلات CloudWatch:

START RequestId: abc-123
[INFO] Connecting to database...
END RequestId: abc-123
REPORT RequestId: abc-123 Duration: 29987.45 ms Billed Duration: 30000 ms Memory Size: 256 MB Max Memory Used: 89 MB Init Duration: 1234.56 ms

START RequestId: def-456
[INFO] Connecting to database...
[ERROR] Connection timed out
END RequestId: def-456
REPORT RequestId: def-456 Duration: 30000.00 ms Billed Duration: 30000 ms Memory Size: 256 MB Max Memory Used: 67 MB

التكوين:

  • انتهاء المهلة: 30 ثانية
  • الذاكرة: 256 MB
  • VPC: نعم (شبكة فرعية خاصة)

الأسئلة​

املأ جدول التشخيص:

العنصرالملاحظةالمشكلة المحتملةالإجراء
Duration???
Memory???
Init Duration???
Error message???
VPC???
💡 الحل
العنصرالملاحظةالمشكلة المحتملةالإجراء
Duration~30s (انتهاء المهلة)اتصال DB بطيء/محظورانتهاء مهلة DB + إعادة المحاولة
Memory67-89 MB / 256 MBحسنًا، لا توجد مشكلة في الذاكرة-
Init Duration1234msبدء بارد مرتفعتحسين أو Provisioned
Error messageConnection timed outDB غير قابلة للوصولتحقق من SG، NAT
VPCشبكة فرعية خاصةلا يوجد NAT للخروج إلى الإنترنت؟إضافة بوابة NAT

التشخيص: الوظيفة في VPC خاص بدون بوابة NAT. لا يمكنها الوصول إلى قاعدة البيانات الخارجية.

الحلول:

  1. إضافة بوابة NAT إلى الشبكة الفرعية الخاصة
  2. أو وضع قاعدة البيانات في نفس VPC
  3. أو استخدام VPC Endpoints إذا كانت قاعدة البيانات خدمة AWS
  4. إضافة انتهاء مهلة الاتصال في الكود

التمرين 6: كتابة سجلات ذات جودة​

الكود قبل (سجلات سيئة)​

def lambda_handler(event, context):
print("Starting")
# ... code ...
print("Done")
return result

التمرين​

أعد كتابة الكود مع سجلات ذات جودة تسهل استكشاف الأخطاء وإصلاحها:

💡 الحل
import json
import logging

# تكوين المسجل
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
# سجل منظم للإدخال
logger.info(json.dumps({
'action': 'start',
'request_id': context.aws_request_id,
'event_keys': list(event.keys()),
'remaining_time_ms': context.get_remaining_time_in_millis()
}))

try:
# منطقك
user_id = event.get('user_id')
logger.info(f"Processing user: {user_id}")

result = process_user(user_id)

# سجل النجاح
logger.info(json.dumps({
'action': 'complete',
'request_id': context.aws_request_id,
'user_id': user_id,
'status': 'success'
}))

return {'statusCode': 200, 'body': json.dumps(result)}

except Exception as e:
# سجل خطأ مفصل
logger.error(json.dumps({
'action': 'error',
'request_id': context.aws_request_id,
'error_type': type(e).__name__,
'error_message': str(e),
'user_id': event.get('user_id')
}), exc_info=True)

return {'statusCode': 500, 'body': json.dumps({'error': str(e)})}

التسليم​

الوثيقة المطلوب إنتاجها​

  1. حلول للتمارين الستة
  2. جدول التشخيص المكتمل
  3. الكود المصحح لكل خطأ
  4. توصيات لأفضل الممارسات

معايير التقييم​

المعيارالنقاط
التشخيص الصحيح/25
الحلول المناسبة/25
الكود المصحح الوظيفي/25
جودة السجلات/15
الوثائق/10
الإجمالي/100

الأسبوع التالي: MODULE 13 - المراقبة