العودة للدورة|مقابلات مهندس الخدمات الخلفية: إتقان قواعد البيانات وواجهات البرمجة والأنظمة الموزعة
معمل

تصميم خدمة اختصار الروابط

25 دقيقة
متوسط
3 المحاولات المجانية

التعليمات

الهدف

صمم ونفّذ خدمة اختصار روابط تتعامل مع 100 مليون رابط جديد شهريًا بنسبة قراءة إلى كتابة 10:1 واحتفاظ بالبيانات لمدة 5 سنوات. يختبر هذا المعمل قدرتك على دمج تصميم API وخوارزميات الترميز ونمذجة قواعد البيانات والتخزين المؤقت والتحليلات والتفكير في التوسع في نظام متماسك.

تقدير السعة

قبل كتابة أي كود، أكمل قسم تقدير السعة في الكود المبدئي. استخدم هذه المعطيات:

  • الروابط الجديدة شهريًا: 100 مليون
  • نسبة القراءة إلى الكتابة: 10:1
  • متوسط حجم سجل الرابط: 500 بايت (الكود القصير + الرابط الطويل + البيانات الوصفية)
  • فترة الاحتفاظ: 5 سنوات
  • نسبة إصابة الكاش المستهدفة: 80% (توزيع باريتو — 20% من الروابط تحصل على 80% من حركة المرور)

يجب أن تحسب:

  • QPS الكتابة (المتوسط والذروة عند 3 أضعاف)
  • QPS القراءة (المتوسط والذروة عند 3 أضعاف)
  • إجمالي التخزين المطلوب خلال 5 سنوات
  • ذاكرة الكاش اليومية المطلوبة
  • عرض النطاق اليومي للقراءات

المتطلبات

1. نقاط نهاية API

صمم واجهة RESTful بهذه النقاط:

  • POST /api/shorten — إنشاء رابط قصير من رابط طويل
    • جسم الطلب: { "long_url": "https://...", "custom_alias?": "my-link", "expires_at?": "2027-01-01" }
    • الاستجابة: { "short_url": "https://short.url/Ab3xK9z", "short_code": "Ab3xK9z", "expires_at": "2027-01-01" }
  • GET /:shortCode — إعادة التوجيه إلى الرابط الأصلي (HTTP 301 أو 302)
  • GET /api/stats/:shortCode — الحصول على تحليلات لرابط قصير
    • الاستجابة: { "total_clicks": 1523, "clicks_by_day": [...], "top_referrers": [...], "top_countries": [...] }
  • DELETE /api/urls/:shortCode — حذف رابط قصير (المالك فقط)

2. منطق ترميز Base62

نفّذ فئة Base62Encoder التي:

  • تحوّل معرفًا رقميًا إلى سلسلة Base62 (الأحرف: a-z، A-Z، 0-9)
  • تحوّل سلسلة Base62 إلى معرف رقمي
  • تُنشئ أكوادًا من 7 أحرف بالضبط (تُكمل بـ a في البداية إذا لزم الأمر)
  • تتعامل مع النطاق الكامل: 0 إلى 62^7 - 1 (3.5 تريليون)

3. مخطط قاعدة البيانات

صمم مخططًا يدعم:

  • تخزين ربط الروابط مع بيانات وصفية (المنشئ، تاريخ الإنشاء، انتهاء الصلاحية)
  • بحث فعال بالكود القصير (نمط الوصول الأساسي)
  • تخزين أحداث التحليلات (طابع النقرة الزمني، IP، وكيل المستخدم، المُحيل، البلد)
  • حالة تحديد المعدل لكل مفتاح API

قدم مخططات لـ كلا PostgreSQL (علائقي) و DynamoDB (NoSQL) حتى يتمكن المقيّم من تقييم فهمك لكلا النموذجين.

4. طبقة تخزين Redis المؤقت

نفّذ فئة CacheService التي:

  • تستخدم نمط cache-aside لبحث الروابط
  • تعيّن TTL بناءً على شعبية الرابط (الروابط الساخنة تحصل على TTL أطول)
  • تتعامل مع إبطال الكاش عند حذف الرابط
  • تتضمن تسخين الكاش للروابط المُنشأة حديثًا المتوقع أن تكون شائعة

5. تتبع التحليلات

نفّذ ClickTracker الذي:

  • يسجل كل إعادة توجيه كحدث تحليلات (غير مانع، أطلق وانسَ)
  • يستخرج الموقع الجغرافي من IP (ضع بديلاً وهميًا للبحث الجغرافي)
  • يحلل وكيل المستخدم لمعلومات الجهاز/المتصفح
  • يُجمّع الأحداث لكتابات قاعدة بيانات فعالة
  • يوفر إحصائيات مُجمّعة (نقرات باليوم، أعلى المُحيلين، أعلى البلدان)

