Η server-side cache λειτουργεί εδώ και καιρό - ένας ντετερμινιστικός υπολογισμός χάρτη για την ίδια date/time/lat/lon επιστρέφεται από την cache αντί να υπολογίζεται ξανά. Αλλά ο πελάτης δεν το έβλεπε. Στο dashboard η στήλη «cache hit %» έδειχνε – επειδή το backend κατέγραφε το cache-outcome σε εσωτερικό μετρικό, όχι στην απάντηση.
Τώρα κάθε response φέρει ένα από τα τρία headers:
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайномΑυτή είναι μια μικρή αλλαγή - ένα header συν μια στήλη στο api_request_log - αλλά ανοίγει μια ολόκληρη κατηγορία βελτιστοποιήσεων που πριν ήταν τυφλή κηλίδα.
Πώς λειτουργεί: παράδειγμα
Ενότητα με τίτλο «Πώς λειτουργεί: παράδειγμα»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() και δεν έχει νόημα να cache-αριστεί.
Ποια endpoints επιστρέφουν τι
Ενότητα με τίτλο «Ποια endpoints επιστρέφουν τι»Δεν όλα τα endpoints cache-αριστούν - και είναι σκόπιμο. Κατανομή:
- Καθορισμένοι υπολογισμοί χάρτη (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) - cache-αριστούν πλήρως. Αναμενόμενο pattern MISS → HIT. - Time-dependent (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) -BYPASS. Η cache θα μπορούσε να είναι σωστή μόνο μέχρι το τέλος του λεπτού, γι’ αυτό είναι πιο απλό να μην cache-αριστεί καθόλου. - AI-generated content (
/v1/horoscope/personal,/v1/interpret/*):BYPASS. Οι απαντήσεις LLM δεν είναι καθορισμένες ακόμη και για το ίδιο prompt, η cache = καταγραφή τυχαιότητας. - Render endpoints (
/v1/render/*):MISS/HITγια μερικά,BYPASSγια εκείνα που δέχονται μεγάλα payloads (π.χ. eclipse-path με 500 σημεία).
Το marker για το συγκεκριμένο endpoint φαίνεται αμέσως στην response - δεν χρειάζεται να διαβάσεις την τεκμηρίωση για να καταλάβεις αν cache-αρίζεται ή όχι.
Αντίκτυπο τιμολόγησης: τα cached αιτήματα παρόλα αυτά κοστίζουν
Ενότητα με τίτλο «Αντίκτυπο τιμολόγησης: τα cached αιτήματα παρόλα αυτά κοστίζουν»Αυτό είναι το πιο σημαντικό για κατανόηση - τα cached αιτήματα συνεχίζουν να deduct credits στο ίδιο tier με το MISS. Γιατί:
- Η τιμολόγηση μας είναι calibrated στην business value του endpoint, όχι στο CPU-cost. Το
/v1/chartκοστίζει το ίδιο είτε ο χάρτης υπολογίστηκε ξανά είτε ήρθε από την cache - ο πελάτης έλαβε τον ίδιο χάρτη. - Διαφάνεια. Δεν θέλουμε κατάσταση όπου μια user-base πληρώνει για MISS, ενώ άλλη για HIT (θεωρητικά λόγω «τυχερού cache»). Η τιμολόγηση είναι προβλέψιμη.
- Υποδομή cache: είναι infra, όχι value-add. Την subsidiize στο tier.
Αλλά αυτό δεν σημαίνει ότι το X-Cache είναι άσχετο στο pricing‑context - δείχνει οπτικά τις αρχιτεκτονικές δυνατότητες για τον πελάτη (δες την επόμενη ενότητα).
Τι να κάνεις με αυτό στην client‑side
Ενότητα με τίτλο «Τι να κάνεις με αυτό στην client‑side»Τέσσερα πρακτικά patterns:
1. Client-side cache layer για MISS
Ενότητα με τίτλο «1. Client-side cache layer για MISS»Αν βλέπεις X-Cache: MISS για ένα αίτημα που το σενάριο μπορεί να επαναληφθεί (γενέθιος χάρτης του ίδιου ατόμου), cache-ά το τοπικά σε Redis/Memcached/IndexedDB. Η server-side cache μας έχει TTL και πολιτική evict - το client‑side σου με ελεγχόμενο TTL θα αποφύγει περιττές credit‑deduct.
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. Batch + dedupe στο backend
Ενότητα με τίτλο «2. Batch + dedupe στο backend»Αν η υπηρεσία σου δέχεται μαζικά αιτήματα με τα ίδια birth-data (καμπάνια onboarding όπου οι συνάδελφοι δοκιμάζουν με τα ίδια demo‑data), χρησιμοποίησε promise‑based 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 (κάθε κλήση καταχωρείται ούτως ή άλλως), αλλά αφαιρεί τα bottlenecks σε spikes concurrency.
3. Pre-warm κρίσιμα paths
Ενότητα με τίτλο «3. Pre-warm κρίσιμα paths»Αν στο προϊόν υπάρχουν ρουχιστικά charts (δημοφιλή daily‑horoscope σημεία), pre-warm τα με scheduled cron. Η πρώτη κλήση της ημέρας - MISS, όλες οι επόμενες μέχρι το evict - HIT. Ο χρήστης λαμβάνει subsecond response.
4. Dashboard insight: πού υπερπληρώνετε
Ενότητα με τίτλο «4. Dashboard insight: πού υπερπληρώνετε»Η νέα στήλη cache hit % στο /dashboard/usage ανά endpoint δείχνει:
- HIT% = 90+ - το endpoint cache-αρίζεται καλά, πιθανώς ο ίδιος χάρτης αποστέλλεται πολλές φορές. Σκέψου client‑side dedupe (#2).
- HIT% = 0 και BYPASS: το endpoint δεν cache-αρίζεται κατά σχεδιασμό (transits/now, AI). Είναι φυσιολογικό.
- HIT% = 50% και MISS: τα μισά αιτήματα έχουν μοναδικές παραμέτρους, τα μισά: επαναλήψεις. Αξίζει client‑side cache.
- HIT% χαμηλό + endpoint καθορισμένο: ύποπτο. Έλεγξε αν ο πελάτης σου προσθέτει τυχαία fields στο payload (timestamps, request-id) που γεμίζουν το κλειδί cache.
Τεχνική υλοποίηση: για curious
Ενότητα με τίτλο «Τεχνική υλοποίηση: για curious»Το tracking προσθέτει μια στήλη στο api_request_log.cache_status (enum: MISS|HIT|BYPASS, migration 030). Η στήλη γεμίζει από τον ίδιο handler που αποφασίζει το cache‑lookup - δεν γίνεται επιπλέον ερώτημα στη DB.
GET /v1/me/usage/endpoints τώρα επιστρέφει το πραγματικό cache_hit_pct αντί για null για κάθε endpoint στο ιστορικό σου. Η SDK‑μέθοδος client.me.usage.endpoints() θα λαμβάνει το πεδίο αυτόματα (τύποι στο επόμενο codegen‑release).
Τι έπεται
Ενότητα με τίτλο «Τι έπεται»Η instrumentation του cache ανοίγει ένα ενδιάμεσο επίπεδο βελτιστοποιήσεων μεταξύ «no caching» και «full edge cache». Τα επόμενα βήματα στο roadmap:
Cache-Controlheader στην response με πραγματικό TTL για τα cached endpoints - θα επιτρέψει CDN/proxy‑cache στην client‑sideIf-None-MatchETags: επαναλαμβανόμενοι έλεγχοι MISS χωρίς πλήρες payload στην απάντηση- Στατιστικά cache ανά χρήστη στο dashboard με δυνατότητα invalidate (π.χ. εξαναγκασμός επανυπολογισμού κάποιου chart μετά τη διόρθωση του birth‑time)
Τεκμηρίωση - /docs/api/ → Performance & Caching. Συγκεκριμένη αναφορά X-Cache - στη ενότητα headers κάθε endpoint.
Αυτή η ίδια Swiss Ephemeris που υπάρχει στο Solar Fire - σε 4 γραμμές κώδικα.
Δωρεάν κλειδί χωρίς κάρτα. 5.000 αιτήματα ανά μήνα μέχρι την πρώτη πληρωμή.