Skip to content

Performance Monitoring

Dino Discover is built around a few performance properties. This page covers what they are, how we measure them, and where to look if a production install feels slow.

The most visible performance property is the size of the storefront JavaScript bundle. It’s built against Preact (via the preact/compat alias) instead of React proper, and gated on every PR by a CI tripwire.

  • Long-term target: ≤12 KB gzipped.
  • Current tripwire: ≤18 KB gzipped (transiently raised on 2026-05-22 to land the results-page and lead-capture surfaces). A trim follow-up to bring it back to 12 KB is tracked openly in the repo.

The build fails hard if the bundle exceeds the tripwire — so the budget can’t silently drift. The tripwire check lives in scripts/check-storefront-bundle-size.sh and runs at the end of npm run build.

[SCREENSHOT: The terminal output of npm run build showing the storefront-bundle-size check.]

The page that hosts the quiz is fast on first paint because Dino Discover injects only a ~1.5 KB inline bootstrap server-side. The full storefront bundle (Preact + the quiz engine + the components) only loads on the first shopper interaction — clicking Start, focusing the embed, or hitting the popup trigger when launchers ship.

So the cost on shoppers who scroll past the quiz without engaging is ~1.5 KB inline, no separate HTTP request.

The bootstrap response is variant-agnostic — variant assignment happens later, on session start — which means the bootstrap HTML is immutable and CDN-cacheable for a year (Cache-Control: public, max-age=31536000, immutable).

The places where speed matters most on a WooCommerce store are the cart and checkout pages. Dino Discover deliberately does not touch either:

  • No cart-time filters. The plugin registers no woocommerce_add_to_cart, woocommerce_cart_calculate_fees, or cart-totals filters. A page with the WooCommerce cart on it but no embedded quiz pays zero Dino-Discover cost.
  • No checkout-time filters. Same property at checkout.
  • Async lead push. When a lead is captured, the push to Klaviyo / Mailchimp / webhook happens on Action Scheduler — off the shopper’s request path entirely.
  • Async coverage refresh. CoverageCalculator runs on Action Scheduler, not synchronously inside POST /publish.

The shopper-facing endpoints that do run on the request path (/bootstrap, /sessions, /recommend) read from immutable snapshots and cached product expansions, so they don’t run database joins per request.

A few caches sit between the engines and the database:

CacheWhereWhat it caches
Snapshot read cacheWP object cacheThe full snapshot payload by ID, version-keyed. Invalidated on publish.
Product expansion cacheWP object cache (transient fallback)The per-target expansion of “all products this answer touches”, with a 24-hour TTL.
Result cacheWP object cacheThe most-recent recommendation payload per session, for the GET /sessions/{sid}/results re-read.

The cache layer lives in includes/Engine/Cache/. A CacheVersionRegistry keys every cache entry to a per-quiz version so that publishing a new snapshot invalidates the right entries without a wipe.

A typical storefront session does three database reads:

  1. The bootstrap call reads one snapshot row.
  2. The session-start call writes one session row.
  3. The recommend call reads the same snapshot (object-cached) and writes one event row.

No N+1 fan-outs on products. The product expansion cache means a recommendation against a flow that touches 500 products does not run 500 queries — it does one cache read.

When to use Catalogue Coverage’s debouncer

Section titled “When to use Catalogue Coverage’s debouncer”

If you have an automated workflow that publishes a quiz many times in quick succession (e.g. a script that bulk-updates upvote weights), the CoverageDebouncer will collapse the recompute into a single job at the end of the burst, rather than running once per publish.

You don’t need to configure this — it’s the default. Just be aware that the Coverage page will catch up after the last publish, not during.

There’s no in-product “performance dashboard” page in 0.1.0. To verify the bundle size on a production install:

Terminal window
curl -s 'https://yourshop.example/wp-content/plugins/dino-discover/build/storefront.js' \
| gzip -c | wc -c

That returns the gzipped byte count of the storefront bundle as served.

To verify the bootstrap size:

Terminal window
curl -s 'https://yourshop.example/page-with-quiz/' \
| grep -A 200 'id="dino-discover-bootstrap"'

The inline bootstrap appears between <script id="dino-discover-bootstrap"> tags.

If a page hosting the quiz feels slow, the usual suspects (in declining order of likelihood) are:

  1. A heavy theme. The host page’s other render cost dwarfs the ~1.5 KB bootstrap. Profile the theme.
  2. An aggressive page cache misconfigured. Caching /wp-json/dino-discover/* will break sessions. Exclude them.
  3. WooCommerce HPOS migration in flight. A site mid-migration can be slow on order writes; Dino Discover doesn’t touch orders, but the same site can have other surfaces that do.

If after those three the bundle still feels heavy, file a bug report with the gzipped size and the page URL.