Pond: An Efficient Python Object Pool Library
The article introduces Pond, a high‑performance Python object‑pool library that reduces memory consumption and GC pressure by reusing objects, explains its design components, automatic eviction strategy, thread‑safe borrowing and returning mechanisms, and provides detailed usage examples with code snippets.
In program design, creating and destroying objects repeatedly can cause high memory consumption and lead to memory leaks, especially when many short‑lived objects are generated, which may eventually slow down or crash the system.
An object pool stores a set of pre‑created objects, allowing programs to obtain objects from the pool instead of instantiating new ones, thereby reducing GC pressure and improving performance.
Pond is a Python object‑pool library that offers high performance, low memory usage, and high hit rates. It automatically adjusts the number of idle objects based on usage frequency and provides a near‑complete test suite, extensive documentation, and code comments.
The library draws inspiration from Apache Commons Pool, Netty Recycler, HikariCP, and Caffeine, combining their strengths. It uses an approximate counter to track usage frequency with minimal memory overhead, achieving up to 48.85% memory reduction under random traffic.
Design Overview
Pond consists of three main components— FactoryDict , Counter , and PooledObjectTree —plus a dedicated reclamation thread.
FactoryDict
Users implement a PooledObjectFactory that defines object creation, initialization, destruction, validation, and reset. FactoryDict maps each factory class name to its instance, enabling the pool to manage heterogeneous objects.
Counter
The Counter holds an approximate counter used to track object usage frequency for eviction decisions.
PooledObjectTree
PooledObjectTree is a dictionary where each key maps to a thread‑safe FIFO queue. Each queue stores PooledObject instances that contain creation time, last borrowed time, and the actual object.
Thread Safety
Borrowing and recycling use Python’s queue.Queue , which is safe for multi‑threaded access. Locks are handled by the caller, making the entire process thread‑safe.
Borrowing Mechanism
When borrowing, Pond checks if a pool for the requested type exists; if empty, it creates a new object. If objects are available, it dequeues one and validates it, destroying invalid objects and continuing until a usable one is found. Each successful borrow increments the Counter .
Recycling Mechanism
On recycle, Pond verifies the pool’s capacity; if full, the returned object is destroyed. It also checks if the object has been borrowed longer than the configured timeout and discards it if so.
Automatic Reclamation
A background thread runs at a configurable interval (default 300 s) to evict rarely used objects based on the eviction_weight and usage frequency.
Usage Instructions
Install the library:
<code>pip install pondpond</code>Define a factory for the objects you want to pool (example uses a Dog class):
<code>class Dog:
name: str
validate_result: bool = True
class PooledDogFactory(PooledObjectFactory):
def creatInstantce(self) -> PooledObject:
dog = Dog()
dog.name = "puppy"
return PooledObject(dog)
def destroy(self, pooled_object: PooledObject):
del pooled_object
def reset(self, pooled_object: PooledObject) -> PooledObject:
pooled_object.keeped_object.name = "puppy"
return pooled_object
def validate(self, pooled_object: PooledObject) -> bool:
return pooled_object.keeped_object.validate_result
</code>Create a Pond instance with optional parameters:
<code>pond = Pond(borrowed_timeout=2,
time_between_eviction_runs=-1,
thread_daemon=True,
eviction_weight=0.8)
</code>Parameter meanings:
borrowed_timeout : maximum borrow duration in seconds; objects exceeding this are destroyed on return.
time_between_eviction_runs : interval in seconds between automatic reclamation cycles.
thread_daemon : if True , the reclamation thread stops when the main thread exits.
eviction_weight : weight used to decide which objects are eligible for eviction based on usage frequency.
Instantiate the factory and register it with the pool:
<code>factory = PooledDogFactory(pooled_maxsize=10, least_one=False)
pond.register(factory)
</code>Borrow and recycle objects:
<code>pooled_object: PooledObject = pond.borrow(factory)
dog: Dog = pooled_object.use()
pond.recycle(pooled_object, factory)
</code>You can also borrow/return by factory name:
<code>pooled_object = pond.borrow(name="PuppyFactory")
dog = pooled_object.use()
pond.recycle(pooled_object, name="PuppyFactory")
</code>Clear an entire pool:
<code>pond.clear(factory)
# or by name
pond.clear(name="PuppyFactory")
</code>For more details, refer to the GitHub project or the author’s blog.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.