الخادم الجانبي كان يعمل منذ وقت طويل - حساب المخطط المحدد لنفس date/time/lat/lon يُسترجع من الذاكرة المؤقتة، ولا يُعاد حسابه من جديد. لكن العميل ما شاف هذا. في الـ dashboard عمود “cache hit %” كان يظهر – لأن الـ backend كان يسجل cache-outcome في مقياس داخلي، مو في الاستجابة.
الآن كل response يحمل واحد من ثلاثة رؤوس:
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайномهذه تغيير صغير - رأس plus عمود واحد في api_request_log - لكنه يفتح فئة كاملة من التحسينات اللي كانت قبل كده بقعة عمياء.
كيف يعمل: مثال
Section titled “كيف يعمل: مثال”curl -I -X POST https://api.astroway.info/v1/chart \ -H "X-Api-Key: aw_live_..." \ -H "Content-Type: application/json" \ -d '{ "date": "1990-05-15", "time": "14:30:00", "timezoneOffset": 3, "latitude": 50.45, "longitude": 30.52 }' | grep -i x-cache# X-Cache: MISSنفذ مرة ثانية بنفس جسم الطلب:
# X-Cache: HITللـ endpoints من نوع /v1/transits/now (وقت ديناميكي) - X-Cache: BYPASS، لأن النتيجة تعتمد على Date.now() الحالي وما في فائدة من التخزين المؤقت.
أي endpoints وما تُرجع
Section titled “أي endpoints وما تُرجع”مش كل الـ endpoints تُخزن مؤقتًا - وهذا مقصود. التقسيم:
- حسابات المخطط المحددة (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) - تُخزن بالكامل. نمط متوقع MISS → HIT. - معتمد على الوقت (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) -BYPASS. الذاكرة المؤقتة ممكن تكون صحيحة بس لحد دقيقة، فالأبسط ما تخزنها أصلاً. - محتوى مولد بالذكاء الاصطناعي (
/v1/horoscope/personal,/v1/interpret/*):BYPASS. إجابات الـ LLM غير محددة حتى لو كان الـ prompt نفسه، التخزين المؤقت = تثبيت العشوائية. - Endpoints للعرض (
/v1/render/*):MISS/HITلبعضها،BYPASSللّي تستقبل payload كبير (eclipse-path بـ 500 نقطة).
العلامة على الـ endpoint المحدد تظهر مباشرة في الـ response - ما تحتاج تقرأ الوثائق لتعرف إذا كان مخزن مؤقتًا ولا لا.
تأثير التسعير: الطلبات المخزنة مؤقتًا ما زالت تكلف
Section titled “تأثير التسعير: الطلبات المخزنة مؤقتًا ما زالت تكلف”هذا أهم شيء للفهم - الطلبات المخزنة مؤقتًا تستمر في خصم الcredits من نفس الـ tier مثل الـ MISS. لماذا:
- التسعير عندنا مضبوط على قيمة الـ business للـ endpoint، مو على تكلفة الـ CPU.
/v1/chartتكلف نفس السعر، سواء أُعيد حساب المخطط أو جاء من الذاكرة المؤقتة - العميل حصل على نفس المخطط. - الشفافية. ما نحبش موقف أحد يدفع لـ MISS والآخر لـ HIT (نظريًا بسبب “حظك مع الذاكرة المؤقتة”). التسعير يكون متوقع.
- بنية الذاكرة المؤقتة: هي بنية تحتية، مو قيمة مضافة. إحنا ندعمها في الـ tier.
لكن هذا ما يعني إن X-Cache بلا معنى في سياق التسعير - هو يوضح إمكانيات معمارية للعميل (شوف القسم التالي).
ماذا تفعل بهذا على جانب العميل
Section titled “ماذا تفعل بهذا على جانب العميل”أربع أنماط عملية:
1. طبقة تخزين مؤقت على جانب العميل لـ MISS
Section titled “1. طبقة تخزين مؤقت على جانب العميل لـ MISS”إذا شفت X-Cache: MISS لطلب ممكن يتكرر (خريطة ميل لنفس الشخص)، خزن محليًا في Redis/Memcached/IndexedDB. الذاكرة المؤقتة على الخادم عندنا لها TTL وسياسة إخلاء - طبقة العميل مع TTL متحكم فيه بتتفادى خصم الcredits الزايدة.
import { Astroway } from "@astroway/sdk";
const cache = new Redis();const client = new Astroway({ apiKey: process.env.ASTROWAY_KEY, // optional: hook on response headers onResponse(req, res) { const cacheStatus = res.headers["x-cache"]; metrics.increment(`astroway.cache.${cacheStatus.toLowerCase()}`); },});
async function getChart(input: ChartInput) { const cacheKey = `chart:${hashChart(input)}`; const cached = await cache.get(cacheKey); if (cached) return JSON.parse(cached);
const chart = await client.chart.create(input); await cache.set(cacheKey, JSON.stringify(chart), "EX", 86400); return chart;}2. تجميع + dedupe على الـ backend
Section titled “2. تجميع + dedupe على الـ backend”إذا خدمتك تستقبل طلبات جماعية على نفس birth-data (حملة onboarding، حيث الزملاء يختبرون ببيانات تجريبية متساوية)، استخدم dedupe على أساس الوعود:
const inFlight = new Map<string, Promise<Chart>>();
function getChart(input: ChartInput) { const key = hashChart(input); if (inFlight.has(key)) return inFlight.get(key)!;
const promise = client.chart.create(input); inFlight.set(key, promise); promise.finally(() => inFlight.delete(key)); return promise;}هذا ما راح يوفر الcredits (كل استدعاء ما زال يُخصم)، لكنه يخفف الازدحام عند انفجارات الـ concurrency.
3. تسخين مسبق للمسارات الحرجة
Section titled “3. تسخين مسبق للمسارات الحرجة”إذا في المنتج مخططات ريتوالية (أبراج daily-horoscope المشهورة)، سِخّنها مسبقًا بـ cron مجدول. أول طلب في اليوم - MISS، كل البقية لحد الإخلاء - HIT. المستخدم يحصل على استجابة بأقل من ثانية.
4. نظرة من الـ Dashboard: أين تدفع زيادة
Section titled “4. نظرة من الـ Dashboard: أين تدفع زيادة”العمود الجديد cache hit % في /dashboard/usage لكل endpoint يوضح:
- HIT% = 90+ - الـ endpoint يتخزن جيدًا، غالبًا نفس المخطط يُعاد إرسالها عدة مرات. فكر في client-side dedupe (#2).
- HIT% = 0 و BYPASS: الـ endpoint غير مخزن مؤقتًا حسب التصميم (transits/now, AI). هذا طبيعي.
- HIT% = 50% و MISS: نصف الطلبات تحمل معلمات فريدة، والنصف الآخر تكرارات. يستحق تخزين مؤقت على جانب العميل.
- HIT% منخفض + endpoint محدد: مريب. تأكد إن عميلك ما يضيف حقول عشوائية في الـ payload (timestamps, request-id) اللي تملأ مفتاح الذاكرة المؤقتة.
تنفيذ تقني: للفضوليين
Section titled “تنفيذ تقني: للفضوليين”التتبع يضيف عمود واحد في api_request_log.cache_status (enum: MISS|HIT|BYPASS, migration 030). العمود يُملأ من نفس الـ handler اللي يقرر عن cache-lookup - ما في طلب إضافي لقاعدة البيانات.
GET /v1/me/usage/endpoints الآن يرجع cache_hit_pct الحقيقي بدلًا من null لكل endpoint في تاريخك. طريقة SDK client.me.usage.endpoints() ستحصل على الحقل تلقائيًا (الأنواع في إصدار codegen القادم).
ما التالي
Section titled “ما التالي”أدوات قياس الذاكرة المؤقتة تفتح مستوى وسط من التحسينات بين “بدون تخزين مؤقت” و “تخزين كامل على الحافة”. الخطوات القادمة في خارطة الطريق:
- رأس استجابة
Cache-Controlمع TTL حقيقي للـ endpoints المخزنة - سيسمح لـ CDN/proxy-cache على جانب العميل If-None-MatchETags: فحص MISS متكرر بدون payload كامل في الاستجابة- إحصائيات الذاكرة المؤقتة لكل مستخدم في الـ dashboard مع إمكانية الإبطال (مثلاً، إعادة حساب مخطط معين بعد تعديل birth-time)
الوثائق - /docs/api/ → Performance & Caching. مرجع X-Cache المحدد - في قسم الـ headers لكل endpoint.
نفس Swiss Ephemeris الموجود في Solar Fire - في 4 أسطر من الكود.
مفتاح مجاني بدون بطاقة. 5000 استدعاء شهرياً حتى الدفعة الأولى.