Loading…
Making Shopify’s Flagship App 20% Faster in 6 Weeks Using a Novel Caching Solution
2023-10-18
- Source
- Shopify
- Published
- Added to Yomu
Summary
Shop’s home feed, the app’s most used feature, aggregated orders from millions of Shopify and non-Shopify merchants plus tracking data from dozens of carriers, accounted for 30% of database load and affected application performance. Before each database update, the custom write-through cache marks a user’s cache invalid, applies the write, then refreshes and revalidates it. To handle concurrent updates, it uses a separate pending-writes key and a short-expiry key to distinguish active writes from interrupted processes, while Active Record Concerns preserve the existing ORM interface. After a staged validation rollout, the cache reduced database load by 15%, overall app latency by about 20%, and overall GraphQL latency by 20%, with a double-digit decrease in CPU usage.
Context
Shop’s home feed was the app’s most heavily used feature, but aggregating orders and tracking data was computationally expensive and slow. The feed accounted for 30% of database load, affecting performance across the application. Database-level optimization options were limited, and a large code rewrite was not feasible.
Approach / What changed
A custom write-through cache was built with distributed Memcached. Before database writes, the user’s cache is marked invalid; after a successful write, it is updated and marked valid. A pending-writes key supports concurrent updates, while a short-expiry key helps identify interrupted processes. Active Record Concerns integrated the behavior without changing the ORM API, and staged testing compared cached and database results before global rollout.
Takeaways
- The home feed cache uses invalidation before writes and revalidation afterward, preventing cached data from being served while the cache and database are out of sync.
- A per-user pending-writes counter handles concurrent order updates, while a short-expiry key helps distinguish an active write from a process that failed before decrementing the counter.
- After global rollout, database load fell 15%, overall app latency fell about 20%, overall GraphQL latency fell 20%, and CPU usage decreased by double digits.