Cache-ul server-side funcționează la noi de mult - un calcul determinist de hartă pentru aceleași date/time/lat/lon este returnat din cache, fără să fie recalculat. Dar clientul nu vedea asta. În dashboard, coloana “cache hit %” afișa –, pentru că backend-ul înregistra cache-outcome într-o metrică internă, nu în răspuns.
Тепер кожна response несе один з трьох заголовків:
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайномЦе маленька зміна - заголовок плюс одна колонка в api_request_log - але вона відкриває цілий клас оптимізацій, які раніше були сліпою плямою.
Cum funcționează: exemplu
Section titled “Cum funcționează: exemplu”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: MISSExecută a doua oară cu același corp al cererii:
# X-Cache: HITPentru endpoint-uri de tip /v1/transits/now (timp dinamic) - X-Cache: BYPASS, deoarece rezultatul depinde de Date.now() curent și nu are sens să fie cache-uit.
Ce endpoint-uri returnează ce
Section titled “Ce endpoint-uri returnează ce”Nu toate endpoint-urile sunt cache-uite - și asta este intenționat. Descompunere:
- Calculuri de hartă determinate (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) - sunt cache-uite complet. Modelul așteptat MISS → HIT. - Time-dependent (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) -BYPASS. Cache-ul ar putea fi corect doar până la sfârșitul minutei, așa că e mai simplu să nu cache-uiești deloc. - Conținut generat de AI (
/v1/horoscope/personal,/v1/interpret/*):BYPASS. Răspunsurile LLM nu sunt determinate nici măcar pentru același prompt, cache-ul = fixarea aleatorietății. - Endpoint-uri de render (
/v1/render/*):MISS/HITpentru unele,BYPASSpentru cele care primesc payload-uri mari (eclipse-path cu 500 de puncte).
Marcatorul pentru un endpoint specific este vizibil imediat în răspuns - nu trebuie să citești documentația pentru a înțelege dacă este cache-uit sau nu.
Impact asupra prețurilor: cererile cache-uite totuși costă
Section titled “Impact asupra prețurilor: cererile cache-uite totuși costă”Acesta este cel mai important lucru de înțeles - cererile cache-uite continuă să deducă credite din același tier ca și MISS. De ce:
- Prețurile noastre sunt calibrate pe valoarea de business a endpoint-ului, nu pe costul CPU.
/v1/chartcostă la fel indiferent dacă harta a fost recalculată sau a venit din cache - clientul a primit aceeași hartă. - Transparență. Nu vrem o situație în care un grup de utilizatori plătește pentru MISS, iar altul pentru HIT (teoretic din cauza „norocului cu cache-ul”). Prețurile sunt previzibile.
- Infrastructura de cache: este infrastructură, nu un value-add. O subvenționăm în tier.
Dar asta nu înseamnă că X-Cache este lipsit de sens în contextul prețurilor - arată vizibil posibilitățile arhitecturale pentru client (vezi secțiunea următoare).
Ce să faci cu asta pe partea clientului
Section titled “Ce să faci cu asta pe partea clientului”Patru pattern-uri practice:
1. Strat de cache pe client pentru MISS
Section titled “1. Strat de cache pe client pentru MISS”Dacă vezi X-Cache: MISS pentru o cerere a cărei scenariu poate să se repete (harta natală a aceleiași persoane), cache-uiește local în Redis/Memcached/IndexedDB. Cache-ul pe server are TTL și politică de evicție - stratul tău client cu TTL controlat va evita deduceri de credite suplimentare.
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 pe backend
Section titled “2. Batch + dedupe pe backend”Dacă serviciul tău primește cereri în masă pentru aceleași birth-data (campanie de onboarding, unde colegii testează cu aceleași date demo), folosește deduping promis:
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;}Acest lucru nu va economisi credite (fiecare apel tot deduce), dar elimină blocajele la explozii de concurență.
3. Pre-warm căi critice
Section titled “3. Pre-warm căi critice”Dacă în produs există hărți rituale (semnele populare de daily-horoscope), pre-warm-le cu un cron programat. Prima apelare a zilei - MISS, toate următoarele până la evict - HIT. Utilizatorul primește un răspuns subsecund.
4. Insight din dashboard: unde plătești în plus
Section titled “4. Insight din dashboard: unde plătești în plus”Noua coloană cache hit % în /dashboard/usage pe endpoint arată:
- HIT% = 90+ - endpoint este bine cache-uit, probabil aceeași hartă este trimisă de mai multe ori. Ia în considerare deduping pe client (#2).
- HIT% = 0 și BYPASS: endpoint nu este cache-uit prin design (transits/now, AI). E normal.
- HIT% = 50% și MISS: jumătate din cereri au parametri unici, jumătate sunt repetiții. Merită un cache pe client.
- HIT% scăzut + endpoint determinat: suspect. Verifică dacă clientul tău nu adaugă câmpuri aleatorii în payload (timestamps, request-id), care umplu cheia de cache.
Implementare tehnică: pentru curioși
Section titled “Implementare tehnică: pentru curioși”Tracking costă o coloană în api_request_log.cache_status (enum: MISS|HIT|BYPASS, migrare 030). Coloana este populată de același handler care decide despre cache-lookup - nu se face o interogare suplimentară la DB.
GET /v1/me/usage/endpoints acum returnează cache_hit_pct real în loc de null pentru fiecare endpoint din istoricul tău. Metoda SDK client.me.usage.endpoints() va primi câmpul automat (tipurile în următoarea lansare codegen).
Ce urmează
Section titled “Ce urmează”Instrumentarea cache-ului deschide un nivel intermediar de optimizări între „no caching” și „full edge cache”. Următoarele ajustări în roadmap:
- Antetul de răspuns
Cache-Controlcu TTL real pentru endpoint-urile cache-uite - va permite CDN/proxy-cache pe partea clientului If-None-MatchETags: verificări repetate de MISS fără payload complet în răspuns- Statistici de cache per utilizator în dashboard cu posibilitatea de invalidare (de exemplu, forțarea recalculării unei anumite hărți după corectarea birth-time)
Documentație - /docs/api/ → Performance & Caching. Referință specifică X-Cache - în secțiunea de antete a fiecărui endpoint.
Același Swiss Ephemeris ca în Solar Fire - în 4 linii de cod.
Cheie gratuită fără card. 5.000 de apeluri pe lună până la prima plată.