Skip to content
  • Lukasa's avatar
    Implement a simple Redis Connection Pool. · 5dbd716a
    Lukasa authored and Nathan Harris's avatar Nathan Harris committed
    Motivation:
    
    Users of Redis will frequently want to be able to run queries in
    parallel, while bounding the number of connections they use. They will
    also often want to be able to reuse connections, without having to
    arrange to manage those connections themselves. These are jobs usually
    done by a Connection Pool.
    
    This new connection pool will conform to `RedisClient` so a pool of clients and a single connection are interchangeable.
    
    Connection Pools come in a wide range of shapes and sizes. In NIO
    applications and frameworks, there are a number of questions that have
    to be answered by any pool implementation:
    
    1. Is the pool safe to share across EventLoops: that is, is its
       interface thread-safe?
    2. Is the pool _tied_ to an EventLoop: that is, can the pool return
       connections that belong on lots of event loops, or just one?
    3. If the pool is not tied to an EventLoop, is it possible to influence
       its choice about what event loop it uses for a given connection?
    
    Question 1 is straightforward: it is almost always a trivial win to
    ensure that the public interface to a connection pool is thread-safe.
    NIO makes it possible to do this fairly cheaply in the case when the
    pool is only used on a single loop.
    
    Question 2 is a lot harder. Pools that are not tied to a specific
    EventLoop have two advantages. The first is that it is easier to bound
    maximum concurrency by simply configuring the pool, instead of needing
    to do math on the number of pools and the number of event loops. The
    second is that non-tied pools can arrange to keep busy applications
    close to this maximum concurrency regardless of how the application
    spreads its load across loops.
    
    However, pools that are tied to a specific EventLoop have advantages
    too. The first is one of implementation simplicity. As they always serve
    connections on a single EventLoop, they can arrange to have all of their
    state on that event loop too. This avoids the need to acquire locks on
    that loop, making internal state management easier and more obviously
    correct without having to worry about how long locks are held for.
    
    The second advantage is that they can be used for latency sensitive
    use-cases without needing to go to the work of (3). In cases where
    latency is very important, it can be valuable to ensure that any Channel
    that needs a connection can get one on the same event loop as itself.
    This avoids the need to thread-hop in order to communicate between the
    pooled connection and the user connection, reducing the latency of
    operations.
    
    Given the simplicity and latency benefits (which we deem particularly
    important for Redis use-cases), we concluded that a good initial
    implementation will be a pool that has a thread-safe interface, but is
    tied to a single EventLoop. This allows a compact, easy-to-verify
    implementation of the pool with great low-latency performance and simple
    implementation logic, that can still be accessed from any EventLoop in
    cases when latency is not a concern.
    
    Modifications:
    
    - Add new internal `ConnectionPool` object
    - Add new `RedisConnectionPool` object
    - Add new `RedisConnectionPoolError` type
    - Add tests for new types
    
    Results:
    
    Users will have access to a pooled Redis client.
    5dbd716a