Order Fulfillment Algorithm: Warehouse to Last-Mile

Answer-first: E-commerce order fulfillment engines optimize cross-regional delivery through a 4-stage algorithmic pipeline: real-time Available-to-Promise (ATP) soft reservations in Redis, multi-warehouse constraint optimization minimizing distance and split-shipment penalties in Go, warehouse wave picking route heuristics, and last-mile Capacitated Vehicle Routing (CVRP) with Time Windows via Google OR-Tools.

graph TD
    Order["Customer Confirms Multi-Item Cart"] --> ATP["Stage 1: Redis ATP Check & Soft Reservation (< 2ms)"]
    ATP --> Allocation["Stage 2: Go Warehouse Allocation Solver (Min Cost + Split Penalty)"]
    Allocation -->|"Split Decision"| Plan["Fulfillment Plan (e.g. WH-East: 2 items, WH-Central: 1 item)"]
    Plan --> Wave["Stage 3: Warehouse Wave & Batch Picking (S-Shape Routing & 3D Bin Packing)"]
    Wave --> Carrier["Sortation Center & Carrier Dispatch"]
    Carrier --> VRP["Stage 4: Last-Mile CVRP Solver (OR-Tools Time Windows & Capacity)"]
    VRP --> Doorstep["Customer Doorstep Delivery"]

    style Order fill:#f0f9ff,stroke:#0284c7,stroke-width:2px
    style Allocation fill:#fef3c7,stroke:#d97706,stroke-width:2px
    style Wave fill:#ecfdf5,stroke:#059669,stroke-width:2px
    style VRP fill:#fae8ff,stroke:#a855f7,stroke-width:2px

Executive Summary & Fulfillment Fundamentals

When an order is confirmed, the fulfillment system executes a multi-step decision pipeline:

  1. Available-to-Promise (ATP) Check: Filter candidate warehouses by real-time uncommitted stock.
  2. Cost & Proximity Scoring: Evaluate shipping distance, labor rate, carrier capacity, and SLA risk.
  3. Split vs. Consolidate Trade-Off: Determine whether to ship from multiple warehouses or wait for inventory consolidation.
  4. CONDOR & Anticipatory Dispatch: Pre-position stock globally based on probabilistic ML demand forecasts.
  5. Last-Mile VRP Solving: Optimize driver routes using vehicle routing solvers (OR-Tools / GraphHopper).

Step 1 — Real-Time Inventory & Available-to-Promise (ATP)

Physical stock on hand does not equal sellable stock. Fulfillment systems distinguish between raw inventory counts and uncommitted inventory:

  • Physical On-Hand: Total inventory units located inside the warehouse bin.
  • Available-to-Promise (ATP): Physical stock minus hard-committed and soft-reserved units.

Soft Reservations with TTL

When a customer enters checkout, a soft reservation decrements ATP in an in-memory Redis cluster. The reservation carries a TTL (typically 5–15 minutes). If payment fails or the session times out, the reservation automatically expires and ATP is restored.

Production Redis Lua Engine for Atomic ATP Soft Reservations

In high-concurrency e-commerce environments during sales events, naive non-atomic stock checks (e.g., executing a read GET stock followed by DECRBY) cause catastrophic race conditions and overselling. To prevent database row lock contention while maintaining strict transactional guarantees, modern order fulfillment engines delegate soft reservations to single-threaded, atomic Redis Lua scripts.

Atomic Reservation Protocol:

  1. Stock Check & Decrement: Atomically verify if stock_key (e.g., atp:wh_sg01:sku_9942) holds sufficient uncommitted units.
  2. TTL Key Binding: If stock is available, decrement stock_key by requested_qty and create a reservation key (reservation:ord_1042:sku_9942) bound with a strict Time-To-Live (TTL) expiration window (e.g., 900 seconds / 15 minutes).
  3. Rollback & Expire: If stock is insufficient, exit immediately with zero mutations. If checkout completes, the worker converts the soft reservation into a hard database commitment. If the session expires, Redis keyspace notifications (__keyevent@0__:expired) automatically trigger an INCRBY rollback back to the warehouse stock pool.

Thread-safe Go implementations execute atomic soft reservations using github.com/redis/go-redis/v9:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/redis/go-redis/v9"
)

