use crate::ValkeyResult;
use crate::cmd::Cmd;
use crate::error::GlideError;
use crate::pipeline_options::PipelineOptions;
use crate::value::ValkeyValue;
use crate::write::ToValkeyArgs;
use crate::write::ValkeyWrite;
use glide_core::client::Client as CoreClient;
use redis::PipelineRetryStrategy;
use redis::cluster_routing::RoutingInfo;
pub fn pipe() -> Pipeline {
Pipeline::new()
}
#[derive(Clone, Debug, Default)]
pub struct Pipeline {
inner: redis::Pipeline,
}
impl Pipeline {
pub fn new() -> Self {
Pipeline {
inner: redis::Pipeline::new(),
}
}
pub fn atomic(&mut self) -> &mut Self {
self.inner.atomic();
self
}
pub fn cmd(&mut self, name: &str) -> &mut Self {
self.inner.cmd(name);
self
}
pub fn arg<A: ToValkeyArgs>(&mut self, arg: A) -> &mut Self {
arg.write_valkey_args(self);
self
}
pub fn ignore(&mut self) -> &mut Self {
self.inner.ignore();
self
}
pub(crate) fn add_command(&mut self, cmd: Cmd) -> &mut Self {
self.inner.add_command(cmd.into_redis());
self
}
pub(crate) fn as_redis(&self) -> &redis::Pipeline {
&self.inner
}
}
impl ValkeyWrite for Pipeline {
fn write_arg(&mut self, arg: &[u8]) {
self.inner.arg(arg);
}
fn write_arg_fmt(&mut self, arg: impl std::fmt::Display) {
self.inner.arg(arg.to_string().into_bytes());
}
}
pub(crate) async fn dispatch_pipeline(
core: &CoreClient,
pipeline: &Pipeline,
routing: Option<RoutingInfo>,
raise_on_error: bool,
options: &PipelineOptions,
) -> ValkeyResult<ValkeyValue> {
let redis_pipeline = pipeline.as_redis();
if redis_pipeline.is_empty() {
return Ok(ValkeyValue::Array(Vec::new()));
}
let timeout = options.timeout_millis();
let mut client = core.clone();
let reply = if redis_pipeline.is_atomic() {
client
.send_transaction(redis_pipeline, routing, timeout, raise_on_error)
.await
} else {
let retry_strategy = PipelineRetryStrategy {
retry_server_error: options.retry_server_error,
retry_connection_error: options.retry_connection_error,
};
client
.send_pipeline(
redis_pipeline,
routing,
raise_on_error,
timeout,
retry_strategy,
)
.await
};
ValkeyValue::from_redis(reply.map_err(GlideError::from_redis_error)?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_packed_pipeline_matches_redis() {
for atomic in [false, true] {
let mut glide_pipe = pipe();
let mut redis_pipe = redis::pipe();
if atomic {
glide_pipe.atomic();
redis_pipe.atomic();
}
glide_pipe
.set("k", "v")
.incr("k", 1)
.ignore()
.get("k")
.rpush("l", &["a", "b"][..]);
redis_pipe
.set("k", "v")
.incr("k", 1)
.ignore()
.get("k")
.rpush("l", &["a", "b"][..]);
let big = vec![0xABu8; 4096];
glide_pipe
.cmd("APPEND")
.arg("k")
.arg(&big)
.arg(&["a", "b", "c"][..]);
redis_pipe
.cmd("APPEND")
.arg("k")
.arg(&big)
.arg(&["a", "b", "c"][..]);
assert_eq!(
glide_pipe.as_redis().get_packed_pipeline(),
redis_pipe.get_packed_pipeline(),
"glide pipeline encoding diverged from redis (atomic = {atomic})",
);
}
}
}