Multi-pass Calculation

Use Case

When a price of a product “B” depends on (i.e. requires it for its own calculation) a price of another product “A” within the same price list (or LPG, Quote), then you need to calculate first the price of product “A” and only then the price of “B”.

Process Flow

From a technical perspective there is no guarantee that line items are calculated in any specific order, so you can have two situations:

  • The product “A” was calculated first (and so its price is already stored in a DB) and the “B” product is calculated afterwards, so the system can already retrieve the price of “A”, because it is available, and it can calculate the price of “B” (which depends on the price of “A”).

  • Or the “B” product comes to the calculation first, when “A” does not have the price yet. In this case the calculation of the “B” product must wait – i.e. the logic must schedule the line item for re-run (“mark as dirty”). Once the system finishes processing of all line items, it will verify if some lines were marked as dirty and if so, it starts the next pass (next round) calculation for all of the lines marked as dirty.

Notes:

  • You can do as many passes as needed.

  • The product without any dependency is sometimes referred to as “master” or ”base” product.

  • In case of distributed/parallel calculations:

    • It can happen that “A” and “B” will be calculated on different nodes. Nevertheless, the same rules apply – if the logic starts calculation of the dependent product “B” and the price of the master product “A” is not yet available, the logic will mark the line as dirty, and once the first pass finishes on all nodes, then the system will start the second pass for all “dirty lines” (possibly again distributed across multiple nodes).

  • Failure in a dirty pass calculation will leave the item dirty. In case of permanent failure, the dirty pass is recalculated until the maximum number of passes is reached. Sometimes, it is preferable not to retry but to mark the item as clean in case of a calculation error. This special behavior is optional and can be configured on a per-instance basis (on a cluster level), defaulting to the current behavior. Set the calculationTasks.alwaysCleanItems (<alwaysCleanItems>false</alwaysCleanItems>) to true to mark all items as clean upon a calculation failure – then the following dirty runs will not start.

  • Keep in mind that keys within the map being cached are converted to String in the multi-pass calculation (for example, when using the libs.SharedLib.CacheUtils.getOrSet to store the data in the cache).

Process Sample

Technical Implementation

Conceptual Code Sample

def price if (productDoesNotDependOnAnother) { /* price of the product does not depend on another product */ /* so we can calculate the price right away */ price = calculatePrice() } else { /* price of the product is derived from price of other/master product */ if (api.getIterationNumber() == 0) { /* if this is first pass, then we need to wait for the related product to be calculated */ api.markItemDirty() } else { /* this is already second pass, so we expect the other/master product to be calculated already */ masterProductPrice = api.currentContext(skuOfTheMasterProduct)?.Price price = calculatePriceFrom(masterProductPrice) } } return price

The important functions here are:

References

Found an issue in documentation? Write to us.