Adaptive sizing¶
Adaptive sizing lets the manager grow and shrink idle executor pools based on a runtime signal.
Example¶
examples/04_adaptive_sizing.py¶
"""
Adaptive sizing with `size_provider`.
The manager does not need to know what your units are. They can be connected
devices, tenants, queues, shards, customers, or anything else.
The desired pool count is:
max(min_pools, ceil(size_provider() / units_per_pool))
capped by max_pools.
"""
from __future__ import annotations
import asyncio
from leasepool import LeasedExecutorManager
async def main() -> None:
connected_devices: set[str] = set()
manager = LeasedExecutorManager(
backend="thread",
max_pools=5,
min_pools=1,
units_per_pool=10,
size_provider=lambda: len(connected_devices),
check_interval=60,
workers_per_pool=2,
)
await manager.start()
try:
print("Initial desired:", manager.desired_executor_count())
print("Initial total:", manager.total_count)
for i in range(31):
connected_devices.add(f"device-{i}")
# Wake the checker immediately instead of waiting for check_interval.
manager.notify_scale_changed()
await asyncio.sleep(0.1)
print("After adding 31 devices:")
print(" desired:", manager.desired_executor_count())
print(" total:", manager.total_count)
connected_devices.clear()
manager.notify_scale_changed()
await asyncio.sleep(0.1)
print("After removing all devices:")
print(" desired:", manager.desired_executor_count())
print(" total:", manager.total_count)
finally:
await manager.stop()
if __name__ == "__main__":
asyncio.run(main())
Sizing rule¶
The desired executor count is:
max(min_pools, ceil(size_provider() / units_per_pool))
capped by max_pools.
If size_provider is omitted, the unit count is treated as zero, so the target
is min_pools.
Notify changes¶
When your signal changes, call:
manager.notify_scale_changed()
This wakes the checker immediately instead of waiting for check_interval.
Shrinking behavior¶
Idle executors above the target are shut down.
Non-expired leased executors are not revoked just because the target shrinks. They are returned or shut down when released depending on the new target.
Failure behavior¶
If size_provider raises, the manager logs a debug message and treats the unit
count as zero for that check. This prevents a broken signal from crashing the
checker loop.