Does anyone have a good resource on implementing a shared object pool strategy for a limited resource in vein of Sql connection pooling? (ie would be implemented fully that it is thread safe).
To follow up in regards to @Aaronaught request for clarification the pool usage would be for load balancing requests to an external service. To put it in a scenario that would probably be easier to immediately understand as opposed to my direct situtation. I have a session object that functions similarly to the ISession
object from NHibernate. That each unique session manages it's connection to the database. Currently I have 1 long running session object and am encountering issues where my service provider is rate limiting my usage of this individual session.
Due to their lack of expectation that a single session would be treated as a long running service account they apparently treat it as a client that is hammering their service. Which brings me to my question here, instead of having 1 individual session I would create a pool of different sessions and split the requests up to the service across those multiple sessions instead of creating a single focal point as I was previously doing.
Hopefully that background offers some value but to directly answer some of your questions:
Q: Are the objects expensive to create?
A: No objects are a pool of limited resources
Q: Will they be acquired/released very frequently?
A: Yes, once again they can be thought of NHibernate ISessions where 1 is usually acquired and released for the duration of every single page request.
Q: Will a simple first-come-first-serve suffice or do you need something more intelligent, i.e. that would prevent starvation?
A: A simple round robin type distribution would suffice, by starvation I assume you mean if there are no available sessions that callers become blocked waiting for releases. This isn't really applicable since the sessions can be shared by different callers. My goal is distribute the usage across multiple sessions as opposed to 1 single session.
I believe this is probably a divergence from a normal usage of an object pool which is why I originally left this part out and planned just to adapt the pattern to allow sharing of objects as opposed to allowing a starvation situation to ever occur.
Q: What about things like priorities, lazy vs. eager loading, etc.?
A: There is no prioritization involved, for simplicity's sake just assume that I would create the pool of available objects at the creation of the pool itself.
Java oriented, this article expose the connectionImpl pool pattern and the abstracted object pool pattern and could be a good first approach : http://www.developer.com/design/article.php/626171/Pattern-Summaries-Object-Pool.htm
Object pool Pattern:
I really like Aronaught's implementation -- especially since he handles the waiting on resource to become available through the use of a semaphore. There are several additions I would like to make:
sync.WaitOne()
tosync.WaitOne(timeout)
and expose the timeout as a parameter onAcquire(int timeout)
method. This would also necessitate handling the condition when the thread times out waiting on an object to become available.Recycle(T item)
method to handle situations when an object needs to be recycled when a failure occurs, for example.Something like this might suit your needs.
Example Usage
Back in the day Microsoft provided a framework through Microsoft Transaction Server (MTS) and later COM+ to do object pooling for COM objects. That functionality was carried forward to System.EnterpriseServices in the .NET Framework and now in Windows Communication Foundation.
Object Pooling in WCF
This article is from .NET 1.1 but should still apply in the current versions of the Framework (even though WCF is the preferred method).
Object Pooling .NET
Object Pooling in .NET Core
The dotnet core has an implementation of object pooling added to the base class library (BCL). You can read the original GitHub issue here and view the code for System.Buffers. Currently the
ArrayPool
is the only type available and is used to pool arrays. There is a nice blog post here.An example of its usage can be seen in ASP.NET Core. Because it is in the dotnet core BCL, ASP.NET Core can share it's object pool with other objects such as Newtonsoft.Json's JSON serializer. You can read this blog post for more information on how Newtonsoft.Json is doing this.
Object Pooling in Microsoft Roslyn C# Compiler
The new Microsoft Roslyn C# compiler contains the ObjectPool type, which is used to pool frequently used objects which would normally get new'ed up and garbage collected very often. This reduces the amount and size of garbage collection operations which have to happen. There are a few different sub-implementations all using ObjectPool (See: Why are there so many implementations of Object Pooling in Roslyn?).
1 - SharedPools - Stores a pool of 20 objects or 100 if the BigDefault is used.
2 - ListPool and StringBuilderPool - Not strictly separate implementations but wrappers around the SharedPools implementation shown above specifically for List and StringBuilder's. So this re-uses the pool of objects stored in SharedPools.
3 - PooledDictionary and PooledHashSet - These use ObjectPool directly and have a totally separate pool of objects. Stores a pool of 128 objects.
Microsoft.IO.RecyclableMemoryStream
This library provides pooling for
MemoryStream
objects. It's a drop-in replacement forSystem.IO.MemoryStream
. It has exactly the same semantics. It was designed by Bing engineers. Read the blog post here or see the code on GitHub.Note that
RecyclableMemoryStreamManager
should be declared once and it will live for the entire process–this is the pool. It is perfectly fine to use multiple pools if you desire.This question is a little trickier than one might expect due to several unknowns: The behaviour of the resource being pooled, the expected/required lifetime of objects, the real reason that the pool is required, etc. Typically pools are special-purpose - thread pools, connection pools, etc. - because it is easier to optimize one when you know exactly what the resource does and more importantly have control over how that resource is implemented.
Since it's not that simple, what I've tried to do is offer up a fairly flexible approach that you can experiment with and see what works best. Apologies in advance for the long post, but there is a lot of ground to cover when it comes to implementing a decent general-purpose resource pool. and I'm really only scratching the surface.
A general-purpose pool would have to have a few main "settings", including:
For the resource loading mechanism, .NET already gives us a clean abstraction - delegates.
Pass this through the pool's constructor and we're about done with that. Using a generic type with a
new()
constraint works too, but this is more flexible.Of the other two parameters, the access strategy is the more complicated beast, so my approach was to use an inheritance (interface) based approach:
The concept here is simple - we'll let the public
Pool
class handle the common issues like thread-safety, but use a different "item store" for each access pattern. LIFO is easily represented by a stack, FIFO is a queue, and I've used a not-very-optimized-but-probably-adequate circular buffer implementation using aList<T>
and index pointer to approximate a round-robin access pattern.All of the classes below are inner classes of the
Pool<T>
- this was a style choice, but since these really aren't meant to be used outside thePool
, it makes the most sense.These are the obvious ones - stack and queue. I don't think they really warrant much explanation. The circular buffer is a little more complicated:
I could have picked a number of different approaches, but the bottom line is that resources should be accessed in the same order that they were created, which means that we have to maintain references to them but mark them as "in use" (or not). In the worst-case scenario, only one slot is ever available, and it takes a full iteration of the buffer for every fetch. This is bad if you have hundreds of resources pooled and are acquiring and releasing them several times per second; not really an issue for a pool of 5-10 items, and in the typical case, where resources are lightly used, it only has to advance one or two slots.
Remember, these classes are private inner classes - that is why they don't need a whole lot of error-checking, the pool itself restricts access to them.
Throw in an enumeration and a factory method and we're done with this part:
The next problem to solve is loading strategy. I've defined three types:
The first two should be self-explanatory; the third is sort of a hybrid, it lazy-loads resources but doesn't actually start re-using any resources until the pool is full. This would be a good trade-off if you want the pool to be full (which it sounds like you do) but want to defer the expense of actually creating them until first access (i.e. to improve startup times).
The loading methods really aren't too complicated, now that we have the item-store abstraction:
The
size
andcount
fields above refer to the maximum size of the pool and the total number of resources owned by the pool (but not necessarily available), respectively.AcquireEager
is the simplest, it assumes that an item is already in the store - these items would be preloaded at construction, i.e. in thePreloadItems
method shown last.AcquireLazy
checks to see if there are free items in the pool, and if not, it creates a new one.AcquireLazyExpanding
will create a new resource as long as the pool hasn't reached its target size yet. I've tried to optimize this to minimize locking, and I hope I haven't made any mistakes (I have tested this under multi-threaded conditions, but obviously not exhaustively).You might be wondering why none of these methods bother checking to see whether or not the store has reached the maximum size. I'll get to that in a moment.
Now for the pool itself. Here is the full set of private data, some of which has already been shown:
Answering the question I glossed over in the last paragraph - how to ensure we limit the total number of resources created - it turns out that the .NET already has a perfectly good tool for that, it's called Semaphore and it's designed specifically to allow a fixed number of threads access to a resource (in this case the "resource" is the inner item store). Since we're not implementing a full-on producer/consumer queue, this is perfectly adequate for our needs.
The constructor looks like this:
Should be no surprises here. Only thing to note is the special-casing for eager loading, using the
PreloadItems
method already shown earlier.Since almost everything's been cleanly abstracted away by now, the actual
Acquire
andRelease
methods are really very straightforward:As explained earlier, we're using the
Semaphore
to control concurrency instead of religiously checking the status of the item store. As long as acquired items are correctly released, there's nothing to worry about.Last but not least, there's cleanup:
The purpose of that
IsDisposed
property will become clear in a moment. All the mainDispose
method really does is dispose the actual pooled items if they implementIDisposable
.Now you can basically use this as-is, with a
try-finally
block, but I'm not fond of that syntax, because if you start passing around pooled resources between classes and methods then it's going to get very confusing. It's possible that the main class that uses a resource doesn't even have a reference to the pool. It really becomes quite messy, so a better approach is to create a "smart" pooled object.Let's say we start with the following simple interface/class:
Here's our pretend disposable
Foo
resource which implementsIFoo
and has some boilerplate code for generating unique identities. What we do is to create another special, pooled object:This just proxies all of the "real" methods to its inner
IFoo
(we could do this with a Dynamic Proxy library like Castle, but I won't get into that). It also maintains a reference to thePool
that creates it, so that when weDispose
this object, it automatically releases itself back to the pool. Except when the pool has already been disposed - this means we are in "cleanup" mode and in this case it actually cleans up the internal resource instead.Using the approach above, we get to write code like this:
This is a very good thing to be able to do. It means that the code which uses the
IFoo
(as opposed to the code which creates it) does not actually need to be aware of the pool. You can even injectIFoo
objects using your favourite DI library and thePool<T>
as the provider/factory.I've put the complete code on PasteBin for your copy-and-pasting enjoyment. There's also a short test program you can use to play around with different loading/access modes and multithreaded conditions, to satisfy yourself that it's thread-safe and not buggy.
Let me know if you have any questions or concerns about any of this.