Skip to content

Commit

Permalink
Merge branch 'main' into mock_connection
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Dyas committed Jun 11, 2022
2 parents 339952e + bb329dd commit 657bdc2
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 38 deletions.
10 changes: 6 additions & 4 deletions .github/workflows/rust.yml
Expand Up @@ -8,7 +8,6 @@ on:

env:
CARGO_TERM_COLOR: always
REDIS_VERSION: '6.2.4'

jobs:
build:
Expand All @@ -17,6 +16,9 @@ jobs:
strategy:
fail-fast: false
matrix:
redis:
- 6.2.4
- 7.0.0
rust:
- stable
- beta
Expand All @@ -37,9 +39,9 @@ jobs:
if: steps.cache-redis.outputs.cache-hit != 'true'
run: |
sudo apt-get update
wget https://github.com/redis/redis/archive/${{ env.REDIS_VERSION }}.tar.gz;
tar -xzvf ${{ env.REDIS_VERSION }}.tar.gz;
pushd redis-${{ env.REDIS_VERSION }} && BUILD_TLS=yes make && sudo mv src/redis-server src/redis-cli /usr/bin/ && popd;
wget https://github.com/redis/redis/archive/${{ matrix.redis }}.tar.gz;
tar -xzvf ${{ matrix.redis }}.tar.gz;
pushd redis-${{ matrix.redis }} && BUILD_TLS=yes make && sudo mv src/redis-server src/redis-cli /usr/bin/ && popd;
echo $PATH
- name: Install latest nightly
Expand Down
42 changes: 41 additions & 1 deletion redis/src/commands.rs
Expand Up @@ -3,7 +3,7 @@
use crate::cmd::{cmd, Cmd, Iter};
use crate::connection::{Connection, ConnectionLike, Msg};
use crate::pipeline::Pipeline;
use crate::types::{FromRedisValue, NumericBehavior, RedisResult, ToRedisArgs, RedisWrite};
use crate::types::{FromRedisValue, NumericBehavior, RedisResult, ToRedisArgs, RedisWrite, Expiry};

#[cfg(feature = "cluster")]
use crate::cluster_pipeline::ClusterPipeline;
Expand Down Expand Up @@ -397,6 +397,24 @@ implement_commands! {
cmd("PTTL").arg(key)
}

/// Get the value of a key and set expiration
fn get_ex<K: ToRedisArgs>(key: K, expire_at: Expiry) {
let (option, time_arg) = match expire_at {
Expiry::EX(sec) => ("EX", Some(sec)),
Expiry::PX(ms) => ("PX", Some(ms)),
Expiry::EXAT(timestamp_sec) => ("EXAT", Some(timestamp_sec)),
Expiry::PXAT(timestamp_ms) => ("PXAT", Some(timestamp_ms)),
Expiry::PERSIST => ("PERSIST", None),
};

cmd("GETEX").arg(key).arg(option).arg(time_arg)
}

/// Get the value of a key and delete it
fn get_del<K: ToRedisArgs>(key: K) {
cmd("GETDEL").arg(key)
}

/// Rename a key.
fn rename<K: ToRedisArgs>(key: K, new_key: K) {
cmd("RENAME").arg(key).arg(new_key)
Expand Down Expand Up @@ -1008,6 +1026,28 @@ implement_commands! {
cmd("PUBLISH").arg(channel).arg(message)
}

// Object commands

/// Returns the encoding of a key.
fn object_encoding<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("ENCODING").arg(key)
}

/// Returns the time in seconds since the last access of a key.
fn object_idletime<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("IDLETIME").arg(key)
}

/// Returns the logarithmic access frequency counter of a key.
fn object_freq<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("FREQ").arg(key)
}

/// Returns the reference count of a key.
fn object_refcount<K: ToRedisArgs>(key: K) {
cmd("OBJECT").arg("REFCOUNT").arg(key)
}

// ACL commands

/// When Redis is configured to use an ACL file (with the aclfile
Expand Down
3 changes: 3 additions & 0 deletions redis/src/lib.rs
Expand Up @@ -372,6 +372,8 @@ pub use crate::pipeline::Pipeline;
#[cfg_attr(docsrs, doc(cfg(feature = "script")))]
pub use crate::script::{Script, ScriptInvocation};

