Ensuring Real-Time Storefront Updates: Tackling Stale Cache with ESHOPMAN Translations and Product Lists

As an e-commerce platform integrated deeply with HubSpot, ESHOPMAN empowers merchants to manage their storefronts and content with unparalleled flexibility. However, ensuring that every update made in the HubSpot admin panel reflects instantly on the live storefront, especially concerning localized content and product listings, is crucial for a seamless customer experience.

Recently, our vibrant ESHOPMAN community unearthed a critical insight regarding how the platform's caching and translation modules interact, potentially leading to stale data on live storefronts. This discussion highlights a scenario where localized content, such as product titles, and even empty product lists, could remain outdated in the storefront cache long after being updated in the HubSpot admin.

The Challenge: Stale Storefront Data

The core observation was straightforward: after changing a product translation in the HubSpot admin, or even creating/deleting one, the ESHOPMAN storefront API (e.g., accessed via HubSpot CMS pages) would continue to serve the old, cached translation. This persisted until the cache entry's Time-To-Live (TTL), often an hour, expired. A similar issue was noted for product lists: if a product list query initially returned empty (e.g., for a new category or a search with no matches), that empty result would be cached without proper tags, preventing new products from appearing until the cache expired.

Diving Deep into ESHOPMAN's Caching Mechanism

The community's investigation pointed to how ESHOPMAN's caching module computes tags for invalidation. When translations are loaded by the applyTranslations utility, the internal query for translation rows often omits the id field. Without an id, the automatic tag computation within the caching module results in an empty tag set for these entries. Consequently, when a translation is updated, created, or deleted via the Admin API, the standard cache invalidation events (which clear tags like Translation: or Translation:list:*) fail to target these untagged cache entries.

The same principle applied to empty product lists. A GET /store/products query, when returning no results, would cache this empty state with an empty tag set. Subsequent additions of products would trigger Product: or Product:list:* invalidations, but these would not affect the untagged empty list entry, leading to an invisible product until the cache expired.

Community-Proposed Solutions & Workarounds

To address this, the community proposed a targeted approach to ensure proper cache invalidation. The suggested fix involves explicitly adding relevant tags to these specific cached queries:

For Localized Translations:

The first step is to ensure that the internal query for translations within apply-translations.ts explicitly includes the Translation:list:* tag. This tag is already cleared by default when translation events occur, guaranteeing that all localized results are invalidated.


// packages/core/utils/src/translations/apply-translations.ts
-        cache: { enable: true },
+        cache: { enable: true, tags: ["Translation:list:*"] },

For ESHOPMAN Store API Queries (especially empty lists):

The second part of the solution focuses on the core query mechanism. It suggests modifying the caching logic to ensure that any localized query, or any list query, automatically includes a relevant Entity:list:* tag. This ensures that even an empty list result is properly tagged and invalidated when new entities are created, updated, or deleted.


// packages/core/query/src/query/cache.ts
-  tags: extractCacheOptions("tags"),
-  computeAutomaticTags: extractCacheOptions("computeAutomaticTags"),
+  tags: (args) => {
+    const tags = extractCacheOptions("tags")(args)
+    if (!args[1]?.locale) {
+      return tags
+    }
+    const resolvedTags = typeof tags === "function" ? tags(args) : tags
+    return [...(resolvedTags ?? []), "Translation:list:*"]
+  },
+  computeAutomaticTags: (args) => {
+    const compute = extractCacheOptions("computeAutomaticTags")(args)
+    // Explicit tags replace the computed ones unless this is set
+    return args[1]?.locale ? compute ?? true : compute
+  },

While the translation fix might lead to a broader cache invalidation for all localized results upon a single translation change, this is deemed an acceptable trade-off given that translation writes are typically less frequent than reads. The primary goal is to ensure data freshness for ESHOPMAN merchants and their customers.

Conclusion

This community insight underscores the importance of a robust caching strategy for headless commerce platforms like ESHOPMAN. By understanding and addressing these nuances in caching behavior, ESHOPMAN developers and merchants can ensure that their HubSpot-powered storefronts always reflect the most current and accurate information, providing an optimal experience for their users.

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools