SimPy FilterStore Explained: Getting Exactly What You Need

Store gives you the next item. FilterStore lets you be picky.

The Problem Store Can't Solve

Regular Store is FIFO—first in, first out. But what if you need a specific item?

# With regular Store
store = simpy.Store(env)
store.put({'type': 'red', 'id': 1})
store.put({'type': 'blue', 'id': 2})
store.put({'type': 'red', 'id': 3})

# You can only get item 1, then 2, then 3
# No way to say "give me a blue one"

FilterStore fixes this.

Basic Usage

import simpy

env = simpy.Environment()
store = simpy.FilterStore(env, capacity=10)

# Put some items
yield store.put({'type': 'red', 'value': 1})
yield store.put({'type': 'blue', 'value': 2})
yield store.put({'type': 'red', 'value': 3})

# Get specifically a blue item
blue = yield store.get(lambda x: x['type'] == 'blue')
print(blue)  # {'type': 'blue', 'value': 2}

The filter function receives each item and returns True if it matches.

How Filtering Works

When you get() with a filter:

  1. FilterStore checks each item against your filter
  2. First matching item is returned
  3. If no match, the get blocks until a matching item arrives
# Will block until a green item is put
green = yield store.get(lambda x: x['type'] == 'green')

Real-World Example: Parts Bin

class Part:
    def __init__(self, part_type, size, quality):
        self.part_type = part_type
        self.size = size
        self.quality = quality

    def __repr__(self):
        return f"Part({self.part_type}, {self.size}, {self.quality})"

def supplier(env, bin):
    """Delivers random parts."""
    types = ['bolt', 'nut', 'washer']
    sizes = ['small', 'medium', 'large']

    while True:
        part = Part(
            random.choice(types),
            random.choice(sizes),
            random.uniform(0.8, 1.0)
        )
        yield bin.put(part)
        yield env.timeout(1)

def assembler(env, bin, needs_type, needs_size):
    """Assembles using specific parts."""
    while True:
        # Get exactly what we need
        part = yield bin.get(
            lambda p: p.part_type == needs_type and p.size == needs_size
        )
        print(f"Got {part} at {env.now}")
        yield env.timeout(5)  # Assembly time

env = simpy.Environment()
parts_bin = simpy.FilterStore(env)

env.process(supplier(env, parts_bin))
env.process(assembler(env, parts_bin, 'bolt', 'medium'))
env.process(assembler(env, parts_bin, 'nut', 'small'))

env.run(until=50)

Multiple Criteria

Combine conditions in the filter:

# Get a large red item
item = yield store.get(
    lambda x: x['type'] == 'red' and x['size'] == 'large'
)

# Get any item with value > 10
item = yield store.get(lambda x: x['value'] > 10)

# Get by multiple possible types
item = yield store.get(lambda x: x['type'] in ['red', 'blue'])

Default Get (No Filter)

Without a filter, FilterStore behaves like regular Store:

# Get any item (FIFO)
item = yield store.get()

Checking Available Items

Peek at what's in the store:

# All items
print(store.items)

# Check if any match
matching = [x for x in store.items if x['type'] == 'blue']
print(f"Blue items available: {len(matching)}")

But remember: checking doesn't reserve. Another process might grab it first.

Non-Blocking Filter Get

What if you don't want to wait?

def try_get_blue(env, store, timeout):
    get_event = store.get(lambda x: x['type'] == 'blue')
    timeout_event = env.timeout(timeout)

    result = yield get_event | timeout_event

    if get_event in result:
        return get_event.value
    else:
        get_event.cancel()
        return None

Priority with Filtering

Need priority AND filtering? Combine patterns:

class PrioritizedPart:
    def __init__(self, priority, part_type, part_id):
        self.priority = priority
        self.part_type = part_type
        self.part_id = part_id

def get_highest_priority_of_type(store, part_type):
    """Get highest priority item of specific type."""
    matching = [p for p in store.items if p.part_type == part_type]
    if not matching:
        return None
    matching.sort(key=lambda x: x.priority)
    return matching[0]

Or use a custom store class.

FilterStore vs PriorityStore

Feature FilterStore PriorityStore
Selection By custom filter By priority
Multiple criteria Yes No
Get any item Yes Yes (lowest priority)
Complexity Higher Lower

Use FilterStore when selection criteria are complex. Use PriorityStore when you just need ordering.

Example: Taxi Dispatch

class Ride:
    def __init__(self, pickup, destination, passengers):
        self.pickup = pickup
        self.destination = destination
        self.passengers = passengers

def dispatch_taxi(env, ride_queue, taxi_type, capacity):
    """Taxi only takes rides it can handle."""
    while True:
        # Only get rides within capacity
        ride = yield ride_queue.get(lambda r: r.passengers <= capacity)
        print(f"{taxi_type} takes ride for {ride.passengers} from {ride.pickup}")
        yield env.timeout(10)

env = simpy.Environment()
rides = simpy.FilterStore(env)

# Small taxi (up to 4 passengers)
env.process(dispatch_taxi(env, rides, "SmallTaxi", 4))

# Large taxi (up to 7 passengers)
env.process(dispatch_taxi(env, rides, "LargeTaxi", 7))

# Generate rides
def generate_rides(env, queue):
    for i in range(20):
        ride = Ride(f"Location{i}", f"Dest{i}", random.randint(1, 6))
        yield queue.put(ride)
        yield env.timeout(2)

env.process(generate_rides(env, rides))
env.run(until=100)

Performance Consideration

FilterStore checks items linearly. With thousands of items, this matters.

For high-performance scenarios: - Keep stores small - Use multiple stores by category - Consider custom data structures

Common Gotchas

Filter Never Matches

# Blocks forever if no blue items ever arrive
blue = yield store.get(lambda x: x['type'] == 'blue')

Add timeouts for safety.

Modifying Items

item = yield store.get(lambda x: x['id'] == 5)
item['status'] = 'processed'  # Changes the object

# If you put it back, filters will see the change
yield store.put(item)

Be careful about mutating items.

Summary

FilterStore: - Lets you get specific items, not just FIFO - Filter function returns True for matching items - Blocks until a match is found - Perfect for typed queues, dispatch systems, inventory

When FIFO isn't enough, filter.

Next Steps


Discover the Power of Simulation

Want to become a go-to expert in simulation with Python? The Complete Simulation Bootcamp will show you how simulation can transform your career and your projects.

Explore the Bootcamp