// AtomicLuaReserve evaluates available stock, decrements counter, and sets TTL reservation atomically.
const AtomicLuaReserve = `
local stock_key = KEYS[1]
local res_key   = KEYS[2]
local req_qty   = tonumber(ARGV[1])
local ttl_sec   = tonumber(ARGV[2])

local current_stock = tonumber(redis.call("GET", stock_key) or "0")
if current_stock >= req_qty then
    redis.call("DECRBY", stock_key, req_qty)
    redis.call("SETEX", res_key, ttl_sec, req_qty)
    return 1
else
    return 0
end
`

type InventoryEngine struct {
	client *redis.Client
	script *redis.Script
}

// NewInventoryEngine initializes the Redis client and pre-compiles the Lua script SHA.
func NewInventoryEngine(client *redis.Client) *InventoryEngine {
	return &InventoryEngine{
		client: client,
		script: redis.NewScript(AtomicLuaReserve),
	}
}

// ReserveATP executes the atomic soft reservation for an order SKU.
func (e *InventoryEngine) ReserveATP(ctx context.Context, warehouseID, sku, orderID string, qty int, ttl time.Duration) (bool, error) {
	stockKey := fmt.Sprintf("atp:{%s:%s}:stock", warehouseID, sku)
	resKey := fmt.Sprintf("atp:{%s:%s}:res:%s", warehouseID, sku, orderID)

	keys := []string{stockKey, resKey}
	args := []interface{}{qty, int(ttl.Seconds())}

	res, err := e.script.Run(ctx, e.client, keys, args...).Int64()
	if err != nil {
		return false, fmt.Errorf("redis ATP reservation script failed: %w", err)
	}

	return res == 1, nil
}

Step 2 — Multi-Warehouse Selection Cost Function & Go Allocation Solver

When an order contains multiple SKUs distributed across national fulfillment centers, selecting which warehouse ships which item is an NP-hard combinatorial optimization challenge (a variant of the Multi-Knapsack Problem with Distance Penalties).

The allocation engine solves the following multi-criteria objective function:

$$\min \sum_{w \in W} \sum_{i \in O} \left( c_{\text{carrier}}(w, \text{dest}) \cdot x_{w, i} + c_{\text{labor}}(w) \cdot x_{w, i} \right) + P_{\text{split}} \cdot (\text{packages} - 1) + P_{\text{SLA}} \cdot \text{risk}(w)$$

Where:

  • $c_{\text{carrier}}(w, \text{dest})$: Freight shipping cost based on distance and volumetric weight.
  • $c_{\text{labor}}(w)$: Pick, pack, and cartonization labor cost at warehouse $w$.
  • $P_{\text{split}}$: Financial and environmental penalty for splitting an order into multiple separate shipments (typically $4.50–$7.00 per extra parcel).
  • $P_{\text{SLA}}$: Penalty if fulfillment transit time risks violating guaranteed delivery promises.
  • $x_{w, i} \in {0, 1}$: Decision variable indicating whether SKU $i$ is fulfilled by warehouse $w$.

Production Go Multi-Warehouse Allocation Solver

The Go implementation below evaluates available stock across candidate warehouses, calculates Great-Circle (Haversine) transit distances, and computes the optimal allocation plan that minimizes total costs and parcel splits:

// File: internal/fulfillment/allocator/solver.go
package allocator

import (
	"context"
	"fmt"
	"math"
)

// GeoLocation represents latitude and longitude coordinates.
type GeoLocation struct {
	Lat float64
	Lon float64
}

// Warehouse models a fulfillment center facility.
type Warehouse struct {
	ID        string
	Location  GeoLocation
	LaborCost float64
	Inventory map[string]int // SKU -> Available Quantity
}

// CartItem represents an item requested by the customer.
type CartItem struct {
	SKU      string
	Quantity int
}

// ShipmentPackage represents a group of items shipped from one warehouse.
type ShipmentPackage struct {
	WarehouseID string
	Items       []CartItem
	FreightCost float64
}

// AllocationPlan details the complete fulfillment strategy for an order.
type AllocationPlan struct {
	Packages    []ShipmentPackage
	TotalCost   float64
	SplitCount  int
}

