Configuration

Stormpot has many configuration options, but only one mandatory setting: You have to specify what Allocator or Reallocator implementation to use. This is enforced by the Pool#from, Pool#fromThreaded, and Pool#fromInline methods, which all take the Allocator (or Reallocator) as an argument. The rest of the configuration options are optional, though there are a couple you most likely want to tweak.

Pool Modes

The pool can operate in three different modes, which determine how objects are allocated:

  1. In the default or threaded mode, the objects in the pool are allocated by a background allocation thread. The benefit of this is that the pool has a more predictable response time. This also allows the pool to check object expiration in the background. The pool can also automatically heal allocation failures, so fewer of these exceptions bubble out through the claim methods. The drawback is that every pool in this mode will have a dedicated thread allocated for it. If you plan on having many pool instances in your application, this might be costly. This mode is obtained by creating a pool with the Pool#from or Pool#fromThreaded methods.

  2. The inline mode omits the background thread, and instead allocates and deallocates objects as part of - or inline with - the claim method calls. There is no background thread in this mode, and thus all features that depend on a background thread, like automatic allocation failure healing, are unavailable. The benefit is that these pools take up fewer memory and CPU resources, due to the lack of the background thread. This mode is obtained by creating a pool with the Pool#fromInline.

  3. In the direct mode, the objects in the pool are pre-allocated before the pool is created. This mode is even more restricted in its configuration than the inline mode. There is no background thread, but there is also no need for one. The objects in a direct pool never expire, and explicitly expiring them has no effect. The objects are also never deallocated by the pool, because a direct pool has no allocator associated with it. This also means that a direct pool cannot change its size; the setTargetSize method will throw an exception. This mode is obtained by creating a pool with the Pool#of(…​) method.

Pool Size

You configure the size of the pool with the setSize method. This configuration determines the maximum number of objects the pool should keep in circulation at any given time. The default size is 10 objects. Note that this is a maximum. The pool will never have more than this many objects allocated at any given time.

As it happens, the current implementations will allocate objects until the pool is full, and then stop until objects expire and needs reallocating. This effectively means that there’s no such thing as a low-water-mark in Stormpot. It also means that, if you’ve done your capacity planning right, you will have fairly predictable latencies in claiming objects from the pool, since the given size is exactly the number of objects the pool will contain at any time. On the other hand, it also means that if an object has expired, it will have to be deallocated before a replacement can be allocated.

It also means that you can’t mindlessly set this to some arbitrary high value and consider it done. The pool will really allocate this many objects, expecting them to be in demand. The upside to this is that if your application develops a fault where it claims objects uncontrollably, it will be prevented from allocating more objects than the system can handle. For instance, if the pooled objects represent connections to a database, that database will be sheltered from such a rogue application.

It is possible to configure the pool size to be zero. This means there will be no objects in the pool, and it will act as if it is depleted. The Pool#setTargetSize method can then be used to change the size of the pool later.

Object Expiration

Prior to claiming an object, Stormpot always checks if the object in question has expired. What it means for an object to be "expired" depends on what objects we are talking about, what they are used for and what it means to pool them. For instance, you might be pooling network connections that are closed by a firewall if they have been idle for too long, in which case it makes sense to have them reconnect before that happens.

It is the Expiration policy that decides whether an object has expired, and it is configured with setExpiration. The default expiration policy expires objects after they’ve been circulating for somewhere between 8 and 10 minutes. This is pretty handy, because it spreads the expirations out over time. This means that the objects in the pool don’t all expire at the same time.

If all objects expire at the same time, it could temporarily leave the pool completely empty, until all the expired objects have been reallocated. Such periods of "object drought" can lead to long stalls, aka. latencies in claiming objects from the pool, as the demand for objects is suddenly much higher than the availability.

It might also be the case that there is no reason for objects to expire. In that case, a custom Expiration policy can be configured, that simply always returns false from its hasExpired method.

Custom Object Expiration

You can do more advanced stuff with object expiration, though, than just deciding how long objects are allowed to circulate. You can implement your own policy by implementing the Expiration interface, or by using its constructor and combinator methods. To implement the Expiration interface yourself, you will have to return true from the hasExpired method, whenever you deem the given object needs to be reallocated. The hasExpired method gets a SlotInfo object, that represents the Poolable being validated, and its Slot.

Note
Your Expiration implementation needs to be thread-safe, since it will be used concurrently by many threads, to validate whatever objects they happen to be trying to claim at any given point in time. However, only a single thread will be validating any given SlotInfo, and by extension, any given Poolable, at a time. This means that access to the associated Poolable instance is thread-safe.

The SlotInfo has a number of useful methods for implementing Expiration policies:

  • For instance, the getAgeMillis is used by the Expiration#after() expirations, to determine if an object has expired.

  • The setStamp and getStamp methods can be used to store any state as a single long, that you might need in your expiration implementation. The stamp of a SlotInfo is always zero for newly allocated objects. Its value means whatever the expiration policy wants it to mean. For instance, the after() expiration, and every() combinator, uses the stamp to store the deadline for when an object will expire. This way, the number and frequency of claims does not influence the distribution of when objects expire.

  • The getClaimCount simply returns the number of times an object has been claimed.

