الهندسة · 21 سبتمبر 2026
مفاتيح عدم التكرار (Idempotency Keys) في واجهات API للبريد الإلكتروني
امنع الرسائل المكررة أثناء إعادة محاولات الشبكة بتطبيق مفاتيح عدم التكرار. تعلّم كيف تتعامل مع إخفاقات الأنظمة الموزعة دون إزعاج مستخدميك.
مشكلة الرسائل المكررة
تحدث الرسائل المكررة عندما يرسل العميل طلبًا فيعالجه الخادم، لكن الشبكة تفشل قبل أن يتلقى العميل استجابة النجاح. فيعيد العميل، وقد رأى انتهاء مهلة أو خطأ 5xx، محاولة الطلب. وبدون عدم التكرار يعامل الخادم إعادة المحاولة كطلب جديد ويرسل الرسالة مرة أخرى. وتمنع مفاتيح عدم التكرار ذلك بأن تتيح للخادم التعرّف على الطلب المتكرر وإعادة النتيجة الأصلية دون إعادة تنفيذ الأثر الجانبي.
بصفتك المهندس المسؤول عن طابور الحوادث، ليس هناك ما هو أسوأ من «عاصفة الرسائل المكررة». وتحدث عادةً أثناء انقطاع جزئي لدى مزوّد في المنبع أو تعطل قفل في قاعدة البيانات يبطئ أزمنة الاستجابة. فيتحول منطق إعادة المحاولة لديك، المصمَّم للموثوقية، إلى سلاح يزعج مستخدميك ويضر بسمعة المُرسِل.
لماذا تفشل إعادة المحاولات بدون عدم التكرار
في النظام الموزع، هناك ثلاث نقاط فشل لأي استدعاء API:
- لا يصل الطلب إلى الخادم أبدًا.
- يعالج الخادم الطلب لكن الاستجابة تضيع.
- يتعطل الخادم في منتصف المعالجة.
إذا أعدت المحاولة في الحالة 1، فأنت آمن. وإذا أعدتها في الحالة 2، فسترسل رسالة مكررة. وإذا أعدتها في الحالة 3، فقد ترسل رسالة مكررة بحسب موضع التعطل.
إرسال الرسالة أثر جانبي خارجي. وعلى عكس تحديث اسم مستخدم في قاعدة بيانات (وهو غير مكرَّر بطبيعته إذا استخدمت SET name = 'Alice')، فإن إرسال الرسالة إجراء تراكمي. فكل استدعاء لنقطة نهاية send ينشئ رسالة جديدة في العالم الحقيقي. ولجعل ذلك غير مكرَّر، يجب أن تُدخل معرّفًا فريدًا لنية الإرسال، يُعرف باسم مفتاح عدم التكرار (idempotency key).
تطبيق مفاتيح عدم التكرار
مفتاح عدم التكرار قيمة فريدة (عادةً UUID v4) يولّدها العميل ويرسلها في ترويسة الطلب. ويستخدم الخادم هذا المفتاح لتتبع حالة الطلب.
سير العمل على الخادم
- استلام الطلب: يتحقق الخادم من وجود الترويسة
Idempotency-Key. - البحث: يفحص الخادم مخزنًا سريع الوصول (مثل Redis) بحثًا عن ذلك المفتاح.
- إصابة الذاكرة المؤقتة (Cache Hit): إذا كان المفتاح موجودًا، يعيد الخادم الاستجابة المخزّنة فورًا دون استدعاء محرك تسليم البريد.
- إخفاق الذاكرة المؤقتة (Cache Miss): يقفل الخادم المفتاح، وينفّذ إرسال الرسالة، ويخزّن الاستجابة، ثم يعيدها إلى العميل.
- انتهاء الصلاحية: يُضبط المفتاح لينتهي بعد نافذة زمنية (مثلًا 24 ساعة) لمنع نمو قاعدة البيانات إلى ما لا نهاية.
مثال ملموس على الحمولة
هكذا ينبغي أن يبدو الطلب عند استخدام واجهة API مثل SendHQ:
POST /v1/send
Host: api.sendhq.cc
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer YOUR_API_KEY
{
"to": "user@example.com",
"template_id": "welcome-email",
"variables": {
"name": "Alex"
}
}
التعامل مع حالات الخطأ
لا ينبغي التعامل مع كل إعادات المحاولة بالطريقة نفسها. يجب أن تميّز بين أخطاء العميل وأخطاء الخادم.
- أخطاء 4xx: إذا أعاد الخادم 400 (Bad Request) أو 422 (Unprocessable Entity) فالطلب غير صالح. وإعادة المحاولة بالمفتاح نفسه ينبغي أن تعيد خطأ 4xx نفسه. لا تغيّر الحمولة وتعيد استخدام المفتاح، فهذا يسبب تعارضًا.
- أخطاء 5xx: إذا أعاد الخادم 500 أو 503، فينبغي للعميل إعادة المحاولة. وإذا كان الخادم قد سلّم الرسالة بنجاح بالفعل إلى MTA (وكيل نقل البريد)، فإن مفتاح عدم التكرار يضمن أن تعيد المحاولة 200 OK بدلًا من إرسال رسالة ثانية.
- الطلبات المتزامنة: إذا وصل طلبان متطابقان بالمفتاح نفسه في الملّي ثانية ذاتها، فينبغي أن يعيد الخادم 409 Conflict للطلب الثاني للإشارة إلى أن الأول ما زال قيد المعالجة.
عدم التكرار لوكلاء الذكاء الاصطناعي
تُدخل وكلاء الذكاء الاصطناعي (التي تستخدم خوادم MCP أو بطاقات A2A) طبقة جديدة من المخاطر. فقد تكون النماذج اللغوية الكبيرة غير حتمية، وقد تُطلق استدعاء الأداة نفسه عدة مرات إذا رأت فشلًا في الحلقة.
عند بناء تكاملات جاهزة للوكلاء، لا تسمح أبدًا لوكيل بإطلاق إجراء send دون خطوة موافقة أو مفتاح عدم تكرار حتمي يولّده المنسّق (orchestrator). وينبغي للمنسّق أن يربط نية الوكيل (مثل «أرسل التقرير الأسبوعي إلى Bob») بمفتاح ثابت مبني على معرّف التقرير والتاريخ. وهذا يمنع الوكيل من إرسال التقرير نفسه خمس مرات عن غير قصد لأنه «ظنّ» أن الاستدعاء الأول فشل.
تكلفة الإخفاق: مقارنة بين المزوّدين
عندما تتجاهل تطبيق عدم التكرار، فإنك لا تزعج المستخدمين فحسب بل تهدر المال أيضًا. وبينما بعض المزوّدين أرخص، تتضاعف تكلفة الرسائل المكررة بسرعة.
وفقًا لصفحات الأسعار الرسمية (حتى سبتمبر 2026):
- Amazon SES: تكلّف 0.10 USD لكل 1,000 رسالة بنموذج a la carte (أسعار Amazon SES). وتشمل الباقات المتدرجة الجديدة التي أُطلقت في 21 يوليو 2026 باقة Essentials (0.16 USD لكل 1,000)، وPro (0.22 USD لكل 1,000 مع 105 USD شهريًا لكل منطقة)، وEnterprise (0.23 USD لكل 1,000 مع 500 USD شهريًا).
- Resend: الباقة المجانية 3,000 رسالة شهريًا بحد أقصى 100 يوميًا. وباقة Pro بـ 20 USD شهريًا لـ 50,000 رسالة، مع رسوم تجاوز قدرها 0.90 USD لكل 1,000 (أسعار Resend).
- SendGrid: الباقة المجانية أصبحت الآن فترة تجريبية مدتها 60 يومًا، وتبدأ باقات Essentials من 19.95 USD شهريًا (أسعار SendGrid).
- Mailgun: تكلّف 15 USD شهريًا لـ 10,000 رسالة، مع رسوم تجاوز تتراوح بين 1.80 و1.10 USD لكل 1,000 (أسعار Mailgun).
- Postmark: تكلّف 15 USD شهريًا لـ 10,000 رسالة، مع رسوم تجاوز من 1.80 إلى 1.20 USD لكل 1,000 (أسعار Postmark).
لوضع ذلك في منظوره، يكلّف إرسال 50,000 رسالة نحو 5 USD على SES بنموذج a la carte، مقابل نحو 66 USD على باقات Postmark. وإذا ضاعفت حلقة إعادة محاولة بلا عدم تكرار حجمك عن غير قصد عشر مرات، فإن الفرق المالي بين المزوّدين يصبح بندًا كبيرًا في تقرير الحادث.
قابلية التسليم مقابل القبول
من الضروري أن تفهم أن عدم التكرار لا يحل إلا مشكلة القبول.
- القبول: تقبل واجهة API طلبك وتعيد 200 OK. وهنا تعمل مفاتيح عدم التكرار.
- التسليم: تسلّم واجهة API الرسالة إلى الخادم المستلِم (مثل Gmail). وهنا تكمن أهمية SPF وDKIM/DMARC.
- الوصول إلى صندوق الوارد: يقرر الخادم المستلِم ما إذا كانت الرسالة تذهب إلى صندوق الوارد أو مجلد البريد المزعج.
يضمن مفتاح عدم التكرار أنك تقبل الطلب مرة واحدة فقط. ولا يضمن أن الرسالة ستُسلَّم أو أنها ستتجنب مجلد البريد المزعج. وللتأكد من أن بنيتك التحتية مضبوطة بشكل صحيح للتسليم، ينبغي أن تستخدم أدوات مثل أداة فحص DNS للبريد من SendHQ للتحقق من سجلاتك.
قائمة التحقق للتنفيذ للمهندسين
إذا كنت تدقق منطق إرسال البريد لديك اليوم، فاستخدم قائمة التحقق هذه:
- إنشاء المفتاح من جهة العميل: هل تنشئ UUID v4 لكل نية فريدة لإرسال بريد إلكتروني؟
- تنفيذ الترويسة: هل يُمرَّر المفتاح في ترويسة قياسية (مثل
Idempotency-Key) بدلًا من متن الطلب؟ - طبقة التخزين: هل لديك TTL (Time To Live) على مفاتيح عدم التكرار لمنع تضخم التخزين؟
- القفل الذري: هل يستخدم خادمك قفلًا موزعًا (مثل
SET NXفي Redis) لمنع حالات السباق على المفتاح نفسه؟ - التخزين المؤقت للاستجابة: هل تخزّن الاستجابة الكاملة (رمز الحالة والمتن) لإعادتها إلى العميل عند إعادة المحاولة؟
- ضوابط الوكيل: عند استخدام وكلاء ذكاء اصطناعي، هل ينشئ منسق النظام المفتاح بدلًا من LLM؟
مثال برمجي: وسيط عدم التكرار بـ Node.js
فيما يلي مثال مبسّط لكيفية تطبيق هذا المنطق في بيئة Node.js باستخدام Redis.
const redis = require('redis');
const client = redis.createClient();
async function sendEmailHandler(req, res) {
const idempotencyKey = req.headers['idempotency-key'];
if (!idempotencyKey) {
return res.status(400).json({ error: 'Idempotency-Key header is required' });
}
// Try to acquire a lock and check for existing response
const cachedResponse = await client.get(`idempotency:${idempotencyKey}`);
if (cachedResponse) {
const { status, body } = JSON.parse(cachedResponse);
return res.status(status).json(body);
}
// Set a lock to prevent concurrent requests
const lock = await client.set(`lock:${idempotencyKey}`, 'true', 'NX', 'EX', 30);
if (!lock) {
return res.status(409).json({ error: 'Request is currently being processed' });
}
try {
// Actual email sending logic
const result = await emailProvider.send(req.body);
const responsePayload = {
status: 200,
body: result
};
// Cache the result for 24 hours
await client.set(`idempotency:${idempotencyKey}`, JSON.stringify(responsePayload), 'EX', 86400);
return res.status(200).json(result);
} catch (error) {
return res.status(500).json({ error: 'Internal Server Error' });
} finally {
await client.del(`lock:${idempotencyKey}`);
}
}
الخلاصة
عدم التكرار ليس «ميزة إضافية» للبريد المعاملاتي، بل شرط لأي نظام يقدّر تجربة المستخدم والتحكم في التكلفة. وبنقل مسؤولية التفرّد إلى العميل وتوفير آلية لتتبع ذلك التفرّد على الخادم، تزيل خطر الإرسال المكرر أثناء اضطراب الشبكة.
سواء كنت تبني منتج SaaS تقليديًا أو وكيل ذكاء اصطناعي مستقلًا، فإن التعامل مع البريد كأثر جانبي حاسم يضمن بقاء نظامك موثوقًا ورضا مستخدميك. ولواجهة API للبريد الإلكتروني تركّز على المطوّرين وتتعامل مع هذه التعقيدات، اطّلع على https://sendhq.cc.