Server-side cache bij ons werkte al lang - een deterministische chart-berekening voor dezelfde date/time/lat/lon wordt uit de cache gehaald, in plaats van opnieuw berekend te worden. Maar de client zag dit niet. In het dashboard toonde de kolom ‘cache hit %’ een streepje (–) omdat de backend de cache-outcome logde naar een interne metric, niet naar het antwoord.
Nu bevat elke reactie één van drie headers:
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайномDit is een kleine wijziging - de header plus één kolom in api_request_log - maar het opent een hele klasse optimalisaties die eerder een blinde vlek waren.
Hoe het werkt: voorbeeld
Section titled “Hoe het werkt: voorbeeld”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: MISSVoer een tweede keer uit met hetzelfde aanvraagbody:
# X-Cache: HITVoor endpoints van het type /v1/transits/now (dynamische tijd) - X-Cache: BYPASS, omdat het resultaat afhangt van de huidige Date.now() en cachen geen zin heeft.
Welke endpoints retourneren
Section titled “Welke endpoints retourneren”Niet alle endpoints worden gecached - en dat is opzettelijk. Overzicht:
- Deterministische chart-berekeningen (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) - worden volledig gecached. Verwacht MISS → HIT patroon. - Time-dependent (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) -BYPASS. De cache zou slechts correct zijn tot het einde van de minuut, dus het is eenvoudiger om helemaal niet te cachen. - AI-generated content (
/v1/horoscope/personal,/v1/interpret/*):BYPASS. LLM-antwoorden zijn niet deterministisch, zelfs niet bij dezelfde prompt, cachen = vastleggen van willekeurigheid. - Render endpoints (
/v1/render/*):MISS/HITvoor sommige,BYPASSvoor diegene die grote payloads accepteren (eclipse-path met 500 punten).
De marker voor een specifiek endpoint is direct zichtbaar in de response - je hoeft de documentatie niet te lezen om te zien of het gecached wordt.
Pricing impact: gecachte verzoeken kosten nog steeds
Section titled “Pricing impact: gecachte verzoeken kosten nog steeds”Dit is het belangrijkste om te begrijpen - gecachte verzoeken blijven credits aftrekken op hetzelfde tier als MISS. Waarom:
- Onze pricing is gekalibreerd op de business value van een endpoint, niet op de CPU-kost.
/v1/chartkost hetzelfde, of de chart nu opnieuw berekend wordt of uit de cache komt - de client krijgt hetzelfde chart. - Transparantie. We willen geen situatie waarin één gebruikersgroep betaalt voor MISS en een andere voor HIT (theoretisch door ‘geluk met de cache’). Pricing voorspelbaar.
- Cache-infrastructuur: het is infra, geen toegevoegde waarde. We subsidiëren het binnen het tier.
Maar dit betekent niet dat X-Cache betekenisloos is in een pricing-context - het laat duidelijk architecturale mogelijkheden zien voor de client (zie volgende sectie).
Wat te doen aan de clientzijde
Section titled “Wat te doen aan de clientzijde”Vier praktische patronen:
1. Client-side cache-laag voor MISS
Section titled “1. Client-side cache-laag voor MISS”Als je X-Cache: MISS ziet voor een verzoek dat zich kan herhalen (het natal chart van dezelfde persoon), cache het lokaal in Redis/Memcached/IndexedDB. Onze server-side cache heeft een TTL en evict policy - jouw client-side laag met een beheersbare TTL voorkomt onnodige credit-aftrek.
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 op de backend
Section titled “2. Batch + dedupe op de backend”Als je service massale verzoeken ontvangt met dezelfde geboortedata (onboarding campagne, waar collegen dezelfde demo-data testen), gebruik dan 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;}Dit bespaart geen credits (elke aanroep wordt nog steeds meegeteld), maar het verwijdert knelpunten bij concurrentiepieken.
3. Pre-warm kritieke paden
Section titled “3. Pre-warm kritieke paden”Als er in het product rituele charts zijn (populaire dagelijkse horoscooptekens), pre-warm ze dan met een geplande cron. De eerste aanroep van de dag is MISS, alle volgende tot de evict zijn HIT. De gebruiker krijgt een subsecond response.
4. Dashboard-inzicht: waar je te veel betaalt
Section titled “4. Dashboard-inzicht: waar je te veel betaalt”De nieuwe kolom cache hit % in /dashboard/usage per endpoint laat zien:
- HIT% = 90+ - de endpoint wordt goed gecached, waarschijnlijk wordt hetzelfde chart meerdere keren verzonden. Overweeg client-side dedupe (#2).
- HIT% = 0 en BYPASS: de endpoint wordt niet gecached qua ontwerp (transits/now, AI). Dit is normaal.
- HIT% = 50% en MISS: de helft van de verzoeken heeft unieke parameters, de helft: herhalingen. Het loont om een client-side cache te gebruiken.
- Lage HIT% + deterministisch endpoint: verdacht. Controleer of je client geen willekeurige velden toevoegt aan de payload (timestamps, request-id) die de cache-sleutel verstoren.
Technische implementatie: voor de nieuwsgierigen
Section titled “Technische implementatie: voor de nieuwsgierigen”Tracking kost één kolom in api_request_log.cache_status (enum: MISS|HIT|BYPASS, migratie 030). De kolom wordt gevuld vanuit dezelfde handler die het besluit neemt over de cache-lookup - er wordt geen extra DB-query uitgevoerd.
GET /v1/me/usage/endpoints retourneert nu een echt cache_hit_pct in plaats van null voor elk endpoint in jouw geschiedenis. De SDK-methode client.me.usage.endpoints() krijgt dit veld automatisch (typen in de volgende codegen-release).
Wat nu
Section titled “Wat nu”Cache-instrumentatie opent een tussenliggend optimalisatieniveau tussen ‘geen caching’ en ‘volledige edge cache’. Volgende stappen op de roadmap:
Cache-Controlresponse-header met echt TTL voor gecachte endpoints - stelt CDN/proxy-cache aan clientzijde in staatIf-None-MatchETags: herhaalde MISS-controles zonder volledig payload in het antwoord- Per-user cache statistics in het dashboard met mogelijkheid tot invalidatie (bijv. geforceerd opnieuw berekenen van bepaalde chart na correctie van geboortetijd)
Documentatie - /docs/api/ → Performance & Caching. Specifieke X-Cache reference - in de headers sectie van elk endpoint.
Dezelfde Swiss Ephemeris als in Solar Fire - in 4 regels code.
Gratis sleutel zonder kaart. 5.000 calls per maand tot de eerste betaling.