Der serverseitige Cache funktionierte bei uns schon lange – eine deterministische Chart-Berechnung für dieselbe date/time/lat/lon wird aus dem Cache zurückgegeben, statt neu berechnet zu werden. Aber der Client sah das nicht. Im Dashboard zeigte die Spalte „cache hit %“ – an, weil das Backend das Cache-Ergebnis in eine interne Metrik und nicht in die Antwort protokollierte.
Jetzt enthält jede Response einen von drei Headern:
X-Cache: HIT # обслужено з кешуX-Cache: MISS # обчислено з нуля, результат збереженоX-Cache: BYPASS # не кешується за дизайномDas ist eine kleine Änderung – ein Header plus eine Spalte in api_request_log – aber sie eröffnet eine ganze Klasse von Optimierungen, die zuvor ein blinder Fleck waren.
Wie es funktioniert: Ein Beispiel
Abschnitt betitelt „Wie es funktioniert: Ein Beispiel“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: MISSFühre es ein zweites Mal mit demselben Request-Body aus:
# X-Cache: HITFür Endpoints wie /v1/transits/now (dynamische Zeit) – X-Cache: BYPASS, da das Ergebnis vom aktuellen Date.now() abhängt und Caching keinen Sinn macht.
Welche Endpoints was zurückgeben
Abschnitt betitelt „Welche Endpoints was zurückgeben“Nicht alle Endpoints werden gecacht – und das ist Absicht. Die Aufschlüsselung:
- Deterministische Chart-Berechnungen (
/v1/chart,/v1/houses,/v1/aspects,/v1/synastry,/v1/dasha/*,/v1/vargas/*) – werden vollständig gecacht. Erwartetes MISS → HIT Muster. - Zeitabhängige (
/v1/transits/now,/v1/horoscope/today,/v1/moon/phase-now) –BYPASS. Der Cache wäre nur bis zum Ende der Minute korrekt, daher ist es einfacher, überhaupt nicht zu cachen. - KI-generierte Inhalte (
/v1/horoscope/personal,/v1/interpret/*):BYPASS. LLM-Antworten sind selbst bei identischem Prompt nicht deterministisch, Caching würde Zufälligkeit fixieren. - Render-Endpoints (
/v1/render/*):MISS/HITfür einige,BYPASSfür diejenigen, die große Payloads akzeptieren (Eclipse-Pfad mit 500 Punkten).
Der Marker für einen spezifischen Endpoint ist direkt in der Response sichtbar – du musst die Dokumentation nicht lesen, um zu verstehen, ob er gecacht wird oder nicht.
Pricing-Auswirkungen: gecachte Anfragen kosten trotzdem
Abschnitt betitelt „Pricing-Auswirkungen: gecachte Anfragen kosten trotzdem“Das ist das Wichtigste zu verstehen – gecachte Anfragen ziehen weiterhin Credits ab auf demselben Tier wie MISS. Warum:
- Unser Pricing ist auf den Business Value des Endpoints kalibriert, nicht auf die CPU-Kosten.
/v1/chartkostet dasselbe, egal ob der Chart neu berechnet wurde oder aus dem Cache kam – der Client hat denselben Chart erhalten. - Transparenz. Wir wollen keine Situation, in der eine Benutzerbasis für MISS bezahlt und eine andere für HIT (theoretisch durch „Glück mit dem Cache“). Pricing ist vorhersehbar.
- Cache-Infrastruktur: Das ist Infrastruktur, kein Mehrwert. Wir subventionieren sie im Tier.
Das bedeutet aber nicht, dass X-Cache im Pricing-Kontext bedeutungslos ist – er zeigt sichtbar architektonische Möglichkeiten für den Client auf (siehe nächster Abschnitt).
Was du clientseitig damit tun kannst
Abschnitt betitelt „Was du clientseitig damit tun kannst“Vier praktische Muster:
1. Clientseitiger Cache-Layer für MISS
Abschnitt betitelt „1. Clientseitiger Cache-Layer für MISS“Wenn du X-Cache: MISS für eine Anfrage siehst, die sich im Szenario wiederholen könnte (Geburtskarte derselben Person), cache sie lokal in Redis/Memcached/IndexedDB. Unser serverseitiger Cache hat eine TTL und Evict-Policy – dein clientseitiger Layer mit kontrollierter TTL vermeidet unnötige Credit-Abzüge.
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 + Deduplizierung im Backend
Abschnitt betitelt „2. Batch + Deduplizierung im Backend“Wenn dein Dienst Massenanfragen für dieselben Geburtsdaten empfängt (Onboarding-Kampagne, bei der Kollegen mit denselben Demodaten testen), verwende eine Promise-basierte Deduplizierung:
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;}Das spart keine Credits (jeder Aufruf zählt trotzdem), aber es beseitigt Engpässe bei Concurrency-Spitzen.
3. Kritische Pfade vorwärmen
Abschnitt betitelt „3. Kritische Pfade vorwärmen“Wenn es im Produkt rituelle Charts gibt (beliebte Tageshoroskop-Zeichen), wärme sie mit einem geplanten Cron-Job vor. Der erste Aufruf des Tages ist MISS, alle folgenden bis zur Eviction sind HIT. Der Benutzer erhält eine Subsekunden-Antwort.
4. Dashboard-Einblick: Wo du zu viel bezahlst
Abschnitt betitelt „4. Dashboard-Einblick: Wo du zu viel bezahlst“Die neue Spalte „cache hit %“ in /dashboard/usage pro Endpoint zeigt:
- HIT% = 90+ – der Endpoint wird gut gecacht, wahrscheinlich wird derselbe Chart mehrmals gesendet. Erwäge clientseitige Deduplizierung (#2).
- HIT% = 0 und BYPASS: der Endpoint wird designbedingt nicht gecacht (transits/now, AI). Das ist normal.
- HIT% = 50% und MISS: die Hälfte der Anfragen enthält einzigartige Parameter, die andere Hälfte: Wiederholungen. Ein clientseitiger Cache ist lohnenswert.
- HIT% niedrig + Endpoint deterministisch: verdächtig. Überprüfe, ob dein Client keine zufälligen Felder in den Payload einfügt (Timestamps, request-id), die den Cache-Schlüssel verstopfen.
Technische Implementierung: für Neugierige
Abschnitt betitelt „Technische Implementierung: für Neugierige“Das Tracking kostet eine Spalte in api_request_log.cache_status (Enum: MISS|HIT|BYPASS, Migration 030). Die Spalte wird vom selben Handler gefüllt, der die Cache-Lookup-Entscheidung trifft – es wird keine zusätzliche DB-Anfrage gestellt.
GET /v1/me/usage/endpoints gibt jetzt das tatsächliche cache_hit_pct anstelle von null für jeden Endpoint in deiner Historie zurück. Die SDK-Methode client.me.usage.endpoints() erhält das Feld automatisch (Typen im nächsten Codegen-Release).
Was kommt als Nächstes
Abschnitt betitelt „Was kommt als Nächstes“Die Cache-Instrumentierung eröffnet eine Zwischenebene von Optimierungen zwischen „kein Caching“ und „vollständigem Edge-Cache“. Die nächsten Punkte auf der Roadmap:
Cache-Control-Response-Header mit realer TTL für gecachte Endpoints – ermöglicht CDN/Proxy-Cache auf ClientseiteIf-None-MatchETags: wiederholte MISS-Prüfungen ohne vollständigen Payload in der Antwort- Pro-Benutzer-Cache-Statistiken im Dashboard mit Invalidate-Möglichkeit (z.B. erzwungenes Neuberechnen eines bestimmten Charts nach Korrektur der Geburtszeit)
Dokumentation – /docs/api/ → Performance & Caching. Die spezifische X-Cache-Referenz findest du im Header-Bereich jedes Endpoints.
Derselbe Swiss Ephemeris wie in Solar Fire - in 4 Zeilen Code.
Kostenloser Schlüssel ohne Kreditkarte. 5.000 Aufrufe pro Monat vor der ersten Zahlung.