Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for Improvements: Enhanced Cache Control in Liquid #1857

Open
notkurt opened this issue Nov 21, 2024 · 1 comment
Open

Request for Improvements: Enhanced Cache Control in Liquid #1857

notkurt opened this issue Nov 21, 2024 · 1 comment

Comments

@notkurt
Copy link

notkurt commented Nov 21, 2024

Background

Optimising Time to First Byte (TTFB) plays a role in improving website performance and user experience on Shopify stores. Complex Liquid code, particularly in sections and snippets involving deeply nested loops and extensive logic, can significantly impact TTFB. Common examples include highly nested mega menus or mobile drawers that, while relatively static, are generally unoptimisable without bodges.

Other frameworks, such as Django, offer granular cache control mechanisms that allow developers to cache specific parts of a template. Introducing similar cache control features in Shopify Liquid would enable merchants and developers to improve rendering times and have greater control over how elements of their store are cached.

Proposal

Introduce Cache Tags for Sections and Snippets

  • Implementation of {% cache %} and {% endcache %} tags. Allow developers to wrap sections of Liquid code that should be cached, minimising the need to reprocess unchanged content.
  • Cache Duration and Key Management. Provide options to set cache lifetimes and specify keys, enabling cache invalidation.

Limitations

Implementing per-section or per-snippet caching will come with distinct limitations:

  • Restricted Objects: Caching would not apply to code containing dynamic objects like customer or cart. This restriction would need to be enforced at the SFR level and flagged in Theme Check to prevent improper caching attempts.
  • Theme Changes: Automatic invalidation of cache upon:
    • Theme template edits.
    • Publishing of a new theme.

Challenges with Existing Cache Handling

Opaque SFR Caching Mechanisms
The current SFR caching process lacks any documentation, with most knowledge being informally passed through developers. Tools like the Shopify Theme Inspector help identify issues, but unresolved cases force developers to resort to client-side rendering.

Client-Side Rendering - This adds complexity, disrupts clean server-side logic, and is less optimal for SEO. While search engines have improved at parsing client-side content, server-rendered content remains preferable.

Cache Miss Triggers
It's too easy to move a session into a x-cache: Miss state. For example:

  • A cart being created, even for something as trivial as setting a cart attribute. Popular analytics and tracking tools exacerbate this by setting cart attributes upon the first page view, leaving many buyers in a cache-less state after their first page view. The values are always unique to the session, and as such the cache is always unique to the session.

This in itself is probably an issue that could be looked at separately. Is there a path where these apps can set attributes that are not tied to the pages rendering lifecycle?

Additional Thoughts

  • Improved caching would not necessarily revolutionise store performance but would serve as a valuable tool for large merchants and complex stores.
  • Documentation on SFR caching is necessary to support developers in leveraging caching effectively. This would also empower app developers who extensively use cart attributes to make more informed decisions about the impact of using cart attributes.

Example Usage

{% cache %}

  <div class="image-banner">
      <!-- Generic image banner from Dawn, not using any dynamic sources with a few settings set -->
  </div>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until SFR's default cache expiry is met. Currently an image banner from Dawn with a few settings set takes ~30ms to render. Could this be pulled from an edge cache faster?

{% cache: expires_in: 3600 %}

  <nav class="mega-menu">
      <!-- Complex nested logic for mega menu with images & custom title parsing -->
  </nav>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached for 60 minutes. Changes to the contents of the mega menu would not be presented via the SFR until the cache object is marked as stale.

{% cache: key: section.settings.menu %}

  <nav class="mega-menu">
      <!-- Complex nested logic for mega menu with images & custom title parsing -->
  </nav>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until the computed hash of the menu object changes, or when the SFR's default cache expiry is met - Whatever comes sooner.

{% cache: key:card_product.updated_at %}

  <div class="product-card">
      <!-- Complex product card, with product swatches / options & multiple images  -->
  </div>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until the updated_at field changes, or when the SFR's default cache expiry is met - Whatever comes sooner. Please note: updated_at is not currently available under the product object within Liquid.

{% cache: key: card_product %}

  <div class="product-card">
      <!-- Complex product card, with product swatches / options & multiple images  -->
  </div>

{% endcache %}

Within the given context of the current theme, and current theme template, this would be cached until a computed hash of the product object changes, or when the SFR's default cache expiry is met - Whatever comes sooner.

{% cache %}

    <nav class="mega-menu">
      <p> {{ customer.first_name }}</p>
      <!-- Complex nested logic for mega menu with images & custom title parsing -->
  </nav>

{% endcache %}

This would not be cached since the customer object is referenced within the cache block. This would also raise a Theme Check error.

@MatthewRCrigger
Copy link

I really prefer to focus on the server-side Liquid render over client side rendering when I can do something in a theme.

I often roll the dice and use Liquid date filters with now and hope I'm on a resource that gets refreshed enough to not fall into an old cached render. I would rather cache things that won't change often enough, but let everything else be fresh. I really love to add time gating to resources by using date time fields and compare with now, but in some rare cases that has failed me. So if this helps me, I'd love to see it come through.

However, I feel like I would end up using a {% cache %} tag all over the place and just set the key to some resource high up resource as a quick "If any of this resource changes, just regenerate the whole thing". That seems a little lazy and bad practice, but most likely the reality of me using it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants