Loading…
Simplify, Batch, and Cache: How We Optimized Server-side Storefront Rendering
2023-10-18
- Source
- Shopify
- Published
- Added to Yomu
Summary
The post explains how a new Ruby-based server-side Storefront Renderer reduced the time required to serve Shopify storefront requests. It combines MySQL multi-statement queries, handcrafted SQL, a thin data-mapping layer built from plain old Ruby objects, query book-keeping with eager- and lazy-loading, multiple LRU caching layers, and techniques for reducing memory allocations. For a product page, one database round trip can load the product, variants, images, shop, theme, and related resources; later requests can replay previously observed queries early, while less frequently used data remains lazy-loaded. The resulting renderer serves 75% of requests in under ~45ms, 90% in under ~230ms, and 99% in under ~900ms, with average response time nearly five times faster than the previous implementation.
Context
The previous implementation was slower, and the team needed to improve rendering time for storefront requests while efficiently loading the data required by Shopify Liquid themes and storefront traffic.
Approach / What changed
The renderer uses MySQL multi-statement queries and handcrafted SQL to reduce database round trips, plain old Ruby objects instead of a full ORM, query book-keeping for eager-loading, lazy-loading for less frequent data, multiple LRU caching layers, and memory-allocation profiling and benchmarks.
Takeaways
- MySQL multi-statement queries batch-load data for a request in one database round trip, including product, variant, image, shop, and theme information for a product page.
- Query book-keeping stores the queries used during a request and replays them early for later matching requests, while data not identified for eager-loading remains lazy-loaded.
- Method-specific memory benchmarks track allocation counts and bytes within ranges, helping prevent inefficient gems and memory-heavy changes from entering the renderer.