6. تحديد المعدل

نفّذ مُحدد معدل بدلو الرموز الذي:

  • يحد إنشاء الروابط إلى 10 في الدقيقة لكل مفتاح API
  • يحد إعادة التوجيه إلى 100 في الدقيقة لكل IP (منع سوء الاستخدام)
  • يستخدم Redis للحالة الموزعة
  • يرجع استجابة HTTP 429 مناسبة مع ترويسة Retry-After

7. خطة التوسع الأفقي

في قسم ScalingPlan من تقديمك، صِف (كتعليقات كود أو سلسلة وثيقة تصميم):

  • كيف ستُجزّئ قاعدة بيانات الروابط (أي مفتاح، أي استراتيجية، لماذا)
  • كيف ستتعامل مع اتساق الكاش عبر مثيلات خادم API المتعددة
  • كيف ستوسع خط أنابيب التحليلات للتعامل مع 10 أضعاف حركة المرور
  • ما هي استراتيجية المراقبة والتنبيه (المقاييس الرئيسية)
  • كيف ستتعامل مع مشكلة "القطيع المتهافت" للروابط الفيروسية

تلميحات

  • لترميز Base62، فكر فيه كتحويل رقم إلى الأساس 62 (مشابه لتحويل العشري إلى الست عشري، لكن بـ 62 رقمًا بدلاً من 16)
  • لمخطط قاعدة البيانات، اعتبر أن القراءات تفوق الكتابات بكثير — حسّن مسار القراءة أولاً
  • للتحليلات، استخدم قائمة رسائل (Kafka) لفصل تسجيل النقرات عن مسار إعادة التوجيه — إعادة التوجيه يجب أن تكون سريعة
  • لتحديد المعدل، استخدم سكربت Lua في Redis لجعل الفحص والإنقاص ذريين
  • خطة التوسع يجب أن تعالج نقطة الاختناق المحددة في كل طبقة: خوادم API عديمة الحالة تتوسع بسهولة، لكن طبقات قاعدة البيانات والكاش تحتاج تجزئة دقيقة

ما يجب تقديمه

يجب أن يحتوي تقديمك على قسم ملف واحد في المحرر أدناه: ملف TypeScript كامل يحتوي على جميع الأقسام الثمانية.


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

تصميم API: جميع النقاط الأربع معرّفة بطرق HTTP صحيحة وأنواع طلب/استجابة وأكواد حالة مناسبة (201 للإنشاء، 301/302 لإعادة التوجيه، 429 لتحديد المعدل، 404 لغير موجود) والتحقق من المدخلات15 نقاط
الترميز والتخزين: ترميز/فك ترميز Base62 يحوّل بشكل صحيح بين المعرفات الرقمية والسلاسل من 7 أحرف، مخطط PostgreSQL لديه فهارس مناسبة على short_code، تصميم DynamoDB يستخدم مفاتيح قسم/ترتيب مناسبة، كلا المخططين يتعاملان مع ربط الروابط والبيانات الوصفية20 نقاط
استراتيجية التخزين المؤقت: نمط cache-aside منفذ بشكل صحيح (فحص الكاش ← فقدان ← استعلام DB ← ملء الكاش)، منطق TTL حسب الشعبية يعمل (الروابط الساخنة تحصل على TTL أطول)، إبطال الكاش عند الحذف، تسخين الكاش للروابط الجديدة20 نقاط
التحليلات: أحداث النقر تُسجل بدون منع مع تخزين مؤقت للأحداث، منطق التفريغ على دفعات موجود، بديل البحث الجغرافي منفذ، دالة الإحصائيات المجمعة تُرجع النقرات باليوم/المُحيل/البلد15 نقاط
خطة التوسع: تغطي تجزئة قاعدة البيانات (مفتاح واستراتيجية محددة مع تبرير)، اتساق الكاش عبر المثيلات، نهج توسع خط أنابيب التحليلات، مقاييس المراقبة، والتخفيف من القطيع المتهافت15 نقاط
تقدير السعة: جميع الحسابات صحيحة (QPS الكتابة ~40، QPS القراءة ~400، قيم الذروة عند 3 أضعاف، التخزين الإجمالي ~3 TB، الكاش ~3.5 GB/يوم)، الصيغ ظاهرة، الوحدات مُسمّاة15 نقاط

قائمة التحقق

0/8

حلك

3 محاولات مجانية متبقية