// Solver executes multi-warehouse constraint optimization.
type Solver struct {
	warehouses    []Warehouse
	splitPenalty  float64
	costPerKm     float64
}

func NewSolver(warehouses []Warehouse, splitPenalty, costPerKm float64) *Solver {
	return &Solver{
		warehouses:   warehouses,
		splitPenalty: splitPenalty,
		costPerKm:    costPerKm,
	}
}

// Haversine computes great-circle distance between two geographic coordinates in kilometers.
func haversine(p1, p2 GeoLocation) float64 {
	const earthRadiusKm = 6371.0
	dLat := (p2.Lat - p1.Lat) * (math.Pi / 180.0)
	dLon := (p2.Lon - p1.Lon) * (math.Pi / 180.0)

	lat1 := p1.Lat * (math.Pi / 180.0)
	lat2 := p2.Lat * (math.Pi / 180.0)

	a := math.Sin(dLat/2)*math.Sin(dLat/2) +
		math.Sin(dLon/2)*math.Sin(dLon/2)*math.Cos(lat1)*math.Cos(lat2)
	c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))

	return earthRadiusKm * c
}

// Solve determines the optimal warehouse assignment minimizing freight, labor, and split penalties.
func (s *Solver) Solve(ctx context.Context, customerLoc GeoLocation, cart []CartItem) (*AllocationPlan, error) {
	// 1. Check if a single warehouse can fulfill 100% of the cart (Zero-Split Preferred)
	var bestSingleWH *Warehouse
	var minSingleCost = math.MaxFloat64

	for i := range s.warehouses {
		wh := &s.warehouses[i]
		canFulfillAll := true
		for _, item := range cart {
			if wh.Inventory[item.SKU] < item.Quantity {
				canFulfillAll = false
				break
			}
		}

		if canFulfillAll {
			dist := haversine(wh.Location, customerLoc)
			cost := (dist * s.costPerKm) + wh.LaborCost
			if cost < minSingleCost {
				minSingleCost = cost
				bestSingleWH = wh
			}
		}
	}

	if bestSingleWH != nil {
		dist := haversine(bestSingleWH.Location, customerLoc)
		return &AllocationPlan{
			Packages: []ShipmentPackage{
				{
					WarehouseID: bestSingleWH.ID,
					Items:       cart,
					FreightCost: dist * s.costPerKm,
				},
			},
			TotalCost:  minSingleCost,
			SplitCount: 0,
		}, nil
	}

	// 2. Multi-Warehouse Greedy Fallback (Minimize split penalty)
	unfulfilled := make(map[string]int)
	for _, item := range cart {
		unfulfilled[item.SKU] = item.Quantity
	}

	plan := &AllocationPlan{}

	for len(unfulfilled) > 0 {
		var selectedWH *Warehouse
		var maxCovered int
		var bestScore = math.MaxFloat64

		for i := range s.warehouses {
			wh := &s.warehouses[i]
			coveredCount := 0
			for sku, needed := range unfulfilled {
				if avail := wh.Inventory[sku]; avail > 0 {
					if avail >= needed {
						coveredCount += needed
					} else {
						coveredCount += avail
					}
				}
			}

			if coveredCount > 0 {
				dist := haversine(wh.Location, customerLoc)
				score := (dist * s.costPerKm) / float64(coveredCount)
				if score < bestScore {
					bestScore = score
					selectedWH = wh
					maxCovered = coveredCount
				}
			}
		}

		if selectedWH == nil || maxCovered == 0 {
			return nil, fmt.Errorf("insufficient global inventory to fulfill cart")
		}

		// Allocate items to selected warehouse
		pkgItems := make([]CartItem, 0)
		for sku, needed := range unfulfilled {
			avail := selectedWH.Inventory[sku]
			if avail > 0 {
				qtyToTake := needed
				if avail < needed {
					qtyToTake = avail
				}
				pkgItems = append(pkgItems, CartItem{SKU: sku, Quantity: qtyToTake})
				unfulfilled[sku] -= qtyToTake
				if unfulfilled[sku] == 0 {
					delete(unfulfilled, sku)
				}
			}
		}

		dist := haversine(selectedWH.Location, customerLoc)
		freight := dist * s.costPerKm
		plan.Packages = append(plan.Packages, ShipmentPackage{
			WarehouseID: selectedWH.ID,
			Items:       pkgItems,
			FreightCost: freight,
		})
		plan.TotalCost += freight + selectedWH.LaborCost
	}

	plan.SplitCount = len(plan.Packages) - 1
	plan.TotalCost += float64(plan.SplitCount) * s.splitPenalty

	return plan, nil
}

