حالت تاریک
فصل ۸ — سرویس ایمیل توزیعشده (Distributed Email Service)
خلاصهٔ تحلیلی — این متن چکیدهٔ مفاهیم فصل به زبان فارسی است، نه ترجمهٔ واژهبهواژه.
طراحی سرویس ایمیل در ابعاد Gmail/Outlook برای ۱ میلیارد کاربر. ایمیل ذاتاً storage-heavy است و برآوردها این را نشان میدهند.
گام ۱ — نیازمندیها
- قابلیتها: ارسال/دریافت، واکشی ایمیلها، فیلتر خوانده/نخوانده، جستوجو (موضوع/فرستنده/بدنه)، ضد-اسپم و ضد-ویروس؛ ارتباط کلاینت-سرور با HTTP؛ پیوست مجاز.
- غیرکارکردی: پایایی (داده نباید گم شود)، دسترسپذیری (replication چندگانه)، مقیاسپذیری و انعطاف (پروتکلهای قدیمی POP/IMAP محدودند).
- برآورد: ارسال ~۱۰ ایمیل/نفر/روز ← ~۱۰۰ هزار QPS ارسال؛ دریافت ~۴۰ ایمیل/روز با متادیتای ~۵۰KB ← ~۷۳۰ پتابایت متادیتا در سال؛ پیوستها (۲۰٪ ایمیلها × ~۵۰۰KB) ← ~۱٬۴۶۰ پتابایت در سال.
دانش پایهٔ ایمیل (Email 101)
- SMTP: ارسال بین سرورها. POP: دانلود و حذف از سرور (فقط یک دستگاه). IMAP: خواندن روی سرور، مناسب چند دستگاه و اتصال کند — رایجترین. HTTPS برای webmail (مثل ActiveSync در Outlook).
- رکورد MX در DNS: تعیین سرورهای ایمیل مقصد با اولویت (عدد کمتر = ترجیح بیشتر).
- پیوست: با Base64/MIME؛ سقف حجم (Gmail ~25MB).
معماری سنتی و محدودیتهایش
جریان کلاسیک: کلاینت ← سرور SMTP فرستنده ← (جستوجوی MX) ← سرور مقصد ← ذخیره ← واکشی گیرنده با IMAP/POP. ذخیرهسازی در فایلهای محلی (مثل Maildir) با رشد حجم به گلوگاه disk I/O و نبود HA میخورد؛ پروتکلهای دههٔ ۶۰-۸۰ برای قابلیتهای مدرن (thread، جستوجو، برچسب) و میلیاردها کاربر طراحی نشدهاند.
گام ۲ — معماری توزیعشده
API های webmail بر HTTP: POST /v1/messages (ارسال)، GET /v1/folders، GET /v1/folders/{id}/messages (با صفحهبندی)، GET /v1/messages/{id}.
اجزا: web server ها (request/response)، real-time server ها (stateful؛ push ایمیل جدید با WebSocket و fallback به long polling — نمونهٔ واقعی: JMAP روی WebSocket در Apache James)، دیتابیس متادیتا، object storage برای پیوستها (Cassandra مناسب نیست: محدودیت عملی blob زیر ~1MB و اشغال row cache)، کش توزیعشده (Redis برای ایمیلهای اخیر) و search store (ایندکس معکوس).
جریان ارسال
وبسرور اعتبارسنجی پایه را انجام میدهد (و اگر گیرنده همدامنه باشد، بدون SMTP مستقیماً در Sent فرستنده و Inbox گیرنده مینویسد) ← صف خروجی (پیوست بزرگ به object store و ارجاعش در پیام) ← SMTP outgoing worker ها بررسی اسپم/ویروس، ذخیره در Sent و ارسال به سرور مقصد. صف خروجی باید پایش شود: انباشت یعنی سرور مقصد در دسترس نیست (retry با exponential backoff) یا consumer کم است.
جریان دریافت
Load balancer مخصوص SMTP (با سیاست پذیرش و bounce ایمیل نامعتبر) ← صف ورودی ← mail processing worker ها (اسپم/ویروس) ← ذخیره در storage/کش/object store ← اگر گیرنده آنلاین است push از راه real-time server؛ وگرنه واکشی بعدی با REST.
گام ۳ — بررسی عمیق
دیتابیس متادیتا
ویژگیهای داده: header کوچک و پرمصرف؛ بدنه بزرگ و معمولاً یکبارخوان؛ عملیاتها منحصر به خود کاربر؛ ۸۲٪ خواندنها برای دادهٔ زیر ۱۶ روز؛ از دست رفتن داده ممنوع. گزینهها: رابطهای (برای رکوردهای بزرگ و جستوجوی BLOB ضعیف)، object storage خام (قابلیتهای read/search/thread سخت)، NoSQL (Bigtable در Gmail؛ بسته). غولها دیتابیس اختصاصی میسازند؛ در مصاحبه ویژگیهای لازم را بگویید: ستون تا چند MB، سازگاری قوی، کمینهسازی disk I/O، و HA و پشتیبانگیری افزایشی.
مدل داده (سبک Cassandra): پارتیشن با user_id. جدولها: folders_by_user؛ emails_by_folder با پارتیشن مرکب (user_id, folder_id) و کلید خوشهبندی email_id از نوع TIMEUUID (مرتبسازی زمانی)؛ emails_by_user + جدول attachments. برای «خوانده/نخوانده» چون is_read کلید نیست، NoSQL کوئری را رد میکند ← denormalization: دو جدول read_emails و unread_emails؛ علامتگذاری یعنی حذف از یکی و درج در دیگری — کد پیچیدهتر، خواندن مقیاسپذیر.
Thread گفتوگو: با فیلدهای header یعنی Message-Id، In-Reply-To و References (الگوریتم JWZ) کلاینت زنجیرهٔ پاسخها را بازسازی میکند.
مبادلهٔ سازگاری: صحت مهمتر از دسترسپذیری است ← هر mailbox یک primary واحد دارد؛ در failover همگامسازی موقتاً متوقف میشود (سازگاری به بهای دسترسپذیری).
تحویلپذیری (deliverability) — سختترین بخش عملی
راهاندازی سرور آسان است؛ نرسیدن به پوشهٔ اسپم سخت است (بیش از ۵۰٪ کل ایمیلها اسپماند و IP تازه اعتباری ندارد):
- IP اختصاصی برای ارسال؛ تفکیک IP بر اساس دستهٔ ایمیل (marketing جدا از transactional).
- گرمکردن تدریجی IP برای اعتبارسازی (۲ تا ۶ هفته طبق Amazon SES) و مسدودسازی سریع اسپمرها.
- حلقهٔ بازخورد با ISP ها: صفهای جداگانه برای hard bounce (آدرس نامعتبر)، soft bounce (خطای موقت) و complaint (دکمهٔ report spam).
- احراز هویت ایمیل: SPF، DKIM و DMARC در برابر phishing (عامل ~۹۳٪ نفوذها).
جستوجو
برخلاف جستوجوی گوگل (کل وب، مرتبسازی بر اساس ربط، ایندکس با تأخیر)، جستوجوی ایمیل محدود به صندوق خود کاربر، مرتب بر اساس ویژگیها و نیازمند ایندکس تقریباً بلادرنگ و دقیق است — و نوشتن بسیار بیشتر از خواندن دارد (هر ارسال/دریافت/حذف = reindex؛ کوئری فقط هنگام فشردن دکمه).
- Elasticsearch: پارتیشن با user_id؛ reindex ناهمگام از راه Kafka. یکپارچهسازی آسان اما همگام نگهداشتن با store اصلی و دو نسخهٔ داده چالش است. مناسب مقیاس کوچک/متوسط.
- موتور اختصاصی: برای مقیاس Gmail؛ گلوگاه اصلی disk I/O است ← ساختار ایندکس با LSM tree (فقط نوشتن ترتیبی؛ همان قلب Bigtable/Cassandra/RocksDB) و جداسازی دادههای کمتغییر (خود ایمیل) از پرتغییر (پوشهها).
مقیاسپذیری و دسترسپذیری
الگوهای دسترسی کاربران مستقلاند ← مقیاس افقی طبیعی است؛ replication چند-دیتاسنتری، کاربر را به نزدیکترین مرکز وصل میکند و در network partition مرکز دیگر پاسخ میدهد.
نکات تکمیلی مصاحبه
تحمل خطا، compliance (GDPR، legal intercept)، امنیت (ضد-phishing، رمزنگاری) و بهینهسازی de-dup پیوستهای تکراری در ایمیلهای گروهی.
نکات کلیدی فصل
- ایمیل مسئلهٔ storage و deliverability است، نه صرفاً پیامرسانی؛ برآورد پتابایتی مسیر طراحی را تعیین میکند.
- صفها در هر دو جریان ارسال/دریافت، پردازش سنگین (اسپم/ویروس) را از مسیر همگام جدا میکنند.
- در NoSQL «کوئریها اسکیما را طراحی میکنند»: denormalization برای read/unread نمونهٔ کلاسیک است.
- اعتبار فرستنده (IP گرمشده + SPF/DKIM/DMARC + حلقهٔ بازخورد) به اندازهٔ معماری اهمیت دارد.
- انتخاب Elasticsearch یا موتور اختصاصی تابع مقیاس و توان تیمی است، نه مد روز.
یادداشتهای مترجم
- SMTP/IMAP/POP، MX record، LSM tree و SPF/DKIM/DMARC لاتین حفظ شدهاند.