Catching Operational Rapidity using Optimization

Mert Parcaoglu
Trendyol Tech
Published in
5 min readJul 20, 2024

Introduction

At Trendyol, we receive an average of 150 B2B orders daily, each containing approximately 125 items, though this number can peak at 42,000 items. In contrast, B2C orders can reach up to 1 million per day. The shared handling of these orders within our warehouse operations results in significant logistical challenges, which we refer to as rapidity. One major aspect of this operational burden is determining from which part of the warehouse each item in a customer’s order should be picked. This decision is followed by determining the specific location within that area, the sequence of picking multiple items, and assigning the picking tasks to staff. We recognize that improving these initial steps with minimal effort can yield significant contributions towards perfecting our operations.

Problem Definition

Figure-1: A sample stock representation in warehouse

Considering the high frequency of incoming requests per second from B2C and B2B orders, it is clear that these operations must be executed asynchronously. Expecting efficiency comparable to batch processing in an asynchronous setup is unrealistic. However, it is possible to achieve efficiency through certain strategic moves even with asynchronous decisions. We focus on B2B order allocation in this article. Currently, our operational teams are allocating items of an order based on prioritized zones, which often leads to missed opportunities. Let’s illustrate a B2B order allocation with an example:

Zone Priority List : A1, B1, A2, B2, A3, B3
Order : { Item1, Item2, Item3 }

Figure-2: Asis solution representation

In the as-is solution in Figure-2, item allocations are completed using two zones due to asynchronous allocation. However, in the optimal solution in Figure-3, all allocations are consolidated into a single zone. Achieving the optimal solution requires freezing to gather all item location information, which is not feasible when considering the combined allocation of B2C and B2B orders with the same items, as this would cause unacceptable delays.

Figure-3: Optimal solution representation

To explain this further, an item in any of our warehouses is located in 48 different locations on average. So, if we wanted to achieve the optimal solution, when a B2B order of 125 items is placed, 125 * 48 = 6,000 item locations would need to be frozen. No item can be allocated for another order until allocation from these frozen locations occurs. B2C orders of up to 1 million per day would necessarily enter a queue here. To prevent this, currently, one-by-one allocation is made based on zone priority in the current system. This causes inefficiency, which we will try to solve in the next section.

Our trade-off here, as in most operations research problems, is time versus efficiency. If we ignore time constraints in real-life problems, we will only create an efficient approach on paper.

Solution: Let’s iterate!

Our optimization team has devised a solution that progresses through a distributed asynchronous method. Imagine a master-worker system where workers retrieve suitable stock locations for each product and make assignments based on a sorted list. These workers operate independently without communicating with each other while handling items for the same order. The master maintains a list of zones to which the order has been assigned, aiming to increase the same zone ratio. Our analysis shows that increasing the same zone ratio improves picking times.

Here’s a look at the structure and the code we’ve implemented. Below, I will showcase the old and new approaches.

As-is Approach:

# Function to sort a stock_info list and return the first element
def sort_and_select_first(stock_list):
sorted_list = sorted(stock_list)
return sorted_list[0] if sorted_list else None

# Use ThreadPoolExecutor to process lists in parallel
with concurrent.futures.ThreadPoolExecutor() as executor:
# Map the sort_and_select_first function to the stock_info lists
results = list(executor.map(sort_and_select_first, stock_info_lists))

old_final_zone_set = set([s[:2] for s in results])
print(old_final_zone_set)

Async Optimization Approach:

# Function to look used zones and return from a stock in used zones if there is else return first stock from sorted list
def select_first_in_the_zone_list(stock_list, zone_set):
zone_keys = set([s[:2] for s in stock_list])
shared_zone = zone_keys.intersection(zone_set)
if shared_zone:
shared_zone = sorted(shared_zone)[0]
return sorted([s for s in stock_list if s[:2] == shared_zone])[0]
else:
selected_stock = sorted(stock_list)[0]
zone_set.add(selected_stock[:2])
return selected_stock

# Use ThreadPoolExecutor to process lists in parallel
zone_set = set()
results = []

# Wrapper function to use the shared zone_set in parallel execution
def process_list(stock_list):
global zone_set
local_zone_set = set(zone_set) # Make a local copy of the current zone set
result = select_first_in_the_zone_list(stock_list, local_zone_set)
zone_set.add(result[:2]) # Update the shared zone_set with the selected stock's zone
return result

# Execute the process_list function for each list in stock_info_lists
with concurrent.futures.ThreadPoolExecutor() as executor:
results = list(executor.map(process_list, stock_info_lists))

# Display the final zone set
print(f"Final zone set: {zone_set}")

Conclusion

The asynchronous optimization approach was tested on 100,000 samples, yielding better results in 23,000 cases with an approximate 8% average reduction in the number of zones used.

Even a minor optimization step can lead to significant operational enhancements. When the time spent achieving the optimal is operationally unacceptable, simple improvements should be pursued.

Instead of giving up because we cannot find the optimal solution due to technological limitations, advancing with the possibility of improvement perfectly aligns with the iterative mentality of the Trendyol tech teams.

Join Us

We’re building a team of the brightest minds in our industry. Interested in joining us? Visit the pages below to learn more about our open positions.

--

--