{"id":517,"date":"2021-12-16T16:47:48","date_gmt":"2021-12-16T16:47:48","guid":{"rendered":"https:\/\/fde.cat\/index.php\/2021\/12\/16\/event-sourcing-for-an-inventory-availability-solution\/"},"modified":"2021-12-16T16:47:48","modified_gmt":"2021-12-16T16:47:48","slug":"event-sourcing-for-an-inventory-availability-solution","status":"publish","type":"post","link":"https:\/\/fde.cat\/index.php\/2021\/12\/16\/event-sourcing-for-an-inventory-availability-solution\/","title":{"rendered":"Event Sourcing for an Inventory Availability Solution"},"content":{"rendered":"<h4>Co-author\u200a\u2014\u200aBalachandar Mariappan<\/h4>\n<h3>An Introduction to Terminology<\/h3>\n<p><strong>ATF<\/strong>\u200a\u2014\u200aAvailable to Fulfill inventory<strong>On-Hand\u200a<\/strong>\u2014\u200aPhysical amount of Inventory available<strong>SKU<\/strong>\u200a\u2014\u200aStock Keeping Unit, which represents a distinct type of item for\u00a0sale.<strong>Location<\/strong>\u200a\u2014\u200aRepresentation of a physical location like a store or warehouse where SKU\u2019s are\u00a0present<strong>Location Group<\/strong>\u200a\u2014\u200aA Logical aggregation of typically one or more Locations.<strong>Reservation or Inventory Reservation<\/strong>\u200a\u2014\u200aReserving a quantity of a SKU. For example: Reservation of 5 iPod-Nanos-Red<strong>Inventory Records<\/strong>\u200a\u2014\u200aA quantity for a SKU at a particular location.<\/p>\n<h3>The Opportunity<\/h3>\n<p><a href=\"https:\/\/www.salesforce.com\/products\/commerce-cloud\/overview\/\">Salesforce Commerce Cloud<\/a> is a product that provides e-commerce solutions for both business-to-business (B2B) <em>and <\/em>business-to-consumer (B2C) companies. To continue enhancing what we are able to offer our customers, we needed to build a multi-tenant, omnichannel inventory availability solution that could be shared by multiple Salesforce products, including B2C Enterprise, Order Management, and B2B. This solution needed to be highly available, highly performant, and highly accurate in its bookkeeping, while unlocking new features like <em>Location Level Inventory Aggregation.<\/em><\/p>\n<p>Location Level Inventory Aggregation is a feature that enables the aggregation of inventory from one or more locations into a Location Group, thereby allowing inventory reservations to be performed at either Location or Location Group level for <em>BOPIS<\/em> (Buy On-line and Pickup in store) among other omnichannel scenarios.<\/p>\n<p>The solution which was to be built had to deal with high contention reservation and row lock concerns associated with scenarios like flash sales over a few highly sought after items in a limited time\u00a0window.<\/p>\n<p>Put yourselves in the shoes of a major retailer with the holiday shopping season fast approaching and you\u2019ll be primed to understand the need for this solution.<\/p>\n<h3>Requirements<\/h3>\n<h4>Functional Requirements<\/h4>\n<p><strong>Accuracy<\/strong>: Inventory reservations must be accurate<br \/>\u200a\u2014\u200a<strong>No duplicate<\/strong> reservations should be allowed<br \/>\u200a\u2014\u200aSolution must <strong>prevent overselling<\/strong>, i.e., prevent an inventory reservation from happening if there is no quantity available.<strong>Consistency<\/strong>: Availability reads can display relatively stale inventory levels and do not need be immediately consistent to what is written.<br \/>\u200a\u2014\u200aNo need for inventory updates to be reflected in a call to obtain availability immediately after an inventory update. Acceptable to have up to <strong>60<\/strong> seconds of staleness between the time the write is reflected in the\u00a0read.<\/p>\n<h4>Non-Functional Requirements<\/h4>\n<p>The solution was expected to support thousands of tenants globally with the maximum number of inventory records per tenant to extend into the billions. The solution also needed to be able to process hundreds of reservation requests per second with low latency while doing the same returning availability of <em>SKUs<\/em> for each tenant. There was also a requirement to maintain an audit log of changes to the inventory for diagnosability and forensics.<\/p>\n<p>Thinking back to our imagined role as a major retailer, we can put these requirements even more simply: if someone reserves a pair of, say, sneakers, that reservation needs to be based on inventory that actually exists, and the person shouldn\u2019t be able to reserve the same pair of shoes twice. As the retailer, you need everything behind the scenes of your website to happen seamlessly.<\/p>\n<p>So how did we satisfy all of these requirements in way that could scale to the levels needed by our many customers?<\/p>\n<h3>Direction for Inventory Solution<\/h3>\n<h4>Event Sourcing<\/h4>\n<p><a href=\"https:\/\/microservices.io\/patterns\/data\/event-sourcing.html\">Event Sourcing<\/a> pattern is an approach to operations around data where changes are recorded to an <strong>append-only<\/strong> datastore in a sequential manner as discrete immutable events. The store behaves as the system of record maintaining the history of changes, and domain objects can be materialized by replaying the individual events and <strong>folding<\/strong> them into the aggregate.<\/p>\n<p>Event Sourcing<\/p>\n<p>Or as Greg Young stated it rather simplistically \u2014<\/p>\n<p>\u200a\u2014\u200a<a href=\"https:\/\/twitter.com\/gregyoung\/status\/313358540821647360\">@gregyoung<\/a><\/p>\n<p>Reconstituting the aggregate by folding the events up during every query is not tractable. For this reason <em>SNAPSHOTs<\/em> are implemented and only the events since the last snapshot are folded-in to provide the current state of the aggregate. We should also note that the Event Log is an ordered sequence of <strong>facts<\/strong> of things that have happened and is not series of intentions or commands.<\/p>\n<p>Reconstituting the aggregate by folding the events up during every query is not tractable. For this reason, <em>SNAPSHOTs<\/em> are implemented and only the events since the last snapshot are <em>folded-in<\/em> to provide the current state of the aggregate. We should also note that the Event Log is an ordered sequence of <strong>facts<\/strong> of things that have happened and is not series of intentions or commands.<\/p>\n<p>Inventory availability mutations typically occur to change<em> on-hand<\/em> values or create reservations. These operations can positively or negatively affect the Available to Fulfill (ATF) of a stock-keeping unit (SKU, basically a barcode, but used here to mean more generally \u201can\u00a0item\u201d).<\/p>\n<p>For the sake of illustration in this blog, let us consider a fictional merchant, <em>Acme. <\/em>Acme has a website, <a href=\"http:\/\/acme.com\/\">acme.com<\/a>, and has a warehouse in Salt Lake City, UT, from where they ship their merchandise. In addition, Acme also has physical stores in Las Vegas, NV and Seattle, WA. These two stores also support shipping from the store of products purchased on acme.com.<\/p>\n<p>Acme Location Graph\u00a0Setup<\/p>\n<p>In the example, we have a SKU, <em>acme-hat-blue<\/em> which is present at the warehouse, and at the Seattle and Las Vegas stores. The warehouse has 220 on-hand and there are 20 reservations made against it, effectively leaving 200 items that can be fulfilled by the warehouse\u200a\u2014\u200aare available to be sold. The Location Inventory State is shown\u00a0below:<\/p>\n<p>Location Inventory Aggregate<\/p>\n<p>In the Event Sourcing model, a bunch of <strong>immutable<\/strong> events occur and these are then applied in order to build the state of the aggregate.<\/p>\n<p>The following shows a sample of how the <em>Location Inventory Aggregate<\/em> is built over time with different events. The yellow rows on the table show the impacted data points at each\u00a0state:<\/p>\n<p>Location Inventory Aggregate Updates over\u00a0time<\/p>\n<p>In the example shown above, the initial state of the aggregate is <strong>NULL<\/strong>, i.e., nothing in it. When the first import event comes in, it gets applied, and the aggregate reflects the same. Subsequent reservation events affect the aggregate similarly, thereby altering the ATF\u00a0number.<\/p>\n<p>The important thing to note here is that the Event Log is a sequence of persistent facts that have occurred, and, at any time, the aggregate can be reconstituted from the Event Log by replaying all events since inception. However, this is not likely to be tractable as the number of events grow. To avoid this, events are folded and <strong>snapshotted<\/strong> at a regular interval and stored in an entity called <em>Location Inventory Aggregate<\/em> as\u00a0shown.<\/p>\n<h4>Using Subscriptions to create the Group Inventory View<\/h4>\n<p>As we needed to surface the Location Group Inventory by aggregating inventory across different locations, we do the same using an entity that is realized called <strong>GroupInventoryView<\/strong>. This Group Inventory View has the aggregated inventory counts in\u00a0it.<\/p>\n<p>Group Inventory View<\/p>\n<p><em>GroupInventoryView<\/em> entries are computed by <strong>rolling\/folding<\/strong> up the inventory availability of the different locations part of the group <strong>into<\/strong> the group. One of our initial approaches involved computing this aggregation on the fly when an API\u2019s invocation was performed to obtain the Location Group\u2019s inventory where the different location level inventory would be combined to serve the Group Level Numbers. This, however, proved to be a performance concern due to the amount of I\/O, slower response time, and additional compute added to the invocation.<\/p>\n<p>To address this problem, we availed one of the core benefits of Event Sourcing, which are <strong>subscriptions<\/strong> where additional actions can be taken by subscribing to the Event Sourced Log. This is similar to Change Data Capture (CDC) in databases. We utilized this CDC ability to roll up the Location level inventory availability into the <em>GroupInventoryView<\/em>by subscribing to the event stream and updating the Location Group numbers and persisting them to the <em>GroupInventoryView<\/em>. This approach worked for us due to the <strong>acceptable staleness<\/strong> of the records allowed for the <em>GroupInventoryView<\/em>.<\/p>\n<p>Group Inventory View Update using Event Sourcing Subscription Feature<\/p>\n<h4>High Contention on Select SKUs during Reservations<\/h4>\n<p>High Contention Reservation<\/p>\n<p>One of our primary objectives is to avoid overselling, as outlined in the first functional requirement above, while also providing a performant and responsive experience during inventory reservation. When inventory approaches low levels, if more than a single caller is competing to grab the item, the regular approach using a transactional datastore is to lock the row and update\/decrement the inventory would result in large latency due to high contention. In other words, a website, store terminal or other consumer attempting to do inventory reservations would be slow, and the request might even error out without completing, so the user experience of the person trying to reserve the item would\u00a0suffer.<\/p>\n<p>To address this problem, we decided to use an <strong><em>optimistic concurrency <\/em><\/strong>approach<strong><em> <\/em><\/strong>to avoid row locking. We utilize an <strong>Event Sequence number that <\/strong>is monotonically increasing associated with a <strong>SKU-Location<\/strong> record.<\/p>\n<p>The following shows a sample <em>Location Inventory Events for Reservation and Inventory Import<\/em>:<\/p>\n<p>Location Inventory Event for Reservation \u2014<\/p>\n<p>{ &#8220;name&#8221; : &#8220;LocationInventoryEvent&#8221;,<br \/>  &#8220;event_type&#8221; : &#8220;<strong>LocationReservation<\/strong>&#8220;,<br \/>  &#8220;sku&#8221; : &#8220;acme-hat-blue&#8221;,<br \/>  &#8220;location_id&#8221; : &#8220;warehouse&#8221;,<br \/>  &#8220;event_sequence_number&#8221; : 1122,<br \/>  &#8220;quantity&#8221;: <strong>5<\/strong><br \/>}<\/p>\n<p>Location Inventory Event for Inventory Import\u00a0\u2014<\/p>\n<p>{<br \/>  &#8220;name&#8221; : &#8220;LocationInventoryEvent&#8221;,<br \/>  &#8220;event_type&#8221; : &#8220;<strong>Import<\/strong>&#8220;,<br \/>  &#8220;sku&#8221; : &#8220;acme-hat-blue&#8221;,<br \/>  &#8220;location_id&#8221; : &#8220;warehouse&#8221;,<br \/>  &#8220;event_sequence_number&#8221; : 1122,<br \/>  &#8220;on_hand&#8221; : <strong>423<\/strong>,<br \/>  &#8230;<br \/>}<\/p>\n<p>The algorithm to apply a <strong>reservation<\/strong> event looks like\u00a0this:<\/p>\n<p>function <strong>reserveInventory<\/strong> [Event <strong>reservationEvent<\/strong>, <br \/>                EventLog <strong>eventLog<\/strong>, <br \/>                LocationInventoryAggregateTable <strong>aggregateTable<\/strong>]<br \/>               1. current_sequence_number <br \/>         = <strong>eventLog<\/strong>.query(<strong>reservationEvent<\/strong>.location_id,            <strong>reservationEvent<\/strong>.sku)                    <\/p>\n<p>2. if <strong>eventLog<\/strong>.get(<strong>reservationEvent<\/strong>.eventId) is <strong>not<\/strong> <strong>NULL<\/strong> <br \/>     return <strong>error<\/strong>   \/\/ Idempotence check                                                                                        3. aggregate =<br \/>        <strong>aggregateTable<\/strong>.readAggregate(<strong>reservationEvent<\/strong>.location_id, <br \/>                    <strong>reservationEvent<\/strong>.sku)\/\/ Get all Events not applied to aggregate including event with the current_sequence_number<br \/>4. pending_event_list =<br \/>       <strong>eventLog<\/strong>.getPendingEvents(<strong>aggregate.sequence_number<\/strong>,<br \/>                                 <strong>current_sequence_number<\/strong>)\/\/ Re-Constitute the Aggregate State<br \/>5. updatedAggregateState = <strong>foldEventsIntoAggregate<\/strong>(<strong>aggregate<\/strong>, <strong>pending_event_list<\/strong>)6. if <strong>reservationEvent<\/strong>.quantity &gt; <strong>updatedAggregateState<\/strong>.ATF<br \/>    return <strong>error<\/strong> \/\/ Insufficient quantity constraint violation  <\/p>\n<p>7. <strong>reservationEvent<\/strong>.event_sequence_number <br \/>          = current_sequence_number + <strong>1<\/strong>\/\/ Constraint on <strong>sequence_number<\/strong> [<em>location_id, sku, sequence_number<\/em>]<br \/>8. result = insert <strong>reservationEvent<\/strong> into <strong>eventLog<\/strong> 9. if result is <strong>false<\/strong><br \/>     restart from <strong>Step #1<\/strong> until a timeout or finite retries<\/p>\n<p>10. return <strong>success<\/strong><\/p>\n<p>The event log is queried to determine the current sequence number of the last inserted event. The Reservation Event\u2019s sequence number is set to one above the last known sequence number. An append of the Event is then attempted on the event log. A constraint exists on the event log that ensures uniqueness of <strong>[Location-id, SKU, event_sequence_number], <\/strong>so if another thread had attempted to insert an event for the SKU\/Location and had gotten through, this insert would have failed. A retry is attempted for a few times before giving\u00a0up.<\/p>\n<h4>Staleness of Read\u00a0Data<\/h4>\n<p>Reads performed against the inventory solution by consumers are not promised to be strongly consistent.<\/p>\n<p><strong>Weakly Vs. Strongly Consistent Read<\/strong><\/p>\n<p>Queries performed against the aggregate represent data that is not guaranteed to be strongly consistent and reflective of the latest state of the event log. This is acceptable for almost all read use cases where stale data on the READ side is fine. If a more consistent read were required, it would entail reading in the Location Inventory Aggregate and applying all events in the stream to it in real time, which could result in higher\u00a0latency.<\/p>\n<h3>Other Options Considered for the\u00a0Solution<\/h3>\n<p>We considered other options to solve this problem, such as directly mutating rows and then having some form of Change Data Capture (CDC) to capture the changes for auditability and forensics. During the evaluations, it became more apparent that, rather than maintaining a separate Event Stream of changes, using Event Sourcing Pattern with its Event Log gave us the historical changes baked into the solution without having to deal with the complexities of a CDC and a separate\u00a0log.<\/p>\n<p>CDC<\/p>\n<p>We wanted our solution to also <strong>avoid<\/strong> dealing with distributed transactions, for example, where we created an entry in a database and then fired a message into a message broker due to the inherent unreliability of such operations from an atomic perspective.<\/p>\n<h3>Resulting Solution<\/h3>\n<p>To implement the solution, we had to pick the appropriate data store and eventing system. The following were some of the needs of our Event log and Aggregates:<\/p>\n<p><strong>Event log<\/strong><\/p>\n<p>Event log is totally ordered, immutable (attempts to update or delete events are rejected) and is append-onlyEvent log contains each event exactly once (attempts to append a duplicate event are rejected)Event log append may enforce additional user-defined constraints (e.g., attempt to append a reservation event is rejected if insufficient inventory available)High performance append log predictability from a time perspective on\u00a0inserts.<\/p>\n<p>From an aggregate perspective, we needed to make sure the solution supports <em>SNAPSHOTs<\/em>.<\/p>\n<p>We considered several popular log-like data-stores like Kafka, Cassandra, and EventStore to build the above but had certain reservations at that time around each of\u00a0them:<\/p>\n<p><strong>Kafka<\/strong>\u200a\u2014\u200aDid not have the ability to support unique events (not to be confused with Kafka Producer idempotence). Kakfa also presented a challenge to be able to support consistent query\/read all events for a particular SKU+Location without an explosion of\u00a0Topics.<strong>EventStore<\/strong>\u2014 Seemed to have challenges with scaling of high throughput writes, though reads looked more promising.<strong>Cassandra<\/strong>\u200a\u2014\u200aCassandra had the ability to enforce event uniqueness with LWT (Light Weight Transactions) albeit with <strong>higher<\/strong>\u00a0latency.<\/p>\n<p>Out solution ended up using an in-house data service called<em> Zero Object Service<\/em> (ZOS), which is an elastically scalable microservice designed to handle data storage and retrieval for multi-tenant, meta-data-driven, high-volume, and low-latency use cases. It does so by providing a set of template patterns like Event Sourcing and Reservation Sets that are built on top of key-value datastores that support per-row atomic writes with strong consistency. The actual implementation of the ZOS Event Sourcing template is beyond the scope of this blog post, but, with ZOS, the Inventory service was able to achieve its desired features for event sourcing and meet the desired scale. The solution was deployed on public cloud across multiple global\u00a0regions.<\/p>\n<h3>Learnings from our Implementation<\/h3>\n<p>Our implementation and adoption of the pattern left us with some learnings that we\u2019d like to\u00a0share:<\/p>\n<h4>Consistency and Staleness<\/h4>\n<p>One of the foremost things that stands out is our Product and Engineering teams embraced the concept of eventual consistency and that it is acceptable that inventory numbers have different degrees of staleness at different points of the architecture. Inventory on a web page might show available, but, when you try to buy it, it could be sold out. Product Searches and other indices do not need to reflect 100% consistent inventory results and neither do Product detail or Product listing pages. But, when attempting to Reserve\/Check-out, it is extremely important to be accurate to not promise something that could potentially not be available. For that reason, the <em>writes<\/em> for Inventory reservations (as we refer to them) were strongly consistent operations.<\/p>\n<h4>Event Sourcing\u00a0Pattern<\/h4>\n<p>Event Sourcing as a pattern can be a bit difficult to wrap one\u2019s head around. It often is easily confused with a simple event stream\/topic, where the messages themselves are no longer relevant once the stream is processed. Our team had to go through a learning curve around Event Sourcing, how it differs from <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/architecture\/patterns\/cqrs#:~:text=CQRS%20stands%20for%20Command%20and,operations%20for%20a%20data%20store.&amp;text=The%20flexibility%20created%20by%20migrating,conflicts%20at%20the%20domain%20level.\">CQRS<\/a> and the benefits it brings over straight up manipulation of data and CRUD. Embracing the need to maintain two data points, namely an event log and the entity\/aggregate\/view, instead of interacting with just the entity\/aggregate\/view took some time to digest. The fact that the Event Sourcing pattern promotes the separation of Write\/Read paths, thereby enabling performance optimizations on either end, was a big benefit that we observed in our performance tests. The ability to build aggregates from the stream while coalescing multiple event records were performance optimizations that gave us the benefit of cost saving as well as reducing the number of I\/O operations. Avoiding lock contention by multiple threads by serializing the updates to entities over single threads for mutation seemed to gain us good performance during <strong>High Contention<\/strong> scenarios like flash\u00a0sales.<\/p>\n<p>Another benefit of Event Sourcing is <strong>subscriptions<\/strong> and providing our consumers the ability to subscribe for changes, which meant that they could cache the availability state closer to their application and reach back to the inventory solution for deltas (changes) while always using the inventory solution for performing strongly consistent reservations. This effectively led to improved performance for consumers as their data was closer to them for the READ path, as well as reducing the cost to serve, as the Inventory Solution did not have to be accessed for every GET availability operation.<\/p>\n<h4>Audit and Forensics<\/h4>\n<p>The benefit of ensuring we have a historical log of changes with the Event Sourcing system without the need for a supplemental audit log helped us many times during forensics to replay and re-construct states. The audit is not something that is an accessory to the primary model but is instead what drives the\u00a0model.<\/p>\n<p>Below are a couple of spots where we found the event log to be very helpful for supporting live customers<\/p>\n<p>The event log provides footprints to trace back the path to arrive at the current inventory state for a SKU. By providing the Event log to the consumers as a self service tool, it helped them trouble shoot the actions leading to the current\u00a0state.The release version of the service is stamped on every event which is helpful for finding the affected events in case of bug introduced in the release, and we are able to isolate the affected events and replay those events to get it to the correct\u00a0state.<\/p>\n<h4>Performance<\/h4>\n<p>The performance of the Architecture of the<em> Inventory Availability Service\u2192 Zero Object Service\u2192 Key\/Value Store <\/em>provided the desired results we needed from the solution for throughput and responsiveness of inventory reservations and availability retrieval.<\/p>\n<h3>Conclusion<\/h3>\n<p>Since launching in February 2021, the Inventory solution has garnered the interest and adoption of many brands globally. The solution offers a headless API as part of the overall suite of Commerce API offerings. <em>(Read more about that at the <\/em><a href=\"https:\/\/developer.commercecloud.com\/s\/api-details\/a003k00000Wa43pAAB\/commerce-cloud-developer-centerinventoryavailability\"><em>Commerce Cloud Developer Center<\/em><\/a><em>.)<\/em> It integrates with Salesforce B2C Commerce, which many merchants have loved using over the years, and also with Salesforce Order Management.<\/p>\n<h3>Credits and Acknowledgements<\/h3>\n<p>We would like to thank and acknowledge Robert Libby and Benjamin Busjeager for their contributions toward this solution and also for helping review this blog. We also would like to acknowledge and appreciate all the teamwork and effort from members of Product and Technology at Salesforce who were involved in the making of this offering.<\/p>\n<p>Interested in working on problems like these? Join our <a href=\"https:\/\/careers.mail.salesforce.com\/tpil-blogs\">Talent Network<\/a> or reach out to Sanjay on his <a href=\"https:\/\/sleeplessinslc.blogspot.com\/\">blog<\/a>, <a href=\"https:\/\/twitter.com\/sanjayvacharya\">Twitter<\/a>, or <a href=\"https:\/\/www.linkedin.com\/in\/sanjayvacharya\">LinkedIn<\/a> and Balachandar on his <a href=\"https:\/\/www.linkedin.com\/in\/balachandar-mariappan-90297365\/\">LinkedIn<\/a>.<\/p>\n<p><a href=\"https:\/\/engineering.salesforce.com\/event-sourcing-for-an-inventory-availability-solution-3cc0daf5a742\">Event Sourcing for an Inventory Availability Solution<\/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\/event-sourcing-for-an-inventory-availability-solution-3cc0daf5a742?source=rss----cfe1120185d3---4\">Read More<\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>Co-author\u200a\u2014\u200aBalachandar Mariappan An Introduction to Terminology ATF\u200a\u2014\u200aAvailable to Fulfill inventoryOn-Hand\u200a\u2014\u200aPhysical amount of Inventory availableSKU\u200a\u2014\u200aStock Keeping Unit, which represents a distinct type of item for\u00a0sale.Location\u200a\u2014\u200aRepresentation of a physical location like a store or warehouse where SKU\u2019s are\u00a0presentLocation Group\u200a\u2014\u200aA Logical aggregation of typically one or more Locations.Reservation or Inventory Reservation\u200a\u2014\u200aReserving a quantity of a SKU. For example:&hellip; <a class=\"more-link\" href=\"https:\/\/fde.cat\/index.php\/2021\/12\/16\/event-sourcing-for-an-inventory-availability-solution\/\">Continue reading <span class=\"screen-reader-text\">Event Sourcing for an Inventory Availability Solution<\/span><\/a><\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","footnotes":""},"categories":[7],"tags":[],"class_list":["post-517","post","type-post","status-publish","format-standard","hentry","category-technology","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":299,"url":"https:\/\/fde.cat\/index.php\/2021\/08\/31\/caching-with-the-salesforce-commerce-sdk\/","url_meta":{"origin":517,"position":0},"title":"Caching with the Salesforce Commerce SDK","date":"August 31, 2021","format":false,"excerpt":"Co-written by Brian\u00a0RedmondEvery 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\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":628,"url":"https:\/\/fde.cat\/index.php\/2022\/09\/06\/viewing-the-world-as-a-computer-global-capacity-management\/","url_meta":{"origin":517,"position":1},"title":"Viewing the world as a computer: Global capacity management","date":"September 6, 2022","format":false,"excerpt":"Meta currently operates 14 data centers around the world. This rapidly expanding global data center footprint poses new challenges for service owners and for our infrastructure management systems. Systems like Twine, which we use to scale cluster management, and RAS, which handles perpetual region-wide resource allocation, have provided the abstractions\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":558,"url":"https:\/\/fde.cat\/index.php\/2022\/03\/29\/investigate-issues-with-ease-by-adding-a-correlation-id-to-your-api\/","url_meta":{"origin":517,"position":2},"title":"Investigate Issues with Ease by Adding a Correlation ID to your API","date":"March 29, 2022","format":false,"excerpt":"With APIs becoming more complex and distributed, developers sometimes struggle to find the relevant logs when they need to investigate a specific issue. In the new Salesforce Commerce APIs (SCAPI), we created such an architecture of distributed systems and recognized this problem early. Our approach to mitigate it was the\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":630,"url":"https:\/\/fde.cat\/index.php\/2022\/09\/07\/network-entitlement-a-contract-based-network-sharing-solution\/","url_meta":{"origin":517,"position":3},"title":"Network Entitlement: A contract-based network sharing solution","date":"September 7, 2022","format":false,"excerpt":"Meta\u2019s overall network usage and traffic volume has increased as we\u2019ve continued to add new services. Due to the scarcity of fiber resources, we\u2019re developing an explicit resource reservation framework to effectively plan, manage, and operate the shared consumption of network bandwidth, which will help us keep up with demand\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":593,"url":"https:\/\/fde.cat\/index.php\/2022\/03\/29\/investigate-issues-with-ease-by-adding-a-correlation-id-to-your-api-2\/","url_meta":{"origin":517,"position":4},"title":"Investigate Issues with Ease by Adding a Correlation ID to your API","date":"March 29, 2022","format":false,"excerpt":"With APIs becoming more complex and distributed, developers sometimes struggle to find the relevant logs when they need to investigate a specific issue. In the new\u00a0Salesforce Commerce APIs\u00a0(SCAPI), we created such an architecture of distributed systems and recognized this problem early. Our approach to mitigate it was the introduction of\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":693,"url":"https:\/\/fde.cat\/index.php\/2023\/03\/27\/sre-weekly-issue-365\/","url_meta":{"origin":517,"position":5},"title":"SRE Weekly Issue #365","date":"March 27, 2023","format":false,"excerpt":"View on sreweekly.com A message from our sponsor, Rootly: Manage incidents directly from Slack with Rootly\u00a0\ud83d\ude92. Rootly automates manual tasks like creating an incident channel, Jira ticket and Zoom rooms, inviting responders, creating statuspage updates, postmortem timelines and more. Want to see why companies like Canva and Grammarly love us?:\u2026","rel":"","context":"In &quot;SRE&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/517","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"}],"replies":[{"embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/comments?post=517"}],"version-history":[{"count":0,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/posts\/517\/revisions"}],"wp:attachment":[{"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/media?parent=517"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/categories?post=517"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fde.cat\/index.php\/wp-json\/wp\/v2\/tags?post=517"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}