// preserve grouping and order
#[rustfmt::skip]
pub use crate::types::{
// utility functions
from_redis_value,
Expand All @@ -385,6 +387,7 @@ pub use crate::types::{
// utility types
InfoDict,
NumericBehavior,
Expiry,

// error and result types
RedisError,
Expand Down
81 changes: 51 additions & 30 deletions redis/src/script.rs
Expand Up @@ -4,6 +4,7 @@ use sha1_smol::Sha1;
use crate::cmd::cmd;
use crate::connection::ConnectionLike;
use crate::types::{ErrorKind, FromRedisValue, RedisResult, ToRedisArgs};
use crate::Cmd;

/// Represents a lua script.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -124,26 +125,15 @@ impl<'a> ScriptInvocation<'a> {
/// Invokes the script and returns the result.
#[inline]
pub fn invoke<T: FromRedisValue>(&self, con: &mut dyn ConnectionLike) -> RedisResult<T> {
loop {
match cmd("EVALSHA")
.arg(self.script.hash.as_bytes())
.arg(self.keys.len())
.arg(&*self.keys)
.arg(&*self.args)
.query(con)
{
Ok(val) => {
return Ok(val);
}
Err(err) => {
if err.kind() == ErrorKind::NoScriptError {
cmd("SCRIPT")
.arg("LOAD")
.arg(self.script.code.as_bytes())
.query(con)?;
} else {
fail!(err);
}
let eval_cmd = self.eval_cmd();
match eval_cmd.query(con) {
Ok(val) => Ok(val),
Err(err) => {
if err.kind() == ErrorKind::NoScriptError {
self.load_cmd().query(con)?;
eval_cmd.query(con)
} else {
Err(err)
}
}
}
Expand All @@ -157,15 +147,7 @@ impl<'a> ScriptInvocation<'a> {
C: crate::aio::ConnectionLike,
T: FromRedisValue,
{
let mut eval_cmd = cmd("EVALSHA");
eval_cmd
.arg(self.script.hash.as_bytes())
.arg(self.keys.len())
.arg(&*self.keys)
.arg(&*self.args);

let mut load_cmd = cmd("SCRIPT");
load_cmd.arg("LOAD").arg(self.script.code.as_bytes());
let eval_cmd = self.eval_cmd();
match eval_cmd.query_async(con).await {
Ok(val) => {
// Return the value from the script evaluation
Expand All @@ -174,12 +156,51 @@ impl<'a> ScriptInvocation<'a> {
Err(err) => {
// Load the script into Redis if the script hash wasn't there already
if err.kind() == ErrorKind::NoScriptError {
load_cmd.query_async(con).await?;
self.load_cmd().query_async(con).await?;
eval_cmd.query_async(con).await
} else {
Err(err)
}
}
}
}

/// Loads the script and returns the SHA1 of it.
#[inline]
pub fn load(&self, con: &mut dyn ConnectionLike) -> RedisResult<String> {
let hash: String = self.load_cmd().query(con)?;

debug_assert_eq!(hash, self.script.hash);

Ok(hash)
}

/// Asynchronously loads the script and returns the SHA1 of it.
#[inline]
#[cfg(feature = "aio")]
pub async fn load_async<C>(&self, con: &mut C) -> RedisResult<String>
where
C: crate::aio::ConnectionLike,
{
let hash: String = self.load_cmd().query_async(con).await?;

debug_assert_eq!(hash, self.script.hash);

Ok(hash)
}

fn load_cmd(&self) -> Cmd {
let mut cmd = cmd("SCRIPT");
cmd.arg("LOAD").arg(self.script.code.as_bytes());
cmd
}

fn eval_cmd(&self) -> Cmd {
let mut cmd = cmd("EVALSHA");
cmd.arg(self.script.hash.as_bytes())
.arg(self.keys.len())
.arg(&*self.keys)
.arg(&*self.args);
cmd
}
}
14 changes: 14 additions & 0 deletions redis/src/types.rs
Expand Up @@ -25,6 +25,20 @@ macro_rules! invalid_type_error_inner {
};
}

/// Helper enum that is used to define expiry time
pub enum Expiry {
/// EX seconds -- Set the specified expire time, in seconds.
EX(usize),
/// PX milliseconds -- Set the specified expire time, in milliseconds.
PX(usize),
/// EXAT timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds.
EXAT(usize),
/// PXAT timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds.
PXAT(usize),
/// PERSIST -- Remove the time to live associated with the key.
PERSIST,
}

/// Helper enum that is used in some situations to describe
/// the behavior of arguments in a numeric context.
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
Expand Down
4 changes: 2 additions & 2 deletions redis/tests/support/cluster.rs
Expand Up @@ -162,8 +162,8 @@ impl RedisCluster {
let client = redis::Client::open(conn_info).unwrap();
let mut con = client.get_connection().unwrap();

// retry 100 times
for _ in 1..100 {
// retry 500 times
for _ in 1..500 {
let value = redis::cmd("CLUSTER").arg("SLOTS").query(&mut con).unwrap();
let slots: Vec<Vec<redis::Value>> = redis::from_redis_value(&value).unwrap();

Expand Down
14 changes: 14 additions & 0 deletions redis/tests/test_async.rs
Expand Up @@ -350,6 +350,20 @@ fn test_script() {
.unwrap();
}

#[test]
#[cfg(feature = "script")]
fn test_script_load() {
let ctx = TestContext::new();
let script = redis::Script::new("return 'Hello World'");

block_on_all(async move {
let mut con = ctx.multiplexed_async_connection().await.unwrap();

let hash = script.prepare_invoke().load_async(&mut con).await.unwrap();
assert_eq!(hash, script.get_hash().to_string());
});
}

#[test]
#[cfg(feature = "script")]
fn test_script_returning_complex_type() {
Expand Down
16 changes: 16 additions & 0 deletions redis/tests/test_async_async_std.rs
Expand Up @@ -286,6 +286,22 @@ fn test_script() {
.unwrap();
}

#[test]
#[cfg(feature = "script")]
fn test_script_load() {
let ctx = TestContext::new();
let mut con = ctx.connection();

let script = redis::Script::new("return 'Hello World'");

block_on_all(async move {
let mut con = ctx.multiplexed_async_connection_async_std().await.unwrap();

let hash = script.prepare_invoke().load_async(&mut con).await.unwrap();
assert_eq!(hash, script.get_hash().to_string());
});
}

#[test]
#[cfg(feature = "script")]
fn test_script_returning_complex_type() {
Expand Down

0 comments on commit 657bdc2

Please sign in to comment.