Performance improvements for large shopping cart

The following shopping cart commands have been optimized for performance: OrderItemAdd, OrderItemUpdate, OrderItemDisplay. These changes optimize the commands so that performance gains can be achieved, especially in cases where shopping carts typically contain hundreds of order items.

Previously, every time these commands were called, they performed a price calculation, checked inventory, and updated the inventory allocation. When the shopping cart contains many order items, this negatively affects performance. Now these commands have two new parameters, doPrice and doInventory, which act as switches to turn on or off the price and inventory subtasks.

Although these subtasks are important, they can be called less frequently to realize performance gains without negatively affecting order or inventory integrity. For instance, you could divide the calls to the subtasks among the commands in the following manner:

Order Command Subtasks performed
OrderItemAdd Price calculation
OrderItemUpdate Update order item information
OrderPrepare Price calculation, inventory checking and allocation, shipping and tax calculations

The following commands only support enabling or disabling the price calculation.

Limitations of this solution:

  1. Customers do not see an instantaneous reflection of price changes, product entitlement changes, or inventory level changes when the price and inventory subtasks are turned off in some URLs.

  2. If the customer creates a shopping cart, does not submit it but returns later, then the price is expired. For this case, the price always refreshes. However, since this is not common, there is little performance impact.

  3. When QuoteGoodFor is small, removing the price calculation subtasks does not improve performance. The price always refreshes when the quoted price expires.

The performance of the OrderItemAdd, OrderItemUpdate, QuoteItemAdd, and QuoteItemUpdate commands is further improved by removing some inefficient loops when they call the other task commands.


Invalidating expired prices

If a customer adds an item to the shopping cart, the price is calculated. However, if the order is not submitted for a long time, the offer price might have already changed, making the price calculated for the shopping cart invalid. In this case, the price must be recalculated when the customer accesses the shopping cart again. To support this, a new bit flag in the ORDERITEMS.PREPAREFLAGS called PREPAREFLAGS_PRICE_REFRESHED is introduced. The OrderItemBaseCmdImpl and OrderCalculate commands call a new task command, CheckAndResetOrderItemPriceFlagCmd, which checks whether the price is expired. If the price is expired, the price is recalculated.

The default implementation of this new task is defined as follows:

  1. If the STORE.PRICEREFFLAG is "8", the task groups all of the order items with the same catentryId.

  2. If one of the order items for a given catentryId is expired, or its binary price flag (PREPAREFLAGS_PRICE_REFRESHED) is 0, the task resets the price flag for all of the order items with the same catentryId.

  3. If the STORE.PRICEREFFLAG is not "8", the task checks whether each order item has been expired. If so, it resets the binary price flag for the order item.

This is expressed as the following formula:

If STORE.QUOTEGOODFOR (unit: second) <= (CurrentTime - ORDERITEMS.LASTCREATE)/1000 (unit of time: millisecond), the orderitem is indicated to be expired.

If price recalculation is turned off when updating prices for order items, the OrderItemAdd, OrderItemUpdate, OrderItemDisplay and OrderCalculate commands check the PREPAREFLAGS_PRICE_REFRESHED bit flag. If the flag is 1, the order item price is not refreshed. If the flag is 0, the price is refreshed and the flag is set to 1.

We can customize the CheckAndResetOrderItemPriceFlag task command to implement our own logic to reset the PREPAREFLAGS_PRICE_REFRESHED flag.


Shipping and tax calculation optimization

Taxes and shipping costs are evaluated differently. This leads to a performance increase of the OrderPrepare command, which calls the tax and shipping calculations.

In most tax and shipping implementations, there are lots of rules configured for a code. Typically, each shipping calculation rule is specific to a single combination of a fulfillment center, a ship mode, and a shipping jurisdiction. Likewise, tax calculation rules are specific to a single combination of a fulfillment center and a TaxJurisdictionGroup. For each of these rules, some command is called to evaluate whether the rule applies to an item in the shopping cart.

The command implementations, FastShippingRuleCombineCmdImpl and FastTaxRuleCombineCmdImpl, are intended to be used by stores that have a large number of shipping or tax rules.

The cache is also enhanced to improve the rule finding logic. Each time the system finds the ship rules for a ship code, for example, the data can be obtained from the cache according to ship code, ship mode, fulfillment center and ship address (jurisdiction). For tax calculation, the rules can be obtained from the cache according to tax code, fulfillment center, ship address (jurisdiction), and tax category.

This solution improves performance in the following scenarios:

  1. There are many rules defined for shipping or tax, and assuming that each order item would only qualify for a small subset of those rules.

  2. For large shop cart, if most order items are ship to same address, using same ship mode (only for ship), and inventory are allocated in same fulfillment center.


Optimization of main shopping flow commands

Large shopping carts have the following characteristics:

These assumptions were the basis of the optimizations for the OrderItemAdd and OrderItemUpdate commands.

The default code contains a loop which iterates for each of the items separately, every time order items are added or updated. Currently, when multiple items are added to the shopping cart each separate order item is processed using this logic:

  1. If passed a part number ID, resolve the part number to a catalog entry ID.

  2. Call the ResolveSku task command to resolve the SKU.

  3. Check the product entitlement.

  4. Check whether the product is buyable.

  5. Create the order item in the database.

  6. Resolve the part number from catalog entry ID and update the part number in the orderitem.

  7. Update other fields of the order item.

The command logic has been improved:

  1. Resolve part numbers to catalog entry IDs for all items, skipping any duplicate part numbers which have already been resolved.

  2. Resolve SKUs for all items, skipping any duplicate SKUs that have already been resolved.

  3. Check the product entitlement for all items, skipping any duplicate items for which entitlement has already been determined.

  4. Determine whether the product is buyable for all items, skipping any duplicate items.

  5. Create all order items in the database.

  6. Unless the part number is provided as an input parameter, resolve the part number from the catalog entry ID and update the part number in the order item. This is often provided by external configuration tools, so the step can be skipped in those cases.

  7. Update other fields for all order items.

Furthermore, these changes improve the performance of the large shopping cart:

  1. If the address parameters are not passed in, they are resolved once and applied to all of the order items.

  2. If the shipping mode parameter is not passed in, it is resolved once and applied to all of the order items.

  3. For the OrderItemUpdate command, if all of the quantity parameters passed in match those in the database - unless there is a price or inventory check - the price and inventory do not change. This happens when certain updates are made, such as changing the shipping address or the ship mode.