6 Commits

Author SHA1 Message Date
Nathan Harris 42e8d4b127 Allow repeated commands to same connection in pool
Motivation:

Some Redis commands are very connection specific that have impacts on future access that makes it difficult in the current
checkout-use-return cycle that `RedisConnectionPool` uses.

Developers need a way to borrow a specific connection, chain several commands together, and then return the connection to the pool.

Modifications:

- Add: `leaseConnection` method to `RedisConnectionPool` which provides a connection from the pool and returns it after a provided closure's ELF resolves
- Add: `allowSubscriptions` property to `RedisConnection` for controlling the ability to make PubSub subscriptions
- Add: `RedisClientError.pubsubNotAllowed` case for when `RedisConnection.allowSubscriptions` is set to `false` and a subscription was still attempted

Result:

Developers should now have an "escape hatch" with `RedisConnectionPool` to do limited exclusive chains of operations on a specific connection.
2020-10-15 13:39:58 -07:00
Nathan Harris 56f0ab0bc0 Add test case for RedisConnectionPool connectionRetryTimeout 2020-10-06 20:19:26 -07:00
Nathan Harris e7b597fc65 Add support for PubSub
Motivation:

One of the great features of Redis is being able to subscribe and receive messages published to specific channels
as a way of acting as a message queue for processing jobs.

PubSub requires a specific understanding of the connection model that can only be implemented directly in this library.

Modifications:

- Add: `RedisPubSubHandler` to sit in front of `RedisCommandHandler` to manage subscription callbacks and Redis registration
- Add: `publish` and the `pubsub` commands
- Add: `addPubSubHandler` extension to `NIO.Channel`
- Add: Type-safe String wrapper of `RedisChannelName` for PubSub methods
- Add: `pubsubSubscriptionNotFound` error case
- Add: `isSubscribed` property to `RedisConnection`
- Add: `availableConnectionCount` and `leasedConnectionCount` properties to `RedisConnectionPool`
- Add: Metrics for PubSub
- Add: `makeNewPool` factory method to `RedisConnectionPoolIntegrationTestCase`
- Change: `RedisClient` to require methods for PubSub management, as they are intrinsicly tied to the client's connection model
- Change: Parsing of `PING` response for handling special case in PubSub mode
- Rename: `ActiveConnectionGauge` to `RedisMetrics.IncrementalGauge`

Result:

Developers will now be able to use Redis in PubSub mode with both connections and pools.

This resolves #6
2020-09-29 22:23:44 -07:00
Lukasa 5dbd716acf Implement a simple Redis Connection Pool.
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.
2020-06-03 16:43:10 +00:00
Nathan Harris a09c434612 Change test utils RedisConnection process to be less opinionated.
Motivation:

After working with RedisKit with RediStackTestUtils as a dependency, it was realized how opinionated the module is in how RedisConnections can be created in test environments.

Modifications:

Require more information, with reasonable defaults for `RedisConnection.init()`. Provide subclass hooks for `RedisIntegrationTestCase` for implementors to make decisions for themselves at how to connect to Redis.

Result:

Users should have more freedom in how they connect to Redis in their units tests.
2019-07-28 00:21:07 -07:00
Nathan Harris 7e7e354697 61 -- Rebrand from RedisNIO to RediStack 2019-07-08 19:45:33 -07:00