feat: المجموعة G — Redis خط أول والقاعدة خط احتياط
الأساس: common/cache — كاش-جانبي فوق Redis. مبدأ صارم: فشل Redis لا يُسقط الطلب (يُسجَّل ويُرجَع للقاعدة) — الكاش تحسين أداء لا مصدر حقيقة. - G1: لغة المستخدم في Redis (كانت استعلاماً قبل كل إشعار)؛ تُكتب عند PATCH /users/me فيصير الإصابة دائمة - G2/G3: توكنات FCM في Redis — يُبطَل عند register وعند اكتشاف توكن ميت. البث الجماعي صار بلا استعلامات قاعدة - G4: كاش tenant resolve/config + إبطال عند الإنشاء - G5: كاش التعرفة و ride-types + إبطال صريح عند أي إنشاء - G6: التقييم يتراكم في Redis، والقاعدة تُكتب مرة واحدة يومياً لكل طرف (حجز الكتابة ذرّي بـLua). التأخير مقصود ليبعد الاحتكاك بين الطرفين. أُضيف ratings.target_user_id و users.rating — تقييم السائق للراكب كان يُخزَّن بلا هدف فلا يُجمَّع أبداً - كاش الخرائط (route/reverse/geocode) بإحداثيات مقرَّبة 4 خانات (~11م): نداء انطلق كان يهيمن على p50 (2.9s). لا يُخزَّن الرجوع لخط مستقيم ولا استجابة فاشلة تصحيح: ادّعيت سابقاً أن tenants.resolve يعمل على كل request — خطأ. tenant_id يأتي من الـJWT؛ resolve يُستدعى عند الدخول و3 كنترولرات فقط. الحمل الأثقل فعلياً هو رفع موقع السائق (المجموعة H). هجرة: RatingsBothParties. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
857652404b
commit
a83dd7e5e5
+18
-14
@@ -77,19 +77,23 @@
|
||||
|
||||
---
|
||||
|
||||
## المجموعة G — Redis خط أول والقاعدة خط احتياط
|
||||
## المجموعة G — Redis خط أول والقاعدة خط احتياط — ✅ منفَّذة
|
||||
> مراجعة المالك (2026-07-17، الجولة الثانية): «كل ما بدي أبعث notification أستعلم من القاعدة — هذا ثقيل. الريدز خط أول، القاعدة نقطة الاحتياط».
|
||||
> القاعدة: البيانات **الساخنة والمتكررة** تُقرأ من Redis؛ القاعدة تُقرأ مرة واحدة عند البرود (cache miss) ثم تُكتب في Redis.
|
||||
> الأساس: `common/cache/cache.service.ts` — كاش-جانبي مع مبدأ **فشل Redis لا يُسقط الطلب** (يُسجَّل ويُرجَع للقاعدة).
|
||||
|
||||
| # | البند | التفصيل |
|
||||
|---|-------|---------|
|
||||
| G1 | **لغة المستخدم في Redis** | `users.getLanguage()` الآن استعلام قاعدة قبل **كل** إشعار. المطلوب: فلاتر يفحص لغة الجهاز عند الفتح ويرفعها → تُخزَّن في Redis (`user:lang:{tenant}:{user}`) → الإشعار يقرأ من هناك. القاعدة احتياط عند غياب المفتاح. |
|
||||
| G2 | **توكنات الأجهزة (FCM) في Redis** | `sendLocalized` يقرأ `device_tokens` من القاعدة لكل إرسال. تُخزَّن set لكل مستخدم في Redis. |
|
||||
| G3 | **`sendLocalizedToMany` عبر غرف Redis** | البث الجماعي يعتمد الغرف بدل استعلام لكل مستخدم — خصوصاً في dispatch. |
|
||||
| G4 | **`tenant` في Redis** | `TenantsService.resolve(slug→uuid)` يعمل على **كل** request (middleware). كاش دائم في Redis (يتغيّر نادراً جداً). أعلى نسبة قراءات في النظام. |
|
||||
| G5 | **التعرفة و ride-types في Redis** | جداول شبه ثابتة تُقرأ في كل تسعير/طلب — كاش مع إبطال عند تعديل الأدمن. |
|
||||
| G6 | **التقييم (ratings) بتراكم يومي** | التقييم يُجمَّع في Redis ويُكتب/يُحدَّث على القاعدة **مرة واحدة يومياً** لكل طرف (إن لم يكن حُدِّث بنفس اليوم). فائدتان: استجابة أسرع، وتأخير ظهور التقييم ~يوم كامل يبعد الاحتكاك بين الطرفين. **للطرفين معاً** (راكب وسائق). |
|
||||
| G7 | **سعة Redis** | رصد مساحة أكبر أو Redis منفصل عند الحاجة (خصوصاً dispatch + المواقع). حالياً DB 3 مشترك — راجع docs/14. |
|
||||
| # | البند | التفصيل | الحالة |
|
||||
|---|-------|---------|--------|
|
||||
| G1 | **لغة المستخدم في Redis** | فلاتر يفحص لغة الجهاز عند الفتح ويرفعها عبر `PATCH /users/me` → تُكتب في Redis مباشرة → الإشعار يقرأ من هناك. | ✅ `user:lang:{tenant}:{user}` (TTL يوم) + كتابة عند التحديث |
|
||||
| G2 | **توكنات الأجهزة (FCM) في Redis** | كان استعلام `device_tokens` قبل كل إرسال. | ✅ `user:fcm:{tenant}:{user}`؛ يُبطَل عند `register` وعند اكتشاف توكن ميت |
|
||||
| G3 | **البث الجماعي بلا استعلام لكل مستخدم** | — | ✅ التوكنات واللغة من الكاش، فالبث الجماعي صار بلا استعلامات قاعدة أصلاً |
|
||||
| G4 | **`tenant` في Redis** | كاش `resolve` (slug↔UUID) + `config` مع إبطال عند الإنشاء/التعديل. | ✅ `tenant:{slugOrId}` (TTL ساعة) |
|
||||
| G5 | **التعرفة و ride-types في Redis** | كاش مع **إبطال صريح** عند إنشاء تعرفة/نوع جديد. | ✅ `tariff:{t}:{city}:{class}` و `ridetypes:{t}` (TTL 15د) |
|
||||
| G6 | **التقييم بتراكم يومي** | مجموع/عدد في Redis، والقاعدة تُكتب **مرة واحدة يومياً** لكل طرف (حجز الكتابة ذرّي بـLua). التأخير مقصود ليبعد الاحتكاك. **للطرفين معاً**. | ✅ + أُضيف `ratings.target_user_id` و`users.rating` — تقييم السائق للراكب كان يُخزَّن **بلا هدف فلا يُجمَّع أبداً** |
|
||||
| — | **كاش الخرائط** | route/reverse/geocode في Redis بإحداثيات مقرَّبة 4 خانات (~11م). | ✅ TTL يوم. لا يُخزَّن الرجوع لخط مستقيم ولا استجابة فاشلة |
|
||||
| G7 | **سعة Redis** | رصد مساحة أكبر أو Redis منفصل عند الحاجة (خصوصاً dispatch + المواقع). حالياً DB 3 مشترك — راجع docs/14. | ⏳ قرار تشغيلي — يُراجَع بعد المجموعة H |
|
||||
|
||||
**تصحيح لادّعاء سابق في هذا المستند:** كُتب أن `TenantsService.resolve` يعمل على كل request وأنه «أعلى نسبة قراءات في النظام». **هذا خطأ** — الـmiddleware يضع نص الهيدر في السياق فقط، و`tenant_id` يأتي جاهزاً من داخل الـJWT. `resolve` يُستدعى عند الدخول و3 كنترولرات فقط. كاشه مفيد لكن أثره أصغر بكثير مما ادّعيت. **الحمل الحقيقي الأثقل هو رفع موقع السائق** (استعلام + كتابة قاعدة كل نبضة) — وهو المجموعة H.
|
||||
|
||||
---
|
||||
|
||||
@@ -175,10 +179,10 @@
|
||||
---
|
||||
|
||||
## ترتيب التنفيذ المقترح
|
||||
1. ~~**A** (الزمن الحقيقي + FCM + Redis + race)~~ ✅ **منفَّذة**.
|
||||
2. **I1** (سباق المحفظة) — **عاجل**: ثغرة مالية حيّة في كودنا، تُنفَّذ وحدها فوراً.
|
||||
3. **G** (Redis خط أول: لغة/توكنات/tenant/تعرفة/تقييم/كاش الخرائط) — امتداد طبيعي لـA.
|
||||
4. **H** (المواقع: إيقاف الكتابة لكل نبضة + batching + tracks).
|
||||
1. ~~**A** (الزمن الحقيقي + FCM + Redis + race)~~ ✅ **منفَّذة ومُثبَتة على السيرفر**.
|
||||
2. ~~**I1** (سباق المحفظة)~~ ✅ **منفَّذة ومُثبَتة بتزامن حقيقي**.
|
||||
3. ~~**G** (Redis خط أول: لغة/توكنات/tenant/تعرفة/تقييم/كاش الخرائط)~~ ✅ **منفَّذة**.
|
||||
4. **H** (المواقع: إيقاف الكتابة لكل نبضة + batching + tracks) — **التالي، وهو الحمل الأثقل فعلياً**.
|
||||
5. **B** (نموذج الرحلة: started/انتظار/فصل السعر/الطوابع/stops).
|
||||
6. **I** الباقي (المدفوعات: جداول لكل طريقة + OTP/بصمة/HMAC للسحب).
|
||||
7. **C** (بيانات المركبة والسائق + ai_data).
|
||||
|
||||
Reference in New Issue
Block a user