Step 3 — Warehouse Wave & Batch Picking Heuristics

Once an order is assigned to a specific warehouse, internal warehouse management systems (WMS) optimize picker travel paths across warehouse storage racks. Walking between aisles accounts for over 50% of total order fulfillment time.

graph TD
    Orders["500 Real-Time Orders"] --> WaveEngine["Wave Planning Engine (Batch Orders by Zone & Cutoff Time)"]
    WaveEngine --> Carton["3D Bin Packing (Cartonization Box Sizing)"]
    Carton --> PickerRouting["Picker Route Solver (S-Shape vs Return Aisle Routing)"]
    PickerRouting --> Sortation["Put-Wall Sortation & Automated Packing"]

Picker Path Routing Heuristics

  1. S-Shape (Serpentine) Routing: Pickers traverse an aisle containing pick locations entirely from front to back, enter the adjacent aisle from the back, and return to the front. Optimal when pick density is high (> 3 items per aisle).
  2. Return (Mid-Point) Routing: Pickers enter an aisle only as far as the furthest pick location and return along the same aisle. Optimal for sparse pick densities.
  3. 3D Cartonization Bin Packing: Before picking begins, algorithms solve a 3-Dimensional Bin Packing Problem (3D-BPP) with item orientations and weight distribution constraints to calculate the smallest possible cardboard shipper box, eliminating void space and cutting freight charges.

Step 4 — Amazon CONDOR & Anticipatory Predictive Pre-Positioning

Amazon’s CONDOR (Continuous Optimization and Network Distribution of Resources) operates at an echelon above individual checkout algorithms. Rather than reacting after an order is placed, CONDOR uses machine learning demand forecasts to rebalance physical stock ahead of time:

  • 14-Day Rolling ML Demand Vectors: Forecasts regional customer purchase probabilities for millions of ASINs based on local weather, search traffic, wishlist additions, and historical velocity.
  • Mid-Mile Inventory Transfers: Rebalances high-velocity items from Tier-1 Central Fulfillment Centers to Tier-2 Regional Sortation Hubs located within 50km of metropolitan clusters.
  • Anticipatory Shipping: Packages are pre-labeled and injected into delivery carrier hubs before the final customer purchase occurs, allowing guaranteed same-day delivery at regular freight shipping rates.

Step 5 — Last-Mile Capacitated Vehicle Routing Problem (CVRP)

The final leg from the regional hub to the customer’s front door is solved as a Capacitated Vehicle Routing Problem with Time Windows (CVRPTW). Beyond simple distance minimization, enterprise logistics engines enforce physical fleet payload weight limits, driver duty hour limits, and customer delivery appointment windows.

Production Google OR-Tools VRP Solver

The Python implementation below configures Google OR-Tools with Guided Local Search (GLS) metaheuristics and dropped-stop disjunction penalties:

from typing import Dict, List, Tuple
from ortools.constraint_solver import routing_enums_pb2, pywrapcp

