Stormpot Documentation

repository·main·Indexed 18 days ago

https://github.com/chrisvest/stormpot

A high-performance Java object pooling library designed to recycle expensive-to-create objects with low latency and high throughput. Stormpot features three pool modes (Default/Threaded, Inline, and Direct), precise leak detection, and support for custom expiration policies. It provides a ManagedPool MXBean for JMX monitoring and a MetricsRecorder interface for integration with telemetry frameworks like Micrometer or Dropwizard.

Tokens
7.3K
Snippets
21
Records
42
Agent score
64%

What's inside Stormpot

  1. Advanced Stormpot configuration and optimization

    main

    For deep dives into specific aspects of Stormpot, refer to the following specialized guides:

    • Configuration: A complete reference of all available configuration options.
    • Memory Effects: Detailed information regarding the memory and concurrency implications of using the Stormpot API.
    • Performance Guide: Best practices and techniques for maximizing the performance of your object pools.
    • Trouble Shooting Guide: Documentation on common failure modes, misuses of the pool, and resolution steps.
    • Configuring JMX: Instructions on exposing Stormpot metrics and management APIs via JMX.
    • Integrating Metrics: Guidance on integrating Stormpot with telemetry libraries and metrics frameworks.
  2. What is an object pool vs an object cache?

    main

    Stormpot is an object pool, which is a homogeneous collection of objects. In a pool, it does not matter which specific instance is returned from claim because all objects are considered similar.

    If you need to manage heterogeneous objects that are identified by a unique key, you should use an object cache instead. For object caching, the authors recommend Caffeine.

  3. Allocator vs Reallocator

    main

    When configuring a pool, you must provide an implementation for object lifecycle management.

    • Allocator: Handles allocate and deallocate. It assumes every freshly allocated object has a unique identity and that deallocated objects never reappear.
    • Reallocator: Extends Allocator by adding a reallocate(T oldObject) method. This method combines deallocation and allocation, allowing the implementation to reuse the oldObject for the next allocation. This can significantly reduce old-generation garbage collection pressure by preventing the constant creation of new objects.
  4. Understand Stormpot's memory visibility and happens-before guarantees

    main

    Stormpot provides specific happens-before edges to ensure thread-safety in concurrent programs. To use the library safely, follow recommended practices: do not share claimed objects between threads; instead, allow the pool to manage the concurrency.

    The following memory visibility guarantees are provided:

    • Allocation to Claim: Allocator#allocate happens-before Pool#claim of that object.
    • Claim to Release: A Pool#claim happens-before any subsequent release of that object.
    • Release to Claim: Poolable#release happens-before any subsequent Pool#claim of that object.
    • Release to Deallocation: The release of an object happens-before its deallocation via Allocator#deallocate.
    • Reallocation to Claim: Reallocator#reallocate happens-before any Pool#claim of that object.
    • Release to Reallocation: The release of an object happens-before its reallocation via Reallocator#reallocate.
    • Deallocation to Completion: The deallocation of all objects happens-before Completion#await successfully returns true.
  5. How to resize a Pool

    main

    Stormpot pools are not fixed in size by default. You can dynamically adjust the number of objects in a pool using the setTargetSize(int size) method on the Pool instance.

    • Growing: The pool will work towards the new target size by allocating more objects.
    • Shrinking: The pool will work towards the new target size by deallocating objects as they are released. It cannot force currently claimed objects to be released.

    Note: Changing the size on the PoolBuilder only affects new pools; use setTargetSize on an existing Pool instance to resize it.

  6. How object expiration works in Stormpot

    main

    Stormpot manages object lifecycles using an Expiration strategy. When claim is called, the pool checks if the returned object has expired.

    • Automatic Expiration: The default policy expires objects randomly between 8 to 10 minutes to avoid mass expiration events.
    • Manual Expiration: If an object becomes invalid while in use, you can call expire() on its Slot (or via BasePoolable). The expiration takes effect once the object is released. The object will then be deallocated and not reused.

    Because hasExpired is called frequently during claim, ensure your implementation is highly performant.

  7. Integrate Stormpot with metrics via ManagedPool

    main
    The ManagedPool interface provides built-in counters that can be integrated into telemetry systems as gauges. Because ManagedPool follows MXBean conventions, it can also be used for JMX integration to expose metrics externally. This is the primary way to access standard pool metrics like allocation counts, leaks, and errors.
  8. Optimize custom Expiration implementations

    main

    The Expiration component is performance-critical because the hasExpired method is called at least once for every claim call, and potentially multiple times if objects expire sequentially. To maintain high performance, ensure the expiration check is as lightweight as possible.

    If your expiration check involves an expensive operation (such as running a database validation query), do not perform the full check on every claim. Instead, use strategies to amortize the cost.

  9. Handle interruptions in Pool#claim and Completion#await

    main

    The two blocking methods in Stormpot, Pool#claim and Completion#await, are designed to respond correctly to thread interruptions.

    If a thread is interrupted while calling these methods, or while waiting within them, the methods will throw a java.lang.InterruptedException and the thread's interruption flag will be cleared. Developers should ensure their code handles this exception appropriately.

  10. Choose a Stormpot Pool Mode

    main

    Stormpot offers three modes that determine how objects are allocated and managed. You must choose a mode by using the corresponding factory method on the Pool class:

    1. Default or Threaded Mode (Pool#from or Pool#fromThreaded):
      • Uses a dedicated background thread for allocation and deallocation.
      • Pros: Predictable response times, background expiration checking, and automatic healing of allocation failures.
      • Cons: One dedicated thread per pool instance; may be costly if you have many pools.
    2. Inline Mode (Pool#fromInline):
      • Allocates and deallocates objects inline with claim calls.
      • Pros: Lower memory and CPU footprint (no background thread).
      • Cons: No background thread means no automatic failure healing and no background expiration checks.
    3. Direct Mode (Pool#of(...)):
      • Objects are pre-allocated before the pool is created.
      • Pros: No background thread required.
      • Cons: Most restricted mode. Objects never expire, cannot be explicitly expired, cannot be deallocated, and the pool size is fixed (calling setTargetSize will throw an exception).
    // Threaded mode (default)
    Pool<MyObject> pool = Pool.from(myAllocator);
    
    // Inline mode
    Pool<MyObject> pool = Pool.fromInline(myAllocator);
    
    // Direct mode (pre-allocated)
    Pool<MyObject> pool = Pool.of(myObject1, myObject2);
  11. Implement the Poolable interface

    main

    To manage objects with a Stormpot pool, the objects must implement the Poolable interface. This interface allows the pool to associate an object with a specific Slot for lifecycle management.

    There are two ways to implement this:

    1. Extend BasePoolable: This is the simplest method. You extend BasePoolable and pass a Slot argument through to its constructor. The base class handles the rest of the implementation.
    2. Direct Implementation: Implement Poolable directly. The object must maintain a reference to its Slot instance and pass itself as a parameter to the Slot#release method when it is finished being used.
    // Example of extending BasePoolable
    public class MyPoolable extends BasePoolable {
        public MyPoolable(Slot slot) {
            super(slot);
        }
    }
    
    // Example of direct Poolable implementation
    public class MyOtherPoolable implements Poolable {
        private final Slot slot;
    
        public MyOtherPoolable(Slot slot) {
            this.slot = slot;
        }
    
        @Override
        public void release() {
            slot.release(this);
        }
    }
  12. Get started with Stormpot object pooling

    main

    To use Stormpot, you must provide three components:

    1. A Poolable type: An implementation of the Poolable interface for the objects you want to pool.
    2. An Allocator: An implementation of the Allocator interface responsible for allocating and deallocating your Poolable objects.
    3. A Pool: Created using Pool.from(allocator).build().

    When claiming an object, use a Timeout. If claim returns null, the timeout was reached. Always release the object in a finally block to return it to the pool.

    MyAllocator allocator = new MyAllocator();
    Pool<MyPoolable> pool = Pool.from(allocator).build();
    Timeout timeout = new Timeout(1, TimeUnit.SECONDS);
    
    MyPoolable object = pool.claim(timeout);
    try {
      // Do stuff with 'object'.
      // Note: 'claim' returns 'null' if it times out.
    } finally {
      if (object != null) {
        object.release();
      }
    }