Skip to content

Commit

Permalink
v4.1.0 (#8)
Browse files Browse the repository at this point in the history
* 4.1.0
  • Loading branch information
aembke authored Sep 27, 2021
1 parent 0e88b3f commit bcb7a9e
Show file tree
Hide file tree
Showing 61 changed files with 1,469 additions and 708 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
language: rust
services:
- docker
rust:
- stable
- beta
Expand Down Expand Up @@ -31,7 +33,9 @@ branches:
env:
- REDIS_VERSION=6.2.2
before_script:
- ./tests/scripts/install_redis_centralized.sh && ./tests/scripts/install_redis_clustered.sh
- ./tests/scripts/install_redis_centralized.sh
- ./tests/scripts/install_redis_clustered.sh
- ./tests/scripts/docker-install-redis-sentinel.sh
jobs:
allow_failures:
- rust: nightly
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.1.0

* Support Redis Sentinel
* Sentinel tests
* Move metrics behind compiler flag

## 4.0.0

Expand Down
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fred"
version = "4.0.0"
version = "4.1.0"
authors = ["Alec Embke <[email protected]>"]
edition = "2018"
description = "An async Redis client for Rust built on Futures and Tokio."
Expand All @@ -13,9 +13,10 @@ license = "MIT"

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
tokio = { version = "1.10", features = ["full"] }
tokio = { version = "1.11", features = ["full"] }
bytes = "1.0"
futures = "0.3"
parking_lot = "0.11"
Expand Down Expand Up @@ -60,7 +61,8 @@ name = "monitor"
required-features = ["monitor"]

[features]
default = ["ignore-auth-error", "pool-prefer-active", "enable-tls"]
default = ["ignore-auth-error", "pool-prefer-active", "enable-tls", "metrics"]
metrics = []
ignore-auth-error = []
enable-tls = ["native-tls", "tokio-native-tls"]
vendored-tls = ["enable-tls", "native-tls/vendored"]
Expand All @@ -74,5 +76,7 @@ blocking-encoding = ["tokio/rt-multi-thread"]
network-logs = []
custom-reconnect-errors = []
monitor = ["nom"]
# Testing Features
sentinel-tests = []
# a testing feature to randomly stop, restart, and rebalance the cluster while tests are running
chaos-monkey = ["custom-reconnect-errors"]
chaos-monkey = ["custom-reconnect-errors"]
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ async fn main() -> Result<(), RedisError> {

// connect to the server, returning a handle to the task that drives the connection
let _ = client.connect(Some(policy));
// wait for the client to connect
let _ = client.wait_for_connect().await?;
let _ = client.flushall(false).await?;

// convert responses to many common Rust types
let foo: Option<String> = client.get("foo").await?;
println!("Foo: {:?}", foo);
assert_eq!(foo, None);
assert!(foo.is_none());

let _: () = client.set("foo", "bar", None, None, false).await?;
// or use turbofish to declare types
// or use turbofish to declare response types
println!("Foo: {:?}", client.get<String, _>("foo").await?);

// or use a lower level interface for responses to defer parsing, etc
let foo: RedisValue = client.get("foo").await?;
assert_eq!(foo.as_str().unwrap(), "bar");
Expand All @@ -55,17 +55,17 @@ cargo add fred
## Features

* Flexible and generic client interfaces.
* Supports clustered and centralized Redis deployments.
* Supports clustered, centralized, and sentinel Redis deployments.
* Optional built-in reconnection logic with multiple backoff policies.
* Publish-Subscribe and keyspace events interfaces.
* Supports transactions.
* Supports Lua scripts.
* Supports streaming results from the `MONITOR` command.
* Supports custom commands provided by third party modules.
* Supports connections over TLS.
* Supports TLS connections.
* Handles cluster rebalancing operations without downtime or errors.
* Supports various scanning functions.
* Automatically [pipeline](https://redis.io/topics/pipelining) requests, with an option for callers to disable this.
* Supports streaming interfaces for scanning functions.
* Options to automatically [pipeline](https://redis.io/topics/pipelining) requests when possible.
* Automatically retry requests under bad network conditions.
* Support for configuring global settings that can affect performance under different network conditions. Callers can configure backpressure settings, when and how the underlying socket is flushed, and how many times requests are attempted.
* Built-in tracking for network latency and payload size metrics.
Expand All @@ -74,7 +74,7 @@ cargo add fred

## Tracing

This crate supports tracing via the [tracing](https://github.com/tokio-rs/tracing) crate. See the [tracing docs](./src/trace/README.md) for more information.
This crate supports tracing via the [tracing crate](https://github.com/tokio-rs/tracing). See the [tracing info](./src/trace/README.md) for more information.

This feature is disabled by default, but callers can opt-in via the `full-tracing` or `partial-tracing` features.

Expand All @@ -91,6 +91,7 @@ When a client is initialized it will generate a unique client name with a prefix
| enable-tls | x | Enable TLS support. This requires OpenSSL (or equivalent) dependencies. |
| vendored-tls | | Enable TLS support, using vendored OpenSSL (or equivalent) dependencies, if possible. |
| ignore-auth-error | x | Ignore auth errors that occur when a password is supplied but not required. |
| metrics | x | Enable the metrics interface to track overall latency, network latency, and request/response sizes. |
| reconnect-on-auth-error | | A NOAUTH error is treated the same as a general connection failure and the client will reconnect based on the reconnection policy. |
| index-map | | Use [IndexMap](https://docs.rs/indexmap/*/indexmap/) instead of [HashMap](https://doc.rust-lang.org/std/collections/struct.HashMap.html) as the backing store for Redis Map types. This is useful for testing and may also be useful for callers. |
| pool-prefer-active | x | Prefer connected clients over clients in a disconnected state when using the `RedisPool` interface. |
Expand Down Expand Up @@ -128,6 +129,14 @@ If callers are using ACLs and Redis version >=6.x they can configure the client

If this is not possible callers need to ensure that the default user can run the two commands above. Additionally, it is recommended to move any calls to the `auth` command inside the `on_reconnect` block.

## Redis Sentinel

To use the [Redis Sentinel](https://redis.io/topics/sentinel) interface callers should provide a `ServerConfig::Sentinel` variant when creating the client's `RedisConfig`. This should contain the host/port tuples for the known sentinel nodes when first [creating the client](https://redis.io/topics/sentinel-clients).

The client will automatically update these values in place as sentinel nodes change whenever connections to the primary Redis server close. Callers can inspect these changes with the `client_config` function on any `RedisClient` that uses the sentinel interface.

Note: Sentinel connections will use the same authentication and TLS configuration options as the connections to the Redis servers.

## Customizing Error Handling

The `custom-reconnect-errors` feature enables an interface on the [globals](src/globals.rs) to customize the list of errors that should automatically trigger reconnection logic (if configured).
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ Examples
* [Dynamic Pool](./dynamic_pool.rs) - Use a redis pool that can be scaled up and down at runtime.
* [Resilience](./resilience.rs) - Configure the client to work under bad network conditions or against unreliable servers.
* [Monitor](./monitor.rs) - Process a `MONITOR` stream.
* [Sentinel](./sentinel.rs) - Connect using a sentinel deployment.

Or check out the [tests](../tests/integration) for more examples.
33 changes: 33 additions & 0 deletions examples/sentinel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use fred::prelude::*;

#[tokio::main]
async fn main() -> Result<(), RedisError> {
let config = RedisConfig {
server: ServerConfig::Sentinel {
// the name of the service, as configured in the sentinel configuration
service_name: "my-service-name".into(),
// the known host/port tuples for the sentinel nodes
// the client will automatically update these if sentinels are added or removed
hosts: vec![
("localhost".into(), 26379),
("localhost".into(), 26380),
("localhost".into(), 26381),
],
},
// sentinels should use the same TLS and authentication settings as the Redis servers
..Default::default()
};

let client = RedisClient::new(config);
let _ = client.connect(Some(policy));
let _ = client.wait_for_connect().await?;

// add a sentinel node...

// force update the sentinel node list if needed
let _ = client.update_sentinel_nodes().await?;
println!("New sentinel nodes: {:?}", client.client_config().server.hosts());

let _ = client.quit().await?;
Ok(())
}
39 changes: 28 additions & 11 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::commands;
use crate::error::{RedisError, RedisErrorKind};
use crate::inner::{MultiPolicy, RedisClientInner};
use crate::metrics::*;
use crate::modules::inner::{MultiPolicy, RedisClientInner};
use crate::modules::response::RedisResponse;
use crate::multiplexer::commands as multiplexer_commands;
use crate::multiplexer::utils as multiplexer_utils;
use crate::protocol::types::RedisCommand;
use crate::response::RedisResponse;
use crate::types::*;
use crate::utils;
use futures::Stream;
Expand All @@ -19,6 +18,9 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
use tokio::time::interval as tokio_interval;
use tokio_stream::wrappers::UnboundedReceiverStream;

#[cfg(feature = "metrics")]
use crate::modules::metrics::Stats;

#[doc(hidden)]
pub type CommandSender = UnboundedSender<RedisCommand>;

Expand Down Expand Up @@ -260,45 +262,53 @@ impl RedisClient {
///
/// This metric reflects the total latency experienced by callers, including time spent waiting in memory to be written and network latency.
/// Features such as automatic reconnect, `reconnect-on-auth-error`, and frame serialization time can all affect these values.
pub fn read_latency_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn read_latency_metrics(&self) -> Stats {
self.inner.latency_stats.read().read_metrics()
}

/// Read and consume latency metrics, resetting their values afterwards.
pub fn take_latency_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn take_latency_metrics(&self) -> Stats {
self.inner.latency_stats.write().take_metrics()
}

/// Read network latency metrics across all commands.
///
/// This metric only reflects time spent waiting on a response. It will factor in reconnect time if a response doesn't arrive due to a connection
/// closing, but it does not factor in the time a command spends waiting to be written, serialization time, backpressure, etc.
pub fn read_network_latency_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn read_network_latency_metrics(&self) -> Stats {
self.inner.network_latency_stats.read().read_metrics()
}

/// Read and consume network latency metrics, resetting their values afterwards.
pub fn take_network_latency_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn take_network_latency_metrics(&self) -> Stats {
self.inner.network_latency_stats.write().take_metrics()
}

/// Read request payload size metrics across all commands.
pub fn read_req_size_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn read_req_size_metrics(&self) -> Stats {
self.inner.req_size_stats.read().read_metrics()
}

/// Read and consume request payload size metrics, resetting their values afterwards.
pub fn take_req_size_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn take_req_size_metrics(&self) -> Stats {
self.inner.req_size_stats.write().take_metrics()
}

/// Read response payload size metrics across all commands.
pub fn read_res_size_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn read_res_size_metrics(&self) -> Stats {
self.inner.res_size_stats.read().read_metrics()
}

/// Read and consume response payload size metrics, resetting their values afterwards.
pub fn take_res_size_metrics(&self) -> DistributionStats {
#[cfg(feature = "metrics")]
pub fn take_res_size_metrics(&self) -> Stats {
self.inner.res_size_stats.write().take_metrics()
}

Expand Down Expand Up @@ -729,6 +739,13 @@ impl RedisClient {
))
}

/// Update the client's sentinel nodes list if using the sentinel interface.
///
/// The client will automatically update this when connections to the primary server close.
pub async fn update_sentinel_nodes(&self) -> Result<(), RedisError> {
utils::update_sentinel_nodes(&self.inner).await
}

/// The command returns information and statistics about the current client connection in a mostly human readable format.
///
/// <https://redis.io/commands/client-info>
Expand Down
2 changes: 1 addition & 1 deletion src/commands/acl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::error::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/cluster.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::types::*;
use std::sync::Arc;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/geo.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::error::RedisError;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/hashes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/hyperloglog.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/keys.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::error::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/lists.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/lua.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;
use crate::client::util::sha1_hash;
use crate::error::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/memory.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::error::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::RedisError;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::RedisCommandKind;
use crate::protocol::utils as protocol_utils;
use crate::types::RedisValue;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/pubsub.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::error::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
use crate::types::*;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/scan.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use crate::error::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::protocol::types::*;
use crate::types::*;
use crate::utils;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;
use crate::client::RedisClient;
use crate::error::*;
use crate::inner::RedisClientInner;
use crate::modules::inner::RedisClientInner;
use crate::multiplexer::utils as multiplexer_utils;
use crate::protocol::types::*;
use crate::protocol::utils as protocol_utils;
Expand Down
Loading

0 comments on commit bcb7a9e

Please sign in to comment.