mod common;
use glide::commands::pubsub::PubSubCommands;
use glide::{
AsyncCommands, CustomCommand, GlideClusterClient, GlideClusterClientConfiguration,
PipelineOptions, PubSubMessageKind, Route, ScriptingCommands, SortedSetCommands, pipe,
};
use std::time::Duration;
macro_rules! cluster_client {
($cluster:expr) => {
match $cluster.client().await {
Some(c) => c,
None => {
eprintln!("SKIP: cluster client connect failed");
return;
}
}
};
}
timed_tokio_test!(
async fn cluster_exec_with_options() {
let cluster = match common::ClusterHarness::start() {
Some(c) => c,
None => {
eprintln!("SKIP: cluster harness unavailable");
return;
}
};
let c = cluster_client!(cluster);
let k = common::tkey("cbo", "k");
let mut pipeline = pipe();
pipeline.set(&k, "1").incr(&k, 1i64).get(&k);
let opts = PipelineOptions::new()
.with_timeout(Duration::from_secs(5))
.with_retry_server_error(true);
let results = c
.execute_pipeline(&pipeline, true, None, &opts)
.await
.unwrap();
assert_eq!(results.len(), 3);
assert_eq!(glide::value::to_i64(results[1].clone()).unwrap(), 2);
assert_eq!(glide::value::to_string(results[2].clone()).unwrap(), "2");
let k2 = common::tkey("cbo", "tx");
let mut tx = pipe();
tx.atomic().set(&k2, "5").incr(&k2, 1i64);
let res2 = c
.execute_pipeline(&tx, true, None, &PipelineOptions::new())
.await
.unwrap();
assert_eq!(glide::value::to_i64(res2[1].clone()).unwrap(), 6);
}
);
timed_tokio_test!(
async fn cluster_fcall_route() {
let cluster = match common::ClusterHarness::start() {
Some(c) => c,
None => {
eprintln!("SKIP: cluster harness unavailable");
return;
}
};
let c = cluster_client!(cluster);
let lib = "#!lua name=glideclib\n\
redis.register_function{function_name='gc_echo', \
callback=function(keys, args) return args[1] end, flags={'no-writes'}}";
if let Err(e) = c
.custom_command_with_route(&["FUNCTION", "LOAD", "REPLACE", lib], Route::AllPrimaries)
.await
{
eprintln!("SKIP: FUNCTION unsupported: {e:?}");
return;
}
let r = c
.fcall_route("gc_echo", &[] as &[&str], &["hi"], Route::RandomNode)
.await
.unwrap();
assert_eq!(glide::value::to_string(r).unwrap(), "hi");
let r = c
.fcall_ro_route("gc_echo", &[] as &[&str], &["ro"], Route::RandomNode)
.await
.unwrap();
assert_eq!(glide::value::to_string(r).unwrap(), "ro");
let all = c
.fcall_route("gc_echo", &[] as &[&str], &["x"], Route::AllPrimaries)
.await
.unwrap();
let echoed = format!("{all:?}").matches("\"x\"").count();
assert!(echoed >= 1, "expected per-node echo replies, got {all:?}");
}
);
timed_tokio_test!(
async fn cluster_runtime_subscribe_receive() {
let cluster = match common::ClusterHarness::start() {
Some(c) => c,
None => {
eprintln!("SKIP: cluster harness unavailable");
return;
}
};
let publisher = cluster_client!(cluster);
let subscriber = GlideClusterClient::connect(
GlideClusterClientConfiguration::with_address("127.0.0.1", cluster.seed_port())
.enable_pubsub(),
)
.await
.expect("connect pubsub cluster client");
let chan = common::key("c-chan");
subscriber.subscribe(&[chan.as_str()]).await.unwrap();
tokio::time::sleep(Duration::from_millis(300)).await;
let n: i64 = publisher.publish(&chan, "hello").await.unwrap();
assert!(n >= 1, "expected >=1 subscriber, got {n}");
let msg = tokio::time::timeout(Duration::from_secs(3), subscriber.get_pubsub_message())
.await
.expect("timed out")
.expect("receive error");
assert_eq!(msg.kind, PubSubMessageKind::Message);
assert_eq!(msg.payload.as_ref(), b"hello");
}
);
timed_tokio_test!(
async fn cluster_ssubscribe_sharded_receive() {
let cluster = match common::ClusterHarness::start() {
Some(c) => c,
None => {
eprintln!("SKIP: cluster harness unavailable");
return;
}
};
let publisher = cluster_client!(cluster);
let subscriber = GlideClusterClient::connect(
GlideClusterClientConfiguration::with_address("127.0.0.1", cluster.seed_port())
.enable_pubsub(),
)
.await
.expect("connect pubsub cluster client");
let chan = common::key("c-shard");
subscriber.ssubscribe(&[chan.as_str()]).await.unwrap();
tokio::time::sleep(Duration::from_millis(300)).await;
let n = publisher.spublish(&chan, "shard-hello").await.unwrap();
assert!(n >= 1, "expected >=1 shard subscriber, got {n}");
let msg = tokio::time::timeout(Duration::from_secs(3), subscriber.get_pubsub_message())
.await
.expect("timed out")
.expect("receive error");
assert_eq!(msg.kind, PubSubMessageKind::SMessage);
assert_eq!(msg.channel.as_ref(), chan.as_bytes());
assert_eq!(msg.payload.as_ref(), b"shard-hello");
}
);
timed_tokio_test!(
async fn cluster_zrangestore_by_score_same_slot() {
let cluster = match common::ClusterHarness::start() {
Some(c) => c,
None => {
eprintln!("SKIP: cluster harness unavailable");
return;
}
};
let c = cluster_client!(cluster);
let src = common::tkey("czr", "src");
let dst = common::tkey("czr", "dst");
let _: i64 = c
.zadd_multiple(&src, &[(1.0, "a"), (2.0, "b"), (3.0, "c")])
.await
.unwrap();
let n = c
.zrangestore_by_score(
&dst,
&src,
glide::commands::sorted_set::ScoreBound::Inclusive(1.0),
glide::commands::sorted_set::ScoreBound::Inclusive(2.0),
false,
None,
)
.await
.unwrap();
assert_eq!(n, 2);
let card: i64 = c.zcard(&dst).await.unwrap();
assert_eq!(card, 2);
}
);