A Pool<T> for expensive objects
See original GitHub issueOriginal issue created by kohanyi.robert on 2011-08-10 at 06:03 AM
I’ve a use-case where where I pool Sockets (SocketChannels) and ByteBuffers and I’ve managed to hack-up a simple Pool / BlockingPool interface for myself with an implementation which uses Suppliers to supply objects for a pool on-demand. I’d like to propose to add something similar to Guava.
Basically the interface(s) goes like this: http://pastebin.com/QJ6v7MxD
When poll() / take() is called the Pool would use a Supplier to create a new object for the pool. A Pool could be initialized like this:
Pools.<O>create(int capacity, Supplier<O> supplier);
supplier
here could create a new object every time its get() method is called (in my case it creates a new unconnected SocketChannel).
As I searched for pooling
on SO the only thing I learned was that nobody likes to use commons-pool, so I thought maybe Guava would extend its rich set of features to cover this, as everybody seems to like Guava. So, is this idea any good? (Btw. here: http://pastebin.com/fwYtKNg2 is my implementation, not tested extensively.)
Issue Analytics
- State:
- Created 9 years ago
- Reactions:12
- Comments:37 (5 by maintainers)
Top GitHub Comments
Sorry, unfortunately this has never seemed to inch its way up the priority list at all.
Original comment posted by Ben.Manes on 2013-07-09 at 09:15 AM
I guess I’ll throw my hat into the ring…
I was asked by the Cassandra folks if I could implement a class mixing CLHM (predecessor to Guava’s Cache), an object pool, and a multimap. The use-case is for maintaining a bound (size, ttl, or idle) on the total number of SSTable random access file readers, with the ability to pool multiple readers per SSTable. As this could impact latencies, a goal was to make it highly concurrent.
The interface is classic and not very interesting.
Internally the resources are denormalized into a cache of synthetic keys to resources. A weak value cache of key to transfer queue acts as a view layer to manage the available resources that category type. A transfer queue is used to provide a fast exchange between producers (release) and consumers (borrow), as elimination helps alleviate contention. The resource’s synthetic key retains a hard reference to its queue, allowing unused queues to be aggressively garbage collected by weak references.
Each resource operates within a simple state machine: IDLE, IN_FLIGHT, RETIRED, and DEAD. The idle and in-flight states are self explanatory, indicating only if the resource is in the transfer queue. The retired state is transitioned to when the cache evicts a resource currently being used, thereby requiring the release() to transition it to the dead state. The lifecycle listeners allows the resource to be reset, closed, etc. as needed.
The time-to-idle is a bit naive, as I didn’t want to complicate it early on. A secondary cache is used so that the idle time is counted as the time the resource is not in-flight. This could be optimized by using the lock amortization technique directly and not be bang against the hash table’s locks. When the idle cache evicts, it transitions the resource to the retired state and invalidates it in the primary cache.
This was written over the July 4th holiday for a specific use-case, so I am sure there’s more that could be flushed out. That also means that while it has unit tests, it has not been benchmarked.
https://github.com/ben-manes/multiway-pool
Cheers, Ben