Skip to main content

Journey: Wave Build and Release

A wave groups orders that are released together. There is no web screen or RF screen for waves. Everything runs through the API methods wave_* and wave_policy_* and through two background loops of app-server. A wave does not allocate by itself. For every member it runs the same ReleaseShipmentOrder command as the Release button (see page 10).

1. How a policy builds waves​

What this shows. One build, started by hand (wave_policy_build) or by the scheduler for a due slot.

Reading notes.

  • The filters, the grouping and the size limits come from the wave policy. They are drawn on page 25.
  • The build is one transaction. A scheduled slot that fails is rolled back and then recorded as a run with outcome = Failed. The schedule moves to the next slot.
  • The scheduler wakes every 5 seconds and takes at most 10 due slots per warehouse. A policy row is taken with "skip locked", so two servers never build the same slot.
  • Every member gets a claim row. An order with a claim cannot enter another wave.

Where it comes from​

Step in the diagramCode
Build entry, lock the policy, version and active checkscrates/libs/wms-outbound/src/waves/policy.rs:225-236
Candidate read, planning, second read under order locks, run and wavescrates/libs/wms-outbound/src/waves/policy.rs:239-343
Candidate SQL (filters, order by priority, ship date, id)crates/libs/wms-lib-core/src/model/outbound/wave_policy.rs:311-344
Grouping and batchescrates/libs/wms-outbound/src/waves/policy.rs:430-510
Member and claim rowscrates/libs/wms-lib-core/src/model/outbound/wave.rs:452-488
auto_release prepares the release in the same transactioncrates/libs/wms-outbound/src/waves/policy.rs:288-294
Scheduler: lock one due policy, build, move next_run_atcrates/libs/wms-outbound/src/waves/scheduler.rs:36-108, crates/libs/wms-lib-core/src/model/outbound/wave_policy.rs:261-305
A failed slot is recorded as a failed runcrates/libs/wms-outbound/src/waves/scheduler.rs:111-136
Loop timing: 5 seconds, 10 slotscrates/services/app-server/src/services/cleanup_tasks.rs:160-163, crates/services/app-server/src/services/cleanup_tasks.rs:289-325
API methods and rolescrates/services/app-server/src/web/rpcs/wave_rpc.rs:46-270

2. How a wave releases its orders​

What this shows. wave_release only queues the work. A worker releases the orders one by one.

Reading notes.

  • The worker takes members in the order of the member id. For a wave from a policy, that is the order of the plan: lowest order priority number first, then earliest requested ship date, then lowest order id. So the first order of a wave reserves its stock first. wave_order.priority and wave_order.sequence do not change this.
  • Every member is released in its own transaction. No transaction covers a whole wave.
  • The member outcome is Released, Skipped (the order was already released), Blocked (business refusal, not found, bad transition, version conflict) or Failed (anything else).
  • A wave that shows Released is only "settled". Members can still be Blocked or Failed. Read the member counts.
  • A shortage of stock does not block a member. A bad allocation policy does not block it either: the order still launches, and the allocation error stays in the order release result, not in the wave.

Where it comes from​

Step in the diagramCode
Freeze: lock orders, wave must be Draft, not empty, all members Draftcrates/libs/wms-outbound/src/waves/commands.rs:254-303, crates/libs/wms-outbound/src/domain/wave/aggregate.rs:140-157
Claim: lowest wave_order.id, lease of 300 secondscrates/libs/wms-lib-core/src/model/outbound/wave.rs:608-641, crates/libs/wms-outbound/src/waves/worker.rs:14
The worker runs ReleaseShipmentOrdercrates/libs/wms-outbound/src/waves/worker.rs:84-158
Outcome classescrates/libs/wms-outbound/src/waves/worker.rs:160-188
Record outcome and settle the wavecrates/libs/wms-outbound/src/waves/worker.rs:190-263, crates/libs/wms-lib-core/src/model/outbound/wave.rs:337-373
Loop timing: 2 seconds, 100 members per warehousecrates/services/app-server/src/services/cleanup_tasks.rs:156-159, crates/services/app-server/src/services/cleanup_tasks.rs:251-285
Retry: wave must be Released, a member must be Blocked or Failedcrates/libs/wms-outbound/src/waves/commands.rs:305-362, crates/libs/wms-outbound/src/domain/wave/aggregate.rs:159-179
Retry puts members to Pending and the wave to Plannedcrates/libs/wms-lib-core/src/model/outbound/wave.rs:309-331, crates/libs/wms-lib-core/src/model/outbound/wave.rs:695-725

3. Statuses of a wave and of a member​

What this shows. Every change of status that code performs. InProgress and Completed exist in the enum, but no code sets them.

Status of the wave (wave.status):

Status of a member (wave_order.status):

Reading notes.

  • wave_cancel is refused when a member is Processing, Released or Skipped. Queued members become Blocked with last_error_code = WAVE_CANCELLED, and the claims of the wave are deleted.
  • A Processing member that nobody finishes within 300 seconds is claimed again by the worker.

Where it comes from​

ChangeCode
Wave Cancelled, Planned (freeze), Planned (reopen), Released (settle)crates/libs/wms-lib-core/src/model/outbound/wave.rs:274, crates/libs/wms-lib-core/src/model/outbound/wave.rs:298, crates/libs/wms-lib-core/src/model/outbound/wave.rs:324, crates/libs/wms-lib-core/src/model/outbound/wave.rs:350
Cancel rule: only Draft or Planned, no started membercrates/libs/wms-outbound/src/domain/wave/aggregate.rs:123-138
Member Blocked on cancel, Pending on freeze, Processing on claim, outcome, Pending on retrycrates/libs/wms-lib-core/src/model/outbound/wave.rs:560, crates/libs/wms-lib-core/src/model/outbound/wave.rs:585, crates/libs/wms-lib-core/src/model/outbound/wave.rs:631, crates/libs/wms-lib-core/src/model/outbound/wave.rs:676, crates/libs/wms-lib-core/src/model/outbound/wave.rs:710
Statuses InProgress, Completed exist but are never assignedcrates/libs/wms-lib-core/src/model/outbound/wave.rs:25-26

Not shown here​

  • Nothing in the code moves a wave to Completed, so there is no "wave finished" step to draw. The field wave.completed_at is never written.
  • Sorting, loading and shipping after the pick are not part of a wave in the code.