La cache lato server funzionava da tempo: il calcolo deterministico di un tema per la stessa date/time/lat/lon viene restituito dalla cache, anziché essere ricalcolato. Ma il client non lo vedeva. Nella dashboard la colonna “cache hit %” mostrava – perché il backend registrava il cache-outcome in una metrica interna e non nella risposta.
Тепер кожна response несе один з трьох заголовків:
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайномЦе маленька зміна - заголовок плюс одна колонка в api_request_log - але вона відкриває цілий клас оптимізацій, які раніше були сліпою плямою.
Come funziona: esempio
Sezione intitolata “Come funziona: esempio”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: MISSEseguire nuovamente con lo stesso corpo della richiesta:
# X-Cache: HITPer gli endpoint di tipo /v1/transits/now (tempo dinamico) - X-Cache: BYPASS, perché il risultato dipende dal corrente Date.now() e non ha senso memorizzarlo nella cache.
Quali endpoint restituiscono
Sezione intitolata “Quali endpoint restituiscono”Non tutti gli endpoint vengono memorizzati nella cache - e questo è intenzionale. Suddivisione:
- Calcoli deterministici del tema natale (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) - vengono memorizzati completamente nella cache. Si prevede lo schema MISS → HIT. - Dipendenti dal tempo (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) -BYPASS. La cache potrebbe essere corretta solo fino alla fine del minuto, quindi è più semplice non memorizzarli affatto. - Contenuti generati da AI (
/v1/horoscope/personal,/v1/interpret/*):BYPASS. Le risposte dell’LLM non sono deterministiche anche con lo stesso prompt, memorizzarle = fissare la casualità. - Endpoint di rendering (
/v1/render/*):MISS/HITper alcuni,BYPASSper quelli che accettano payload grandi (eclipse-path con 500 punti).
L’indicatore per uno specifico endpoint è visibile immediatamente dalla risposta - non è necessario leggere la documentazione per capire se viene memorizzato nella cache o meno.
Impatto sui prezzi: le richieste memorizzate nella cache continuano comunque a costare
Sezione intitolata “Impatto sui prezzi: le richieste memorizzate nella cache continuano comunque a costare”Questa è la cosa più importante da capire - le richieste memorizzate nella cache continuano a detrarre crediti allo stesso tier delle MISS. Perché:
- Il nostro pricing è calibrato sul valore di business dell’endpoint, non sul costo CPU.
/v1/chartcosta lo stesso sia che il tema venga ricalcolato sia che arrivi dalla cache - il cliente riceve lo stesso tema natale. - Trasparenza. Non vogliamo una situazione in cui un gruppo di utenti paghi per MISS e un altro per HIT (teoricamente grazie alla “fortuna con la cache”). Prezzo prevedibile.
- L’infrastruttura della cache è infrastruttura, non valore aggiunto. La sussidiamo nel tier.
Ma ciò non significa che l’header X-Cache sia privo di significato nel contesto dei prezzi - esso mostra visibilmente le possibilità architettoniche per il cliente (vedi la sezione successiva).
Cosa fare con questo lato client
Sezione intitolata “Cosa fare con questo lato client”Quattro pattern pratici:
1. Strato di cache lato client per MISS
Sezione intitolata “1. Strato di cache lato client per MISS”Se vedi X-Cache: MISS per una richiesta che potrebbe ripetersi (il tema natale della stessa persona), memorizzala localmente in Redis/Memcached/IndexedDB. La cache lato server da noi ha TTL e policy di eviction - il tuo strato di cache lato client con TTL controllato eviterà crediti detratti inutilmente.
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 sul backend
Sezione intitolata “2. Batch + dedupe sul backend”Se il tuo servizio riceve richieste massive di dati di nascita identici (campagna di onboarding, dove i colleghi testano con gli stessi dati demo), usa il dedupe basato su promesse:
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;}Questo non farà risparmiare crediti (ogni chiamata viene comunque conteggiata), ma elimina i colli di bottiglia durante i picchi di concorrenza.
3. Pre-warming dei percorsi critici
Sezione intitolata “3. Pre-warming dei percorsi critici”Se nel prodotto ci sono temi rituali (segni popolari dell’oroscopo giornaliero), pre-warmarli con un cron programmato. La prima chiamata del giorno sarà MISS, tutte le successive fino all’eviction saranno HIT. L’utente ottiene una risposta sub-secondo.
4. Intuizione dalla dashboard: dove stai pagando di più
Sezione intitolata “4. Intuizione dalla dashboard: dove stai pagando di più”La nuova colonna cache hit % in /dashboard/usage per endpoint mostra:
- HIT% = 90+ - l’endpoint viene memorizzato bene nella cache, probabilmente lo stesso tema viene inviato più volte. Considera il dedupe lato client (#2).
- HIT% = 0 e BYPASS: l’endpoint non è progettato per essere memorizzato nella cache (transits/now, AI). È normale.
- HIT% = 50% e MISS: metà delle richieste ha parametri unici, metà sono ripetizioni. Vale la pena una cache lato client.
- HIT% basso + endpoint deterministico: sospetto. Controlla se il tuo client non aggiunge campi casuali al payload (timestamp, request-id) che riempiono la chiave della cache.
Implementazione tecnica: per i curiosi
Sezione intitolata “Implementazione tecnica: per i curiosi”Il tracciamento aggiunge una colonna in api_request_log.cache_status (enum: MISS|HIT|BYPASS, migrazione 030). La colonna viene popolata dallo stesso handler che prende la decisione sulla ricerca in cache - non viene effettuata alcuna query aggiuntiva al DB.
GET /v1/me/usage/endpoints ora restituisce il valore reale di cache_hit_pct invece di null per ogni endpoint nella tua cronologia. Il metodo SDK client.me.usage.endpoints() otterrà il campo automaticamente (i tipi nel prossimo rilascio di codegen).
Cosa succede dopo
Sezione intitolata “Cosa succede dopo”L’strumentazione della cache apre un livello intermedio di ottimizzazioni tra “no caching” e “full edge cache”. I prossimi passi nella roadmap:
- l’intestazione di risposta
Cache-Controlcon TTL reale per gli endpoint memorizzati nella cache - consentirà il caching CDN/proxy lato client If-None-MatchETags: nuovi controlli MISS senza il payload completo nella risposta- statistiche della cache per utente nella dashboard con possibilità di invalidazione (ad esempio, ricalcolo forzato di某个 chart dopo la correzione dell’ora di nascita)
Documentazione - /docs/api/ → Performance & Caching. Il riferimento specifico all’header X-Cache - nella sezione headers di ogni endpoint.
Lo stesso Swiss Ephemeris di Solar Fire - in 4 righe di codice.
Chiave API gratuita senza carta. 5 000 chiamate al mese fino al primo pagamento.