Le cache côté serveur chez nous fonctionnait depuis longtemps - le calcul déterministe d’un thème pour la même date/time/lat/lon est renvoyé depuis le cache, au lieu d’être recalculé. Mais le client ne voyait pas cela. Dans le tableau de bord, la colonne “cache hit %” affichait – parce que le backend journalisait le résultat du cache dans une métrique interne, et non dans la réponse.
Maintenant chaque réponse comporte l’un des trois en-têtes suivants :
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайномC’est un petit changement - un en-tête plus une colonne dans api_request_log - mais cela ouvre toute une catégorie d’optimisations qui étaient auparavant un point aveugle.
Comment ça fonctionne : exemple
Section intitulée « Comment ça fonctionne : exemple »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: MISSExécuter une deuxième fois avec le même corps de requête :
# X-Cache: HITPour les endpoints de type /v1/transits/now (temps dynamique) - X-Cache: BYPASS, car le résultat dépend du Date.now() actuel et mettre en cache n’a aucun sens.
Quels endpoints retournent
Section intitulée « Quels endpoints retournent »Tous les endpoints ne sont pas mis en cache - et c’est intentionnel. La répartition :
- Calculs de thème déterministes (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) - ils sont entièrement mis en cache. On s’attend à un schéma MISS → HIT. - Dépendant du temps (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) -BYPASS. Le cache pourrait être correct uniquement jusqu’à la fin de la minute, donc il est plus simple de ne pas mettre en cache du tout. - Contenu généré par IA (
/v1/horoscope/personal,/v1/interpret/*) :BYPASS. Les réponses du LLM ne sont pas déterministes même avec le même prompt, mettre en cache = figer l’aléatoire. - Endpoints de rendu (
/v1/render/*) :MISS/HITpour certains,BYPASSpour ceux qui acceptent de gros payloads (par exemple eclipse-path avec 500 points).
Le marqueur sur un endpoint spécifique est visible directement dans la réponse - il n’est pas nécessaire de lire la documentation pour savoir s’il est mis en cache ou non.
Impact sur la tarification : les requêtes mises en cache coûtent toujours
Section intitulée « Impact sur la tarification : les requêtes mises en cache coûtent toujours »C’est la chose la plus importante à comprendre - les requêtes mises en cache continuent de déduire des crédits au même niveau que les MISS. Pourquoi :
- Notre tarification est calibrée sur la valeur métier de l’endpoint, pas sur le coût CPU.
/v1/chartcoûte pareil que le thème soit recalculé ou servi depuis le cache - le client reçoit le même thème. - Transparence. Nous ne voulons pas de situation où un groupe d’utilisateurs paie pour les MISS et un autre pour les HIT (théoriquement à cause de la « chance avec le cache »). La tarification est prévisible.
- L’infrastructure de cache : c’est de l’infrastructure, pas une valeur ajoutée. Nous la subventionnons dans le niveau.
Mais cela ne signifie pas que l’en-tête X-Cache est inutile dans le contexte de tarification - il montre clairement les possibilités architecturales pour le client (voir la section suivante).
Que faire avec ceci côté client ?
Section intitulée « Que faire avec ceci côté client ? »Quatre modèles pratiques :
1. Couche de cache côté client pour les MISS
Section intitulée « 1. Couche de cache côté client pour les MISS »Si tu vois X-Cache: MISS pour une requête qui pourrait se répéter (la carte natale de la même personne), mets-la en cache localement dans Redis/Memcached/IndexedDB. Le cache côté serveur chez nous a un TTL et une politique d’éviction - ton couche côté client avec un TTL contrôlé évitera les déductions de crédits inutiles.
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 + déduplication côté backend
Section intitulée « 2. Batch + déduplication côté backend »Si ton service accepte des requêtes massives avec les mêmes données de naissance (campagne d’onboarding où les collègues testent avec les mêmes données de démonstration), utilise un déduplication basée sur des promesses :
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;}Cela ne permet pas d’économiser des crédits (chaque appel compte toujours), mais cela élimine les goulets d’étranglement lors de pics de concurrence.
3. Pré-chauffer les chemins critiques
Section intitulée « 3. Pré-chauffer les chemins critiques »Si dans ton produit il y a des thèmes rituels (signes d’horoscope quotidien populaires), préchauffe-les avec un cron planifié. Le premier appel de la journée sera MISS, tous les suivants jusqu’à l’éviction seront HIT. L’utilisateur obtient une réponse en sous-seconde.
4. Insight du tableau de bord : où tu paies trop
Section intitulée « 4. Insight du tableau de bord : où tu paies trop »La nouvelle colonne cache hit % dans /dashboard/usage par endpoint montre :
- HIT% = 90+ - l’endpoint est bien mis en cache, probablement le même thème est envoyé plusieurs fois. Envisage le déduplication côté client (#2).
- HIT% = 0 et BYPASS : l’endpoint n’est pas mis en cache par conception (transits/now, IA). C’est normal.
- HIT% = 50% et MISS : la moitié des requêtes comporte des paramètres uniques, l’autre moitié : des répétitions. Un cache côté client vaut la peine.
- HIT% faible + endpoint déterministe : suspect. Vérifie que ton client n’ajoute pas de champs aléatoires dans le payload (timestamps, request-id) qui encombrent la clé de cache.
Implémentation technique : pour les curieux
Section intitulée « Implémentation technique : pour les curieux »Le suivi ajoute une colonne dans api_request_log.cache_status (énumération : MISS|HIT|BYPASS, migration 030). Cette colonne est remplie par le même gestionnaire qui prend la décision concernant la recherche en cache - aucune requête supplémentaire vers la DB n’est effectuée.
GET /v1/me/usage/endpoints renvoie maintenant un véritable cache_hit_pct au lieu de null pour chaque endpoint de ton historique. La méthode SDK client.me.usage.endpoints() recevra ce champ automatiquement (les types seront disponibles dans la prochaine release de codegen).
Quoi ensuite ?
Section intitulée « Quoi ensuite ? »L’instrumentation du cache ouvre un niveau intermédiaire d’optimisations entre « pas de cache » et « cache edge complet ». Les prochaines étapes de la feuille de route :
- En-tête de réponse
Cache-Controlavec un TTL réel pour les endpoints mis en cache - permettra un cache CDN/proxy côté client - ETags
If-None-Match: vérifications MISS répétées sans charger le payload complet dans la réponse - Statistiques de cache par utilisateur dans le tableau de bord avec possibilité d’invalidation (par exemple, forcer le recalcul d’un certain thème après correction de l’heure de naissance)
Documentation - /docs/api/ → Performance & Caching. La référence spécifique à l’en-tête X-Cache se trouve dans la section headers de chaque endpoint.
Le même Swiss Ephemeris que Solar Fire - en 4 lignes de code.
Clé gratuite sans carte. 5 000 appels par mois avant le premier paiement.