def solve_capacitated_vrp(
    distance_matrix: List[List[int]],
    demands: List[int],
    vehicle_capacities: List[int],
    depot: int = 0,
    time_limit_sec: int = 15,
) -> Tuple[Dict[int, List[int]], int]:
    """Solves Capacitated VRP with payload limits, drop penalties, and GLS metaheuristics."""
    num_locations = len(distance_matrix)
    num_vehicles = len(vehicle_capacities)

    manager = pywrapcp.RoutingIndexManager(num_locations, num_vehicles, depot)
    routing = pywrapcp.RoutingModel(manager)

    # 1. Transit Cost Callback
    def distance_callback(from_index: int, to_index: int) -> int:
        return distance_matrix[manager.IndexToNode(from_index)][manager.IndexToNode(to_index)]

    transit_idx = routing.RegisterTransitCallback(distance_callback)
    routing.SetArcCostEvaluatorOfAllVehicles(transit_idx)

    # 2. Vehicle Capacity Constraints
    def demand_callback(from_index: int) -> int:
        return demands[manager.IndexToNode(from_index)]

    demand_idx = routing.RegisterUnaryTransitCallback(demand_callback)
    routing.AddDimensionWithVehicleCapacity(
        demand_idx,
        0,                  # Null slack
        vehicle_capacities, # Vehicle capacity limits
        True,               # Cumulative starts at zero
        "Capacity"
    )

    # 3. Disjunctions: Penalty for unserviced stops when fleet capacity is exceeded
    penalty = 100_000
    for node in range(1, num_locations):
        routing.AddDisjunction([manager.NodeToIndex(node)], penalty)

    # 4. Search Metaheuristics
    search_params = pywrapcp.DefaultRoutingSearchParameters()
    search_params.first_solution_strategy = (
        routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
    )
    search_params.local_search_metaheuristic = (
        routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
    )
    search_params.time_limit.seconds = time_limit_sec
    search_params.log_search = False

    solution = routing.SolveWithParameters(search_params)
    if not solution:
        return {}, -1

    routes: Dict[int, List[int]] = {}
    for vehicle_id in range(num_vehicles):
        index = routing.Start(vehicle_id)
        route = []
        while not routing.IsEnd(index):
            route.append(manager.IndexToNode(index))
            index = solution.Value(routing.NextVar(index))
        route.append(manager.IndexToNode(index))
        routes[vehicle_id] = route

    return routes, solution.ObjectiveValue()

Step 6 — Multi-Criteria Algorithmic Comparison & Benchmarks

The table below contrasts fulfillment allocation algorithms across key operational dimensions:

Algorithm / ApproachSolve Time (100 SKUs, 10 WHs)Cost Optimality GapSplit-Shipment RateProduction Scale Suitability
Greedy Nearest Neighbor< 2 ms$+14.5%$ (Suboptimal)$28.4%$ (High splits)Real-time web checkout latency budgets
Integer Linear Programming (ILP)450 - 1,200 ms$0.0%$ (Exact Optimal)$8.2%$ (Minimal splits)Offline batch wave planning & large carts
Simulated Annealing Metaheuristic35 ms$+2.1%$ (Near Optimal)$11.4%$Balanced real-time cart allocation
Genetic Algorithm (GA)120 ms$+3.8%$$13.5%$Complex carrier capacity constraints

Frequently Asked Questions

How do e-commerce algorithms decide which warehouse fulfills an order?

Warehouse selection algorithms evaluate candidate fulfillment centers using a multi-criteria cost function combining freight shipping distance, carrier transit rates, pick/pack labor costs, delivery SLA breach risks, and split-shipment penalties. When multiple warehouses carry requested SKUs, solvers evaluate whether consolidating into a single shipment is more economical than splitting the order.

What is the financial impact of a split shipment in e-commerce?

A split shipment occurs when items in a single customer order ship from multiple fulfillment centers in separate boxes. Each split incur duplicate box packing labor, packaging materials, and baseline carrier delivery fees, adding $4.50–$7.00 per extra parcel while multiplying customer friction and carbon emissions.

How does Available-to-Promise (ATP) differ from physical on-hand stock?

Physical on-hand inventory counts all units stored within warehouse bins. Available-to-Promise (ATP) inventory subtracts hard-committed units (confirmed paid orders undergoing picking) and active soft reservations (items in customer checkout funnels). Fulfillment algorithms route orders strictly against ATP to eliminate overselling.

What is the Vehicle Routing Problem (VRP) in last-mile delivery?

The Vehicle Routing Problem with Time Windows (CVRPTW) is a combinatorial optimization challenge that calculates the most cost-effective travel paths for a fleet of delivery vehicles servicing customer destinations. Solvers like Google OR-Tools enforce vehicle payload capacities, delivery time windows, and driver shift constraints to minimize total kilometers driven.

How does Amazon's anticipatory shipping model reduce delivery times?

Anticipatory shipping uses predictive machine learning to transport high-probability items from central national fulfillment centers to regional sortation hubs near customer population centers before orders are placed. When the buyer checks out, the item is already within the local metropolitan delivery radius, enabling sub-24-hour delivery.