{"id":299,"date":"2021-08-31T14:40:03","date_gmt":"2021-08-31T14:40:03","guid":{"rendered":"https:\/\/fde.cat\/?p=299"},"modified":"2021-08-31T14:40:03","modified_gmt":"2021-08-31T14:40:03","slug":"caching-with-the-salesforce-commerce-sdk","status":"publish","type":"post","link":"https:\/\/fde.cat\/index.php\/2021\/08\/31\/caching-with-the-salesforce-commerce-sdk\/","title":{"rendered":"Caching with the Salesforce Commerce SDK"},"content":{"rendered":"<p><em>Co-written by <\/em><a href=\"https:\/\/medium.com\/@brianredmond6\"><em>Brian\u00a0Redmond<\/em><\/a><\/p>\n<p>Every e-commerce application is going to need caching. For some of our customers, millions of shoppers may look at the same product information and, if you have to request that information from the service every time, your application will not scale. This is why we built the <a href=\"https:\/\/salesforcecommercecloud.github.io\/commerce-sdk\/\">Commerce SDK<\/a> with caching as a primary consideration from the start. Here we will discuss how to implement a custom caching solution based on what we learned, demonstrate how to move to a distributed cache, and explore what our customers will see when they start using our new Commerce\u00a0SDK.<\/p>\n<h3>Commerce SDK out of the\u00a0box<\/h3>\n<p>Default SDK Behaviour<\/p>\n<p>The Commerce SDK uses an in-memory cache by default with a limit of 10,000 items. It has an eviction policy of \u201cleast recently used,\u201d meaning that we evict the least recently used response when you reach the limit. The caching behavior is determined by <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Cache-Control\">standard cache headers<\/a>. Responses marked as \u201cprivate\u201d are not cached, and authorization headers are stripped before writing the response to the cache.<\/p>\n<p> Let\u2019s look at how things work when making a call for product details using the Commerce\u00a0SDK.<\/p>\n<p>\/\/ Create a new ShopperProduct API client<br \/>const productClient = new Product.ShopperProducts(config);\/\/ Get product details<br \/>const details = await productClient.getProduct({<br \/>    parameters: {<br \/>        id: &#8220;25591139M&#8221;<br \/>    }<br \/>});Note: for complete code examples, please see the <a href=\"https:\/\/github.com\/SalesforceCommerceCloud\/commerce-sdk\">commerce-sdk README<\/a>)<\/p>\n<p>If we inspect the Cache-Control header of the response, we\u00a0see<\/p>\n<p>public, must-revalidate, max-age=60<\/p>\n<p>Responses may be marked as either public or private. Public responses should not contain any information specific to a requester and may be shared. Private responses should not be cached anywhere except the user\u2019s browser. Since the response in this example is public it is written to our cache. Because of the max-age, if we make the same request again within 60 seconds, we will use the response from our cache without making any request to the server. If we try the same request after 60 seconds has passed, the cached response will not be used and the request will continue to the\u00a0server.<\/p>\n<h3>Distributed Caching with\u00a0Redis<\/h3>\n<p>Our simple default cache is, well, simple. It\u2019s certainly adequate for development purposes and single instance applications. When we look at how it fits into a real-world application, the weaknesses begin to show. If we run our application as autoscaling containers, they would each have to maintain their own separate caches and manage the shared memory between the cache and the application, and the caches would disappear any time a container restarted. We provide a better\u00a0way.<\/p>\n<p>The Commerce SDK comes ready to connect to Redis by supplying your configuration info (also documented in the <a href=\"https:\/\/github.com\/SalesforceCommerceCloud\/commerce-sdk\">commerce-sdk README<\/a>). This allows us to scale out our application horizontally while benefiting from a shared\u00a0cache.<\/p>\n<h3>Bring your own Caching\u00a0Solution<\/h3>\n<p>Both of the caching options we\u2019ve discussed are implementations of the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Cache\">Cache API<\/a> documented on <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\">MDN<\/a>. The Commerce SDK will accept any implementation of that interface. A TypeScript definition of the interface can be imported\u00a0with:<\/p>\n<p>import { ICacheManager } from &#8220;@commerce-apps\/core\/dist\/base\/cacheManager&#8221;;export class CacheManagerCustom&lt;T&gt; implements ICacheManager { &#8230; }<\/p>\n<p>Before we jump to an entire implementation from scratch, let\u2019s break the cache manager into its two fundamental pieces: how to cache and where to cache. The cache manager implementations in the SDK separate the two concerns by using the pluggable <a href=\"https:\/\/github.com\/lukechilds\/keyv\">Keyv<\/a> storage interface. While only quick-lru and Redis have been tested in the Commerce SDK, you\u2019re free to test out other supported Keyv backends. Maybe we can try Memcached with the script we were working on earlier?<\/p>\n<p> We can get Memcached running locally with <a href=\"https:\/\/www.docker.com\/products\/docker-desktop\">Docker<\/a>,\u00a0e.g.:<\/p>\n<p>$ docker run -p11211:11211 &#8211;name memcached -d memcached<\/p>\n<p>and then install the <a href=\"https:\/\/github.com\/jaredwray\/keyv-memcache\">Keyv Memcache\u00a0package<\/a>:<\/p>\n<p>$ npm install &#8211;save keyv-memcache<\/p>\n<p>To configure it in our\u00a0script:<\/p>\n<p>import { CacheManagerKeyv } from &#8216;@commerce-apps\/core&#8217;;<br \/>const KeyvMemcache = require(&#8216;keyv-memcache&#8217;);<br \/>const memcache = new KeyvMemcache(&#8216;user:pass@localhost:11211&#8217;);const config = {<br \/>  cacheManager: new CacheManagerKeyv({ keyvStore: memcache }),<br \/>  &#8230;<br \/>}<\/p>\n<p>Now when we run our script again, the output will look nearly the same, but we can look in memcached and see that the response was\u00a0cached:<\/p>\n<p>$ docker exec -it &#8211;user root memcached bash<br \/>&#8230;# apt-get update &amp;&amp; apt-get install -y libmemcached-tools<br \/>&#8230;# memcdump &#8211;servers=localhostkeyv:keyv:request-cache-metadata:https:\/\/shortcode.api.commercecloud.salesforce.com\/product\/shopper-products\/v1\/organizations\/orgid\/products\/25591139M?siteId=RefArch<br \/>keyv:keyv:request-cache:https:\/\/shortcode.api.commercecloud.salesforce.com\/product\/shopper-products\/v1\/organizations\/orgid\/products\/25591139M?siteId=RefArch<\/p>\n<h3>Conclusion<\/h3>\n<p>This is just the tip of the iceberg, but you can see just how simple it is to utilize caching in a headless implementation of our commerce APIs utilizing our Commerce SDK. There is so much more you can read about this and what the Commerce SDK can do for you. To get started, just head over to our <a href=\"https:\/\/salesforcecommercecloud.github.io\/commerce-sdk\/\">commerce sdk documentation!<\/a><\/p>\n<p><a href=\"https:\/\/engineering.salesforce.com\/caching-with-the-salesforce-commerce-sdk-73bd90a9ad24\">Caching with the Salesforce Commerce SDK<\/a> was originally published in <a href=\"https:\/\/engineering.salesforce.com\/\">Salesforce Engineering<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>\n<p><a href=\"https:\/\/engineering.salesforce.com\/caching-with-the-salesforce-commerce-sdk-73bd90a9ad24?source=rss----cfe1120185d3---4\">Read More<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Co-written by Brian\u00a0Redmond Every e-commerce application is going to need caching. For some of our customers, millions of shoppers may look at the same product information and, if you have to request that information from the service every time, your application will not scale. This is why we built the Commerce SDK with caching as&hellip; <a class=\"more-link\" href=\"https:\/\/fde.cat\/index.php\/2021\/08\/31\/caching-with-the-salesforce-commerce-sdk\/\">Continue reading <span class=\"screen-reader-text\">Caching with the Salesforce Commerce SDK<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","footnotes":""},"categories":[7],"tags":[],"class_list":["post-299","post","type-post","status-publish","format-standard","hentry","category-technology","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":458,"url":"https:\/\/fde.cat\/index.php\/2021\/09\/20\/cachelib-facebooks-open-source-caching-engine-for-web-scale-services\/","url_meta":{"origin":299,"position":0},"title":"CacheLib, Facebook\u2019s open source caching engine for web-scale services","date":"September 20, 2021","format":false,"excerpt":"Caching plays an important role in helping people access their information efficiently. For example, when an email app loads, it temporarily caches some messages, so the user can refresh the page without the app retrieving the same messages. However, large-scale caching has long been a complex engineering challenge. Companies must\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":496,"url":"https:\/\/fde.cat\/index.php\/2021\/10\/26\/kangaroo-a-new-flash-cache-optimized-for-tiny-objects\/","url_meta":{"origin":299,"position":1},"title":"Kangaroo: A new flash cache optimized for tiny objects","date":"October 26, 2021","format":false,"excerpt":"What the research is:\u00a0 Kangaroo is a new flash cache that enables more efficient caching of tiny objects (objects that are ~100 bytes or less) and overcomes the challenges presented by existing flash cache designs. Since Kangaroo is implemented within CacheLib, Facebook\u2019s open source caching engine, developers can use Kangaroo\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":606,"url":"https:\/\/fde.cat\/index.php\/2022\/07\/14\/owl-distributing-content-at-meta-scale\/","url_meta":{"origin":299,"position":2},"title":"Owl: Distributing content at Meta scale","date":"July 14, 2022","format":false,"excerpt":"Being able to distribute large, widely -consumed objects (so-called hot content) efficiently to hosts is becoming increasingly important within Meta\u2019s private cloud. These are commonly distributed content types such as executables, code artifacts, AI models, and search indexes that help enable our software systems. Owl is a new system for\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":285,"url":"https:\/\/fde.cat\/index.php\/2021\/08\/31\/hadoop-hbase-on-kubernetes-and-public-cloud-part-ii\/","url_meta":{"origin":299,"position":3},"title":"Hadoop\/HBase on Kubernetes and Public Cloud (Part II)","date":"August 31, 2021","format":false,"excerpt":"The first part of this two part blog provided an introduction to concepts in Kubernetes and Public Cloud that are relevant to stateful application management. We also covered how Kubernetes and Hadoop features were leveraged to provide a highly available service. In this second part of the blog we will\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":288,"url":"https:\/\/fde.cat\/index.php\/2021\/08\/31\/building-a-successful-enterprise-ai-platform\/","url_meta":{"origin":299,"position":4},"title":"Building a Successful Enterprise AI Platform","date":"August 31, 2021","format":false,"excerpt":"IntroductionIn 2016, I started as a fresh grad software engineer at a small startup called MetaMind, which was acquired by Salesforce. Since then, it has been quite a journey to achieve a lot with a small team. I\u2019m part of Einstein Vision and Language Platform team. Our platform provides customers\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":497,"url":"https:\/\/fde.cat\/index.php\/2021\/10\/28\/carbonj-a-high-performance-high-scale-drop-in-replacement-for-carbon-cache-and-carbon-relay\/","url_meta":{"origin":299,"position":5},"title":"CarbonJ: A high performance, high-scale, drop-in replacement for carbon-cache and carbon-relay","date":"October 28, 2021","format":false,"excerpt":"The Problem In 2015, Salesforce Commerce Cloud (which was then called Demandware) was running a typical open source Grafana\/Graphite\/Carbon stack to store and visualize time series metrics of the Java application clusters powering our e-commerce business. Our JVM clusters at the time produced around 500k time series metrics per\u00a0minute. Even\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/299","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/comments?post=299"}],"version-history":[{"count":1,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/299\/revisions"}],"predecessor-version":[{"id":411,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/299\/revisions\/411"}],"wp:attachment":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/media?parent=299"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/categories?post=299"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/tags?post=299"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}