Common pitfalls in OMS design

Edit on GitHub

Implementing OMS processes can be challenging when they are complex, or requirements are not trivial. This can lead to hidden issues which are hard to debug. An example of such issues is race conditions.

In some cases, OMS works incorrectly. In most cases, a correct flow can be run successfully, but the first run of a wrong path might reveal a problem. In other cases, there might be known limitations that can lead to incorrect transitions. There can also be cases that are valid but should be rewritten into a better readable process. If you discover more edge cases, please send those to our support team.

This document describes the most common issues with OMS design and how you can fix them.

More than one onEnter event from one state

Issue: If there is more than one onEnter transition event from state A, only one is executed.

img

Reason: This behavior is not supported because there must always be only one state after an event execution.

Solution: If you have different commands, you can chain those:

img)

If you have the same commands, then one of the commands could get a condition:

img

Defining states with names

Issue: States with the same names are declared in several processes.

Reason: When a process or a sub-process is read, transitions are assigned to the from-state in the current process or sub-process. In case of several declarations, these will be different from-states.

Solution: Define states with unique names only. We recommend defining the state in the process which introduces it. You can define transition in other states.

Tip: Declare the state in the process where it has outgoing transitions.

Duplicate events

Issue: Events with the same names are declared in multiple processes.

In the OMS drawing, you will see the last read event definition, but during the execution, any might be defined.

Solutions:

  • Rename one of the events.
  • Keep only one event. We recommend having reusable events in the main process rather than using those from some sub-processes.

States with only outgoing transitions

Issue: There are many states with only outgoing transitions.

img

Reason: Function OmsConfig:getInitialStatus has only one return value, so it’s impossible to start from another “initial” state.

Solution: In most cases, this is a mistake, and the transition between some states is missing. Adding transition makes the process correct. For example, adding transition payment doneshipped with the ship event brings the whole process to a correct state.

img

Info

You can change order items’ states through a manual call, and this way, use the states without inbound transitions. On the one hand, this helps not to overwhelm the process with 10+ transitions to a cancellation process. On the other hand, though, this makes the process definition incomplete and, thus, this approach is not recommended.

More than one main process

Issue: Having more than one main process can lead to incorrect process rendering and execution:

img

When placing an order, this issue entails an error like this one:

Exception - Unknown state: new in "/data/vendor/spryker/oms/src/Spryker/Zed/Oms/
Business/Process/Process.php::198" {"exception":"[object] (Exception(code: 0):
Unknown state: new at /data/vendor/spryker/oms/src/Spryker/Zed/Oms/Business/
Process/Process.php:198)

Solution: Removing duplicate flag main fixes the process rendering and processing:

img

More than one transition with the same events and without condition

Issue: It’s impossible to guess which transition is expected, so the first one read is executed.

img

Solution 1: Add a condition to one of the transitions:

img

Solution 2: Change event on one transition:

img

Conditional, timeout, and manual transitions mixture from the same state

Issue: Creating a condition and a timeout, or a manual event and a condition, or a manual event and a timeout from the same state leads to errors.

Reason: Condition check and timeout execution happen in different console commands, and the order of the execution is not defined by the OMS but by a scheduler.

Execution of the manual event could also happen during the console command execution, and the resulting state of the order items, in this case, is unpredictable.

Solution:

  1. Rewrite the process and check the condition after the timeout.
  2. Use TimeoutProcessor. It significantly decreases the probability of the same-time execution.

Use asynchronous break without command and condition

Issue: Adding timeout pauses the execution process and unblocks external systems.

img

Reasons:

  • Timeout triggers the DB update, namely—the creation of a timeout entry for each order item in the spy_oms_event_timeout table.
  • Timeout check requires a DB query to find the affected entries.

Solution: We recommend using an event (pause) without command and without condition. The event will not interact with the DB and will also be executed with the next run of the oms:check-condition command.

img

Not used events or states

Issue: Process contains a declaration of a state which is never used:

img

The unused state could have a missing transition.

Solution: Delete a state, or add a missing transition:

img

Incorrect event definition

Issue: Mixing onEnter="true" and manual="true" in the same event.

img

Event does not appear as manual unless the previous command execution fails with an exception.

Solution: Create separate transitions: one with the onEnter command, the other with the manual command.

img

Tip

Keeping both onEnter and manual commands can only be used for backup for the failed automated execution of the onEnter command with a manual event.

Calling OMS processing functions within a custom DB transaction

Issue: You want to enclose complex processing, including OMS processing functions, inside a transaction.

OMS processing functions, like triggerEvent*, checkConditions and checkTimeouts, use lock on the order item level to prevent processing of the same item more than once at the same time. The lock information is stored as an entry in the spy_oms_state_machine_lock table. Running this code inside a DB transaction make the lock entries inaccessible. This may lead to an undetermined resulting state of the item or even to a DB deadlock in rare cases.

Solution: Avoid OMS processing function calls inside DB transactions.