Dit is het verhaal van hoe we een ERP-analysetool bouwden die in de eerste versie soms 32 seconden deed over een antwoord, en hoe we dat terugbrachten naar gemiddeld 0,4 seconden — zonder de realtime nauwkeurigheid op te offeren.
Voor je snel wegklikt: dit gaat niet over trucjes. Het gaat over een serieuze caching architectuur die je ook kunt toepassen op elk systeem met dure database-queries en een gebruiker die snel antwoord wil.
Startpunt: waarom duurt een ERP-query 32 seconden?
Microsoft Dynamics 365 Business Central is een uitstekend ERP-systeem voor transacties. Het is niet ontworpen voor analytische joins over miljoenen regels. Een query als "geef me de marge per klant per maand voor de afgelopen 18 maanden" vereist joins over Sales Invoice Header,Sales Invoice Line, Item, Customeren meerdere statistiekvelden — zonder indexen op analytische kolommen.
Op ons productiesysteem van Quality Textiles (2,3M orderregels, 6 jaar history) kostte zo'n query 15-32 seconden.
Laag 1: Materialized views — de grote winstpakker
De grootste winst zit in pre-aggregatie. We bouwden materialized views in Azure SQL die elke 15 minuten refreshen:
QT_Materialized_RevenueSummary— omzet per klant/periodeQT_Materialized_MarginAnalysis— marge per artikel/categorieQT_Materialized_AgingReport— debiteuren agingQT_Materialized_TopCustomers— top-N klanten per dimensie
Resultaat: de 32-seconden query daalde naar 93 milliseconden. Dat is een factor 340× sneller.
Lagen 2-5: Redis, memCache, client-side en semantisch
Zelfs 93ms kan verbeteren. Via Upstash Redis cachen we frequent gevraagde combinaties met een TTL van 60 minuten (configureerbaar). Voor "wat is de omzet van vandaag?" is 15 minuten data-vertraging acceptabel; voor een vraag over vorig kwartaal is zelfs 24u TTL prima.
De semantische cache bovenop alles is misschien de meest elegante laag: we slaan embeddings op van eerder gestelde vragen. Bij een nieuwe vraag berekenen we de cosine-similarity. Boven 0,92 similariteit geven we het gecachte antwoord terug — inclusief timestamp wanneer het gecached werd, zodat de gebruiker weet hoe actueel het is.
Monitoring: weten wanneer een laag faalt
Elke caching laag heeft een health check en rapporteert naar Sentry en onze interne observability sink in Supabase. Als Redis onbereikbaar is, valt het systeem naadloos terug op de volgende laag — zonder foutmelding voor de gebruiker, maar met een alert voor ons.
Samenvatting: resultaten na 3 maanden productie
- P50: 32s → 0,4s (80×)
- P95: 32s → 1,8s (17×)
- Cache hit rate: 67%
- AI-kosten (LLM tokens): 43% lager
- Gebruikerstevredenheid NPS: van 34 naar 71
De architectuur is beschikbaar voor alle AskBI-tenants en volledig configureerbaar via het beheerportal.