Note
The hasExpired method can be called several times during every call to claim. It is therefor very critical to the performance of the pool, and you should take great care to make sure it is fast. In fact, it is so performance sensitive, that reading the time from the system clock, e.g. with System#nanoTime, can be a significant drag on the performance of the pool.

Background Expiration Checking

Stormpot pools in the default or threaded modes will start a background thread to take care of all the allocation and deallocation that needs to happen. This background thread can also perform expiration checks on the objects in the pool, when it has nothing better to do.

Background expiration checks are on by default, because they are very useful for applications that may experience periods of low activity. Checking for expired objects in the background, ensures objects will be steadily reallocated, even if they don’t get checked for expiration through the natural traffic to the pool. The background expiration checks will then make sure all the objects in the pool are fresh for when the traffic picks up again. The application would otherwise have found itself with a pool full of expired objects, causing a reallocation storm, and high latencies to claim objects.

However, it is possible to turn this off, since the time it takes to do the expiration checks might interfere with the pools ability to keep up with the demand for allocations and deallocations. The pool does not know how expensive those expiration checks are.

Allocator vs Reallocator

An Allocator does two things: It allocates and deallocates objects. A freshly allocated object is assumed to have a unique object identity among its peers, and once deallocated, is assumed to never appear again.

Since objects in the pool tend to live for a relatively long time, but crucially, not forever, it means that the normal operation of the pool will most likely lead to a slow accretion of garbage in the old generation of the heap. Such garbage can lead to fragmentation and, eventually, an expensive old-generation garbage collection.

The Reallocator interface gives integrators an opportunity to counter this. The Reallocator has a reallocate method, in addition to what it inherits from the Allocator interface. The reallocate method is a combined deallocate and allocate method. Since it’s combined, it can make decisions about whether to take the object that it’s supposed to deallocate, and reuse it for the subsequent allocation. This way, a Reallocator has the potential to produce old-gen garbage at a much slower rate than an Allocator would.

Care must be taken with implementing the Reallocator, though, since the returned object is assumed to be as fresh as any that would have been returned from the allocate method.

MetricsRecorder

Stormpot has the ability to expose metrics and management handles through JMX. This is done by registering the pool with an MBeanServer, or by integrating with a custom metrics system. The Pool also exposes a getManagedPool method that returns a ManagedPool instance for this purpose. You can read more about how to expose Stormpot through JMX in the JMX Guide. You can also read about how to integrate a metrics system on the Metrics page.

There is no MetricsRecorder configured by default, and Stormpot does not come with any integrations built in. Integrators need to build their own. Once you have your MetricsRecorder implementation, you just set an instance of it on your PoolBuilder object with the setMetricsRecorder method.

ThreadFactory

Stormpot pools in the default or threaded mode create a background thread per pool instance. This thread is in charge of allocating and deallocating objects for the pool. This means that the threads that access the pool to claim objects, don’t have to pay the overhead of allocating any of the objects themselves. The latency for claiming objects is thereby reduced, and made more predictable.

However, not all platforms and environments let user code create threads willy-nilly. There might be a SecurityManager that has to approve every thread that is created. There might be particular contexts, that every new thread needs to be associated with, or bound to, for security purposes. It might be that you can only create threads if they get associated with a particular ThreadGroup.

Whatever the case, if the background thread needs to be created in a particular way for your particular environment, then you can provide a ThreadFactory implementation via the setThreadFactory method, and make sure the background thread is created in a way that observes the law of the land in your particular circumstances.

The default ThreadFactory is based on the Executors#defaultThreadFactory, but also assigns the thread a name, that makes it recognisable as a Stormpot background thread.

The thread factory setting is not used by pools that operate in the inline mode.

Precise Leak Detection

Stormpot has precise leak detection enabled by default (except in the direct pool mode) because the CPU overhead is very low. There is, however, a bit of memory overhead. Therefor, it may make sense to disable this in use cases where memory is very constrained, and/or the pool contains a very large number of objects. If you pool upwards a hundred thousand objects or more, you might want to disable it for performance reasons.

The precise leak detection feature lets Stormpot keep track of when objects that were meant to circulate in the pool, suddenly leak out and never come back. The number of leaks detected is reported via the getLeakedObjectsCount method of the ManagedPool interface.

This feature uses the garbage collector to determine whether an object has any chance of ever returning to the pool. This is why it’s called precise leak detection: It never reports any false positives. That is, it never counts an object as leaked, unless there is a 100% certainty, that it will never, ever return to the pool. On the other hand, it might not detect all leaks. For instance, if you claim an object, and then put it in a static final variable and never release it back, then that object will have a strong reference pointing to it, for the lifetime of the class that the static final field. In this case, there is still a chance that the object may be returned, but if no code actually does this, then it has effectively leaked. Such a case will not be detected by the precise leak detector.

While precise leak detection is able to detect leaks – and a leak is always a bug in user code – it is not able to prevent the leaks. This means that if a leak has been observed, you know for sure that the shut down process will not terminate. The shut down process won’t finish until all allocated objects has been deallocated; which will never happen because leaked objects will remain logically claimed for perpetuity. In such a case it’s nice that the API for awaiting on the completion of the shut down process mandates a timeout, so there’s no waiting forever.