"Open-source" high-performance object pool library for Python, with smaller memory usage and higher hit rate

1. Project Overview
Pond is a high-performance, low-memory, and high-hit-rate general object pool for Python, with the ability to automatically adjust the number of idle objects in each pool based on frequency statistics. It is a Python object pool library that is currently the first community-opened library with complete test cases, a coverage rate of over 90%, complete code comments, and complete documentation.
2. Open Source License
Licensed under Apache-2.0
3. Screenshots
Because there is currently no good object pool library in Python that is complete in terms of test cases, code comments, and documentation, and the mainstream object pool libraries do not have a smart automatic recycling mechanism, Pond may be the first Python object pool library that is community-opened, complete in terms of test cases, with a coverage rate of over 90%, complete code comments, and complete documentation.
Pond draws inspiration from Apache Commons Pool, Netty Recycler, HikariCP, and Caffeine, combining the advantages of multiple parties. Next, Pond uses an approximate counting method to statistically count the usage frequency of each object pool with extremely small memory space, and automatically recycles.
Under the condition of relatively random and average traffic, the default strategy and weight can reduce memory occupancy by 48.85%, and hit rate by 100%.

Under the condition of traffic conforming to the 2/8 law, the default strategy and weight can reduce memory occupancy by 45.7%, and hit rate by 100%.

4. Feature Overview
Design Overview
Pond is mainly composed of FactoryDict, Counter, PooledObjectTree three parts, as well as a separate garbage collection thread.
FactoryDict
To use Pond, you need to implement the object factory PooledObjectFactory, PooledObjectFactory provides object creation, initialization, destruction, and validation operations, which are called by Pond. Therefore, to support storing completely different objects in the object pool, Pond uses a dictionary to record the name of each factory class and the instance object of the factory class implemented by itself.
Each PooledObjectFactory should have the functions of creating objects, destroying objects, verifying whether objects are still available, and resetting objects.
Notably, Pond supports automatic object reset, because in some scenarios, objects may be assigned values for transmission, transmitted, and then recycled, which may cause pollution. It is recommended to implement this function in such scenarios.
Counter
Counter saves an approximate counter.
PooledObjectTree
PooledObjectTree is a dictionary, where each key corresponds to a first-in-first-out queue, and these queues are thread-safe. Each queue saves multiple PooledObject. PooledObject saves the creation time, the last borrowing time, and the actual required object.
Thread Safety
Pond's borrowing and recycling are thread-safe. Python's queue module provides a first-in-first-out (FIFO) data structure suitable for multi-threaded programming, which can be used to safely transmit messages or other data between producer and consumer threads. The lock is handled by the caller, and multiple threads can safely and easily use the same Queue instance. Since Pond's borrowing and recycling are operated on the queue, it can be considered thread-safe in general.
Borrowing Mechanism
When borrowing an object from Pond, it will first check if the type of the object to be borrowed exists in PooledObjectTree, if it exists, it will check if the object pool of this object is empty, if it is empty, a new one will be created.
If there are extra objects in the object pool, it will use queue to pop out an object and verify whether the object is available. If it's not available, it will automatically call the belonging Factory to clean up and destroy the object, and clean up its GC count in Python, making it faster to be GC collected, and keep taking the next one until there's an available one.
If the object is available, it will be returned directly. Regardless of whether an object is taken out from the object pool or a new one is created, a count will be increased using Counter.
Recycling mechanism
When recycling an object, it will judge whether the target object pool exists. If it exists, it will check whether the object pool is full. If it's full, the object to be returned will be automatically destroyed.
Then it will check whether the object has been borrowed for too long. If it exceeds the configured maximum time, it will be cleaned up.
Automatic recycling
Automatic recycling will be executed every 300 seconds by default, and will clean up objects in the object pool that are not frequently used.
5. Tech Stack
Usage instructions
You can first install the Pond library and then reference it in your project.
pip install pondpond from pond import Pond, PooledObjectFactory, PooledObject
First, you need to declare a factory class for the type of object you want to put in, for example, as shown below, we hope to pool the Dog object, so we first declare a PooledDogFactory class and implement PooledObjectFactory.
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
Then, you need to create a Pond object:
pond = Pond(borrowed_timeout=2,
time_between_eviction_runs=-1,
thread_daemon=True,
eviction_weight=0.8)
Pond can pass some parameters in, which represent:
borrowed_timeout: unit in seconds, the maximum borrowing period, objects returned after exceeding the period will be automatically destroyed and will not be put into the object pool.
time_between_eviction_runs: unit in seconds, automatic collection interval.
thread_daemon : Guardian thread, if True, the auto-reclaim thread will be closed along with the main thread.
eviction_weight : Auto-reclaim weight, will multiply this weight with the maximum usage frequency, objects in the object pool with usage frequency less than this value will enter the cleaning step.
Instantiation factory class:
factory = PooledDogFactory(pooled_maxsize=10, least_one=False)
All classes that inherit from PooledObjectFactory will have a constructor, which can pass pooled_maxsize and least_one two parameters.
pooled_maxsize : The maximum number of objects that can be placed in the object pool generated by this factory class.
least_one : If True, when entering auto-clearing, this factory class generated object pool will at least retain one object.
Register this factory object to Pond, default will use factory's class name as PooledObjectTree's key:
pond.register(factory)
Of course, you can also customize it, the name will be used as PooledObjectTree's key:
pond.register(factory, name="PuppyFactory")
After registration, Pond will automatically start creating objects according to the pooled_maxsize set in the factory until the object pool is filled.
Borrowing and returning objects:
pooled_object: PooledObject = pond.borrow(factory) dog: Dog = pooled_object.use() pond.recycle(pooled_object, factory)
Of course, you can use the name to borrow and return:
pooled_object: PooledObject = pond.borrow(name="PuppyFactory") dog: Dog = pooled_object.use() pond.recycle(pooled_object, name="PuppyFactory")
Complete clearing of an object pool:
pond.clear(factory)
Clear an object pool by name:
pond.clear(name="PuppyFactory")
In normal cases, you only need to use the above methods, object generation and object reclamation are fully automatic.
Technical principle
Detailed technical principles can be seen: https://qin.news/pond/
Reference
@software{Pond,
author = {Andy Qin},
title = {{Pond: A high performance object-pooling library for Python}},
year = {2022},
url = {https://github.com/T-baby/pondpond},
}