Journey: Top-up After a Lock Collision or a Short Pick
Nothing allocates when new stock arrives. Two background paths do reserve again: the retry after a lock collision, and the follow-up of a short pick. Both call the command RetryOrderAllocation, which runs the same allocation pass as the Allocate button.
1. Lock collision: the retry job
What this shows. Two commands wanted the same stock row at the same moment. The second one did not wait. Its quantity is "deferred", and a background job tries again.
Reading notes.
- A row is "contended" when its balance row, or its LPN container, is locked by another transaction. The reserve step uses "skip locked" and never waits.
- The event is published by
AllocateandRelease, not by the retry itself. - The subscriber has 3 delivery attempts. After a failed attempt, the next one is due after 15 seconds, then 30 seconds. After the third failed attempt the delivery is marked
failedand nobody tries again. - The retry has no order version in its request. It reads the lines as they are now. A line that has no open quantity is skipped.
- The stock that the other transaction took is simply not there at the retry. The retry reserves what is left. If it is less than the need, the line stays short.
- The same subscriber also listens to
process.order.line_allocation_short. The only code that publishes this event isshort_pick, and the code comment says it has no production caller yet. The real short-pick path is in section 2.
Where it comes from
| Step in the diagram | Code |
|---|---|
Container and balance locks use "skip locked"; both give Contended | crates/libs/wms-outbound/src/allocation/reserve.rs:128-160 |
| Deferred quantity is not counted as short | crates/libs/wms-outbound/src/service/order/allocate.rs:554-557, crates/libs/wms-outbound/src/service/order/allocate.rs:667-669 |
Event is published only in the Publish mode | crates/libs/wms-outbound/src/service/order/allocate.rs:638-655 |
| Retry command, fails while quantity stays deferred | crates/libs/wms-outbound/src/service/order/allocate.rs:233-265 |
| Subscriber, 3 attempts, delay 15 s | crates/libs/wms-lib-sql/sql/wms-enterprise/migrations/007_internal_outbound_execution_delivery.sql:25-37 |
| Delivery worker calls the retry for the two order events | crates/services/app-server/src/services/integration_delivery.rs:535-570 |
Delay doubles with each attempt, the third failure ends in failed | crates/services/app-server/src/services/integration_delivery.rs:386-411, crates/services/app-server/src/services/integration_delivery.rs:414-417 |
short_pick is the only publisher of line_allocation_short | crates/libs/wms-outbound/src/service/order/cancel_allocation.rs:337-352, crates/libs/wms-outbound/src/picking/confirmation/episode.rs:55-56 |
2. Short pick: release, quarantine, reserve again
What this shows. The picker could not find the full quantity. The pick reason decides which of the follow-up actions run.
Reading notes.
- The action
ReleaseCurrentRemainderis required in every published reason. It gives the missing quantity back to the line. The line is then short again by that quantity. - Allocation skips a location whose
loc_flagis notNone. So afterBlockSourceLocation, the reserve again draws from other locations. ReleaseRelatedReservationshas a scope:CurrentPick,SameSourceStock,CurrentSkuAtLocationorEntireLocation. Only picks whose task isCreatedwith nothing done, or that have no task, are freed. A pick with a task in work is kept.ReallocateReleasedDemandhas an order scope:CurrentOrder,OrdersWithReleasedReservationsorCurrentAndAffectedOrders(also when the scope is empty). An order that is still short after it does not fail the follow-up: the short is its outcome.RelocateResidualToMissingis accepted only together withBlockSourceLocationandReleaseRelatedReservations, and only for the scopeEntireLocation.- The actions
HoldInventory,HoldContainerandRequestSupervisorRevieware in the list of action types, but no code runs them. No code reads the fieldsupervisor_review.
Where it comes from
| Step in the diagram | Code |
|---|---|
pick_short is a pick command | crates/services/app-server/src/web/rpcs/pick_commands_rpc.rs:263 |
Published reason, ReleaseCurrentRemainder is required, other compatibility rules | crates/libs/wms-outbound/src/domain/pick_short_reason/aggregate.rs:44-160 |
Found quantity moves, line qty_allocated goes down by the released quantity | crates/libs/wms-outbound/src/picking/commands/short.rs:365-384 |
BlockSourceLocation sets the location flag | crates/libs/wms-outbound/src/picking/commands/short.rs:391-401, crates/libs/wms-lib-core/src/model/location.rs:1239-1252 |
| Event published | crates/libs/wms-outbound/src/picking/commands/short.rs:470-475 |
| Subscriber filter includes this event | crates/libs/wms-lib-sql/sql/wms-enterprise/migrations/008_pick_discrepancy_remediation_event.sql:27 |
| Remediation: release, quarantine, count, reallocate | crates/libs/wms-outbound/src/picking/remediation.rs:152-244, crates/libs/wms-outbound/src/picking/remediation.rs:449-495 |
| Scope of the release; only unstarted picks are freed | crates/libs/wms-outbound/src/picking/remediation.rs:246-445 |
| Order scope of the reallocation | crates/libs/wms-outbound/src/picking/remediation.rs:559-578 |
| Allocation skips a location with a flag | crates/libs/wms-lib-core/src/model/inventory/inventory_balance_atp.rs:457 |
| Action types without a runner | crates/libs/wms-lib-core/src/model/outbound/pick_short_reason.rs:22-32 |
Not confirmed by code
- Seeded pick short reasons and their action sets are described on page 23 of the policy reference. This page does not repeat them. To verify: read the seed of
pick_short_reason_actionin thewms-appmigrations.