Model Switching & Cancel¶
This notebook shows how to:
Route orders to the right product using
product_idSwitch models mid-training using
discard()Cancel individual orders with
cancel()Replace a supplier gracefully
import time
import torch
from softs import (
Broker, Supplier, Client, ShmMedium,
BatchConfig, TensorSpec, EndpointConfig,
)
Setup¶
One supplier serves 3 product ids. Each product produces different data so we can verify routing is correct.
config = BatchConfig([TensorSpec("x", (4,), "float32")])
endpoints = EndpointConfig()
broker = Broker(endpoints=endpoints)
broker.start()
time.sleep(0.2)
# Supplier produces product-dependent data
MODEL_VALUES = {"layer_0": 0.0, "layer_1": 1.0, "layer_2": 2.0}
def generator(product_id: str) -> bytes:
val = MODEL_VALUES[product_id]
return config.encode(x=torch.full((4,), val))
supplier = Supplier(
generator_fn=generator,
product_ids=list(MODEL_VALUES.keys()),
endpoint=endpoints.backend,
medium_cls=ShmMedium,
slot_size=config.nbytes(),
)
supplier.start()
time.sleep(0.2)
client = Client(
endpoint=endpoints.frontend,
medium_cls=ShmMedium,
slot_size=config.nbytes(),
num_slots=8,
)
client.hello()
print("Broker started, supplier ready.")
Model-aware routing¶
Orders are routed to the correct product. Each product returns its own value.
for product_id, expected_val in MODEL_VALUES.items():
slot = client.request_sample(product_id, timeout_ms=2000)
data = config.decode(client.medium.read(slot))
client.release_slot(slot)
assert data["x"][0].item() == expected_val
print(f"{product_id}: {data['x']}")
Model switching with discard()¶
Simulate layer-by-layer training: train on layer_0, then switch to layer_1.
discard() cancels all pending orders and drains stale completions.
for layer_idx in range(3):
product_id = f"layer_{layer_idx}"
print(f"\nTraining on {product_id}:")
for step in range(3):
slot = client.request_sample(product_id, timeout_ms=2000)
data = config.decode(client.medium.read(slot))
client.release_slot(slot)
assert data["x"][0].item() == float(layer_idx)
print(f" step {step}: x[0]={data['x'][0].item()}")
# Switch to next layer: discard old work
cancelled = client.discard()
print(f" Discarded {cancelled} pending orders")
Cancel a specific order¶
order_id = client.request_slot("layer_0")
print(f"Requested order: {order_id}")
time.sleep(0.1)
ok = client.cancel(order_id)
print(f"Cancelled: {ok}")
print(f"Pending slots after cancel: {len(client._pending_slots)}")
Supplier replacement¶
Stop the current supplier, start a new one. The broker detects the GOODBYE immediately and routes to the replacement.
supplier.stop()
time.sleep(0.2)
print(f"Suppliers after stop: {broker.get_stats().connected_suppliers}")
# Start replacement
supplier2 = Supplier(
generator_fn=generator,
product_ids=list(MODEL_VALUES.keys()),
endpoint=endpoints.backend,
medium_cls=ShmMedium,
slot_size=config.nbytes(),
)
supplier2.start()
time.sleep(0.2)
print(f"Suppliers after new start: {broker.get_stats().connected_suppliers}")
slot = client.request_sample("layer_0", timeout_ms=2000)
data = config.decode(client.medium.read(slot))
client.release_slot(slot)
print(f"New supplier serving: {data['x']}")
Graceful shutdown¶
client.close()
supplier2.stop()
time.sleep(0.2)
broker.stop()
print("Shutdown complete.")