Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6.2.2 #135

Merged
merged 2 commits into from May 8, 2023
Merged

6.2.2 #135

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
@@ -1,6 +1,11 @@
## 6.2.2

* Fix cluster replica discovery with Elasticache
* Fix cluster replica `READONLY` usage

## 6.2.1

* Fix cluster failover with paused nodes.
* Fix cluster failover with paused nodes

## 6.2.0

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "fred"
version = "6.2.1"
version = "6.2.2"
authors = ["Alec Embke <aembke@gmail.com>"]
edition = "2021"
description = "An async Redis client built on Tokio."
Expand Down
40 changes: 40 additions & 0 deletions src/clients/node.rs
Expand Up @@ -41,6 +41,46 @@ use crate::{
/// A struct for interacting with individual nodes in a cluster.
///
/// See [with_cluster_node](crate::clients::RedisClient::with_cluster_node) for more information.
///
/// ```
/// # use fred::prelude::*;
/// async fn example(client: &RedisClient) -> Result<(), RedisError> {
/// // discover servers via the `RedisConfig` or active connections
/// let connections = client.active_connections().await?;
///
/// // ping each node in the cluster individually
/// for server in connections.into_iter() {
/// let _: () = client.with_cluster_node(server).ping().await?;
/// }
///
/// // or use the cached cluster routing table to discover servers
/// let servers = client
/// .cached_cluster_state()
/// .expect("Failed to read cached cluster state")
/// .unique_primary_nodes();
/// for server in servers {
/// // verify the server address with `CLIENT INFO`
/// let server_addr = client
/// .with_cluster_node(&server)
/// .client_info::<String>()
/// .await?
/// .split(" ")
/// .find_map(|s| {
/// let parts: Vec<&str> = s.split("=").collect();
/// if parts[0] == "laddr" {
/// Some(parts[1].to_owned())
/// } else {
/// None
/// }
/// })
/// .expect("Failed to read or parse client info.");
///
/// assert_eq!(server_addr, server.to_string());
/// }
///
/// Ok(())
/// }
/// ```
#[derive(Clone)]
pub struct Node {
inner: Arc<RedisClientInner>,
Expand Down
43 changes: 1 addition & 42 deletions src/clients/redis.rs
Expand Up @@ -242,53 +242,12 @@ impl RedisClient {
Pipeline::from(self.clone())
}

/// Send subsequent commands to the provided cluster node.
/// Send commands to the provided cluster node.
///
/// The caller will receive a `RedisErrorKind::Cluster` error if the provided server does not exist.
///
/// The client will still automatically follow `MOVED` errors via this interface. Callers may not notice this, but
/// incorrect server arguments here could result in unnecessary calls to refresh the cached cluster routing table.
///
/// ```
/// # use fred::prelude::*;
///
/// async fn example(client: &RedisClient) -> Result<(), RedisError> {
/// // discover servers via the `RedisConfig` or active connections
/// let connections = client.active_connections().await?;
///
/// // ping each node in the cluster individually
/// for server in connections.into_iter() {
/// let _: () = client.with_cluster_node(server).ping().await?;
/// }
///
/// // or use the cached cluster routing table to discover servers
/// let servers = client
/// .cached_cluster_state()
/// .expect("Failed to read cached cluster state")
/// .unique_primary_nodes();
/// for server in servers {
/// // verify the server address with `CLIENT INFO`
/// let server_addr = client
/// .with_cluster_node(&server)
/// .client_info::<String>()
/// .await?
/// .split(" ")
/// .find_map(|s| {
/// let parts: Vec<&str> = s.split("=").collect();
/// if parts[0] == "laddr" {
/// Some(parts[1].to_owned())
/// } else {
/// None
/// }
/// })
/// .expect("Failed to read or parse client info.");
///
/// assert_eq!(server_addr, server.to_string());
/// }
///
/// Ok(())
/// }
/// ```
pub fn with_cluster_node<S>(&self, server: S) -> Node
where
S: Into<Server>,
Expand Down
40 changes: 0 additions & 40 deletions src/clients/replica.rs
Expand Up @@ -35,46 +35,6 @@ use tokio::sync::oneshot::channel as oneshot_channel;
/// or when any connection closes.
///
/// [Redis replication is asynchronous](https://redis.io/docs/management/replication/).
// ### Cluster Replication
//
// In a clustered deployment replicas may redirect callers back to primary nodes, even with read-only commands,
// depending on the server configuration. The client will automatically follow these redirections, but callers should
// be aware of this behavior for monitoring or tracing purposes.
//
// #### Example
//
// ```bash
// // connect to a primary node, print cluster and replica info, and `GET bar`
// foo@d85c70fd4fc0:/project$ redis-cli -h 172.21.0.5 -p 30001
// 172.21.0.5:30001> cluster nodes
// 60ca8d301ef624956e847e6e6ecc865a36513bbe 172.21.0.3:30001@40001 slave f837e4056f564ab7fd69c24264279a1bd81d6420 0 1674165394000 3 connected
// ddc30573f0c7ee1f79d7f263e2f83d7b83ad0ba0 172.21.0.8:30001@40001 slave 101b2a992c6c909d807d4c5fbd149bcc28e63ef8 0 1674165396000 2 connected
// 101b2a992c6c909d807d4c5fbd149bcc28e63ef8 172.21.0.2:30001@40001 master - 0 1674165395807 2 connected 5461-10922
// 38a7f9d3e440a37adf42f2ceddd9ad52bfb4186e 172.21.0.7:30001@40001 slave bd48cbd28cd927a284bab4424bd41b077a25acb6 0 1674165396810 1 connected
// f837e4056f564ab7fd69c24264279a1bd81d6420 172.21.0.4:30001@40001 master - 0 1674165395000 3 connected 10923-16383
// bd48cbd28cd927a284bab4424bd41b077a25acb6 172.21.0.5:30001@40001 myself,master - 0 1674165393000 1 connected 0-5460
// 172.21.0.5:30001> info replication
// # Replication
// role:master
// connected_slaves:1
// slave0:ip=172.21.0.7,port=30001,state=online,offset=183696,lag=0
// [truncated]
// 172.21.0.5:30001> get bar
// "2"
//
// // connect to the associated replica and `GET bar`
// foo@d85c70fd4fc0:/project$ redis-cli -h 172.21.0.7 -p 30001
// 172.21.0.7:30001> role
// 1) "slave"
// 2) "172.21.0.5"
// 3) (integer) 30001
// 4) "connected"
// 5) (integer) 185390
// 172.21.0.7:30001> get bar
// (error) MOVED 5061 172.21.0.5:30001
// ```
//
// **This can result in unexpected latency or errors depending on the client configuration.**
#[derive(Clone)]
#[cfg_attr(docsrs, doc(cfg(feature = "replicas")))]
pub struct Replicas {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/mocks.rs
Expand Up @@ -223,7 +223,7 @@ impl Mocks for SimpleMap {
///
/// ```rust
/// #[tokio::test]
/// async fn should_use_echo_mock() {
/// async fn should_use_buffer_mock() {
/// let buffer = Arc::new(Buffer::new());
/// let config = RedisConfig {
/// mocks: buffer.clone(),
Expand Down