Giter VIP home page Giter VIP logo

redis-rs's Introduction

redis-rs

Rust crates.io Chat

Redis-rs is a high level redis library for Rust. It provides convenient access to all Redis functionality through a very flexible but low-level API. It uses a customizable type conversion trait so that any operation can return results in just the type you are expecting. This makes for a very pleasant development experience.

The crate is called redis and you can depend on it via cargo:

[dependencies]
redis = "0.25.3"

Documentation on the library can be found at docs.rs/redis.

Note: redis-rs requires at least Rust 1.60.

Basic Operation

To open a connection you need to create a client and then to fetch a connection from it. In the future there will be a connection pool for those, currently each connection is separate and not pooled.

Many commands are implemented through the Commands trait but manual command creation is also possible.

use redis::Commands;

fn fetch_an_integer() -> redis::RedisResult<isize> {
    // connect to redis
    let client = redis::Client::open("redis://127.0.0.1/")?;
    let mut con = client.get_connection()?;
    // throw away the result, just make sure it does not fail
    let _ : () = con.set("my_key", 42)?;
    // read back the key and return it.  Because the return value
    // from the function is a result for integer this will automatically
    // convert into one.
    con.get("my_key")
}

Variables are converted to and from the Redis format for a wide variety of types (String, num types, tuples, Vec<u8>). If you want to use it with your own types, you can implement the FromRedisValue and ToRedisArgs traits, or derive it with the redis-macros crate.

Async support

To enable asynchronous clients, enable the relevant feature in your Cargo.toml, tokio-comp for tokio users or async-std-comp for async-std users.

# if you use tokio
redis = { version = "0.25.3", features = ["tokio-comp"] }

# if you use async-std
redis = { version = "0.25.3", features = ["async-std-comp"] }

TLS Support

To enable TLS support, you need to use the relevant feature entry in your Cargo.toml. Currently, native-tls and rustls are supported.

To use native-tls:

redis = { version = "0.25.3", features = ["tls-native-tls"] }

# if you use tokio
redis = { version = "0.25.3", features = ["tokio-native-tls-comp"] }

# if you use async-std
redis = { version = "0.25.3", features = ["async-std-native-tls-comp"] }

To use rustls:

redis = { version = "0.25.3", features = ["tls-rustls"] }

# if you use tokio
redis = { version = "0.25.3", features = ["tokio-rustls-comp"] }

# if you use async-std
redis = { version = "0.25.3", features = ["async-std-rustls-comp"] }

With rustls, you can add the following feature flags on top of other feature flags to enable additional features:

  • tls-rustls-insecure: Allow insecure TLS connections
  • tls-rustls-webpki-roots: Use webpki-roots (Mozilla's root certificates) instead of native root certificates

then you should be able to connect to a redis instance using the rediss:// URL scheme:

let client = redis::Client::open("rediss://127.0.0.1/")?;

To enable insecure mode, append #insecure at the end of the URL:

let client = redis::Client::open("rediss://127.0.0.1/#insecure")?;

Deprecation Notice: If you were using the tls or async-std-tls-comp features, please use the tls-native-tls or async-std-native-tls-comp features respectively.

Cluster Support

Support for Redis Cluster can be enabled by enabling the cluster feature in your Cargo.toml:

redis = { version = "0.25.3", features = [ "cluster"] }

Then you can simply use the ClusterClient, which accepts a list of available nodes. Note that only one node in the cluster needs to be specified when instantiating the client, though you can specify multiple.

use redis::cluster::ClusterClient;
use redis::Commands;

fn fetch_an_integer() -> String {
    let nodes = vec!["redis://127.0.0.1/"];
    let client = ClusterClient::new(nodes).unwrap();
    let mut connection = client.get_connection().unwrap();
    let _: () = connection.set("test", "test_data").unwrap();
    let rv: String = connection.get("test").unwrap();
    return rv;
}

Async Redis Cluster support can be enabled by enabling the cluster-async feature, along with your preferred async runtime, e.g.:

redis = { version = "0.25.3", features = [ "cluster-async", "tokio-std-comp" ] }

use redis::cluster::ClusterClient;
use redis::AsyncCommands;

async fn fetch_an_integer() -> String {
    let nodes = vec!["redis://127.0.0.1/"];
    let client = ClusterClient::new(nodes).unwrap();
    let mut connection = client.get_async_connection().await.unwrap();
    let _: () = connection.set("test", "test_data").await.unwrap();
    let rv: String = connection.get("test").await.unwrap();
    return rv;
}

JSON Support

Support for the RedisJSON Module can be enabled by specifying "json" as a feature in your Cargo.toml.

redis = { version = "0.25.3", features = ["json"] }

Then you can simply import the JsonCommands trait which will add the json commands to all Redis Connections (not to be confused with just Commands which only adds the default commands)

use redis::Client;
use redis::JsonCommands;
use redis::RedisResult;
use redis::ToRedisArgs;

// Result returns Ok(true) if the value was set
// Result returns Err(e) if there was an error with the server itself OR serde_json was unable to serialize the boolean
fn set_json_bool<P: ToRedisArgs>(key: P, path: P, b: bool) -> RedisResult<bool> {
    let client = Client::open("redis://127.0.0.1").unwrap();
    let connection = client.get_connection().unwrap();

    // runs `JSON.SET {key} {path} {b}`
    connection.json_set(key, path, b)?
}

To parse the results, you'll need to use serde_json (or some other json lib) to deserialize the results from the bytes. It will always be a Vec, if no results were found at the path it'll be an empty Vec. If you want to handle deserialization and Vec unwrapping automatically, you can use the Json wrapper from the redis-macros crate.

Development

To test redis you're going to need to be able to test with the Redis Modules, to do this you must set the following environment variable before running the test script

  • REDIS_RS_REDIS_JSON_PATH = The absolute path to the RedisJSON module (Either librejson.so for Linux or librejson.dylib for MacOS).

  • Please refer to this link to access the RedisJSON module:

If you want to develop on the library there are a few commands provided by the makefile:

To build:

$ make

To test:

$ make test

To run benchmarks:

$ make bench

To build the docs (require nightly compiler, see rust-lang/rust#43781):

$ make docs

We encourage you to run clippy prior to seeking a merge for your work. The lints can be quite strict. Running this on your own workstation can save you time, since Travis CI will fail any build that doesn't satisfy clippy:

$ cargo clippy --all-features --all --tests --examples -- -D clippy::all -D warnings

To run fuzz tests with afl, first install cargo-afl (cargo install -f afl), then run:

$ make fuzz

If the fuzzer finds a crash, in order to reproduce it, run:

$ cd afl/<target>/
$ cargo run --bin reproduce -- out/crashes/<crashfile>

redis-rs's People

Contributors

0xwof avatar arthurlm avatar ayosec avatar badboy avatar d3rpp avatar davidbm avatar dbrgn avatar dependabot[bot] avatar djc avatar doumanash avatar doyshinda avatar emschwartz avatar engylemure avatar evdokimovs avatar gavrie avatar gferon avatar hadashia avatar jaymell avatar jwilm avatar marwes avatar mattcollier avatar mitsuhiko avatar nihohit avatar swilcox3 avatar terkwood avatar theglenn88 avatar utkarshgupta137 avatar weihanglo avatar wraithan avatar xoac avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redis-rs's Issues

Wrong number of arguments when using mget with redis-rs

I'm trying to access Redis using Rust with the following:

extern crate redis;

use redis::{Client, Commands, Connection, RedisResult};

fn main() {

    let redis_client = Client::open("redis://127.0.0.1/").unwrap();
    let redis_conn = redis_client.get_connection().unwrap();

    let mut keys_to_get = vec![];
    keys_to_get.push("random_key_1".to_string());
    keys_to_get.push("random_key_2".to_string());
    let redis_result: String = redis_conn.get(keys_to_get).unwrap();
}

When I run cargo run I get:

     Running `target/debug/test_resdis`
thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: An error was signalled by the server: wrong number of arguments for 'get' command', ../src/libcore/result.rs:746
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/test_resdis` (exit code: 101)

As explained in this answer Under the hood, the library inspects the key type to know if it's multivalued or not, using ToRedisArgs::is_single_arg, which has a default implementation of true.

I'm trying to do a PR, but still too newbie in Rust to success :(

I will keep trying, but I'd like to let the bug(?) filled.

Can't Get Example Working

I'm working on a project for which I want to use redis-rs.

Attempting to compile the low-level example in the README (slightly modified) results in the following error:

       Fresh encoding v0.1.0 (https://github.com/lifthrasiir/rust-encoding#35f0d70f)
       Fresh unsafe-any v0.1.0 (https://github.com/reem/rust-unsafe-any.git#75cff194)
       Fresh replace-map v0.0.1 (https://github.com/reem/rust-replace-map.git#2bbe0c93)
       Fresh typeable v0.0.1 (https://github.com/reem/rust-typeable.git#55154e18)
       Fresh openssl v0.0.0 (https://github.com/sfackler/rust-openssl.git#cc7511a3)
       Fresh redis-rs v0.2.0 (https://github.com/mitsuhiko/redis-rs#05eb503c)
       Fresh phantom v0.0.0 (https://github.com/reem/rust-phantom.git#deb5acfe)
       Fresh url v0.1.0 (https://github.com/servo/rust-url#bfdf8093)
       Fresh error v0.0.0 (https://github.com/reem/rust-error.git#5aaaa164)
       Fresh typemap v0.0.0 (https://github.com/reem/rust-typemap.git#895f26e2)
       Fresh http v0.1.0-pre (https://github.com/chris-morgan/rust-http.git#c1d601c1)
       Fresh plugin v0.0.0 (https://github.com/reem/rust-plugin.git#19b97b79)
       Fresh content_type v0.1.0 (https://github.com/reem/rust-http-content-type.git#635e92b7)
       Fresh iron v0.0.1 (file:///old_media/projects/rust/iron)
   Compiling paste v0.0.0 (file:///old_media/projects/rust/paste)
     Running `rustc src/paste.rs --crate-name paste --crate-type bin -g --out-dir /old_media/projects/rust/paste/target --dep-info /old_media/projects/rust/paste/target/.fingerprint/paste-0422568cb1da223b/dep-bin-paste -L /old_media/projects/rust/paste/target -L /old_media/projects/rust/paste/target/deps -L /old_media/projects/rust/paste/target/native/content_type-b3fc4f01bc3dcbfb -L /old_media/projects/rust/paste/target/native/http-b27c1e7938f5a5d0 --extern url=/old_media/projects/rust/paste/target/deps/liburl-921578b148f50e06.rlib --extern iron=/old_media/projects/rust/paste/target/deps/libiron-b3db66ed2f682091.rlib --extern redis=/old_media/projects/rust/paste/target/deps/libredis-bc449fc25395560f.rlib`
warning: using multiple versions of crate `url`
src/paste.rs:1:1: 1:19 note: used here
src/paste.rs:1 extern crate iron;
           ^~~~~~~~~~~~~~~~~~
note: crate name: url
src/paste.rs:2:1: 2:20 note: used here
src/paste.rs:2 extern crate redis;
           ^~~~~~~~~~~~~~~~~~~
note: crate name: url
src/paste.rs:31:9: 31:26 error: unresolved enum variant, struct or const `RedisError`
src/paste.rs:31         redis::RedisError(redis::ResponseError, msg) => {
                        ^~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `paste`.

Caused by:
  Process didn't exit successfully: `rustc src/paste.rs --crate-name paste --crate-type bin -g --out-dir /old_media/projects/rust/paste/target --dep-info /old_media/projects/rust/paste/target/.fingerprint/paste-0422568cb1da223b/dep-bin-paste -L /old_media/projects/rust/paste/target -L /old_media/projects/rust/paste/target/deps -L /old_media/projects/rust/paste/target/native/content_type-b3fc4f01bc3dcbfb -L /old_media/projects/rust/paste/target/native/http-b27c1e7938f5a5d0 --extern url=/old_media/projects/rust/paste/target/deps/liburl-921578b148f50e06.rlib --extern iron=/old_media/projects/rust/paste/target/deps/libiron-b3db66ed2f682091.rlib --extern redis=/old_media/projects/rust/paste/target/deps/libredis-bc449fc25395560f.rlib` (status=101)

You can view the source of the problematic code here.

Make it work with futures-rs

Somehow we should change the library so that it can optionally work with futures. I need to figure out what the best course of action would be here.

Can we open a connection with a &ConnectionInfo?

I'm implementing a simple server application that can read and update some sensor values in Redis. The Redis database is being used from different Iron handlers.

Right now, I instantiate the server with a T: IntoConnectionInfo parameter. That makes sense, because the ConnectionInfo is then passed into the server instance itself.

Then I create a handler for each endpoint. Each of these handlers also needs a clone of the ConnectionInfo, because a handler has a 'static lifetime bound (see http://stackoverflow.com/a/33189734/284318).

But I also have a simple helper function that retrieves a sensor value. I pass it the connection information and it then creates a Client and a Connection and returns the value.

In this case, shouldn't it be possible to simply pass in a read only reference to the ConnectionInfo object (&ConnectionInfo) instead of cloning the entire ConnectionInfo instance?

Right now the Client::open method accepts a parameter of type T: IntoConnectionInfo, but not a &T: IntoConnectionInfo.

Is this desired behavior? Am I overlooking something? (I don't have too much experience with Rust yet, so forgive me if this is a stupid questionโ„ข ๐Ÿ˜ƒ)

Handling bulk mixed types?

I'm working on porting the redis-cluster library to rust and it uses the CLUSTER SLOTS command to initialize its slots cache. This returns a mixed nested array and I'm not sure how to properly handle it.

let mut cmd = Cmd::new();
cmd.arg("CLUSTER").arg("SLOTS");

If I try a nested vector of u8:

let res = cmd.query::<Vec<Vec<u8>>>(&conn);

I get Ok([[171, 255], [0, 84], [85, 170]]).

If I try a nested vector of String (<Vec<Vec<String>>>) I get Ok([[], [], []]).

If I try a double nested vector of String (<Vec<Vec<String>>>) I get Ok([[["127.0.0.1"], ["127.0.0.1"]], [["127.0.0.1"], ["127.0.0.1"]], [["127.0.0.1"], ["127.0.0.1"]]]).

Here's what the response is:

response was bulk(bulk(int(10923), int(16383), bulk(string-data('"127.0.0.1"'), int(7002)), bulk(string-data('"127.0.0.1"'), int(7005))), bulk(int(0), int(5460), bulk(string-data('"127.0.0.1"'), int(7000)), bulk(string-data('"127.0.0.1"'), int(7003))), bulk(int(5461), int(10922), bulk(string-data('"127.0.0.1"'), int(7001)), bulk(string-data('"127.0.0.1"'), int(7004))))

No bindings for LSET

There donโ€™t seem to be command bindings for LSET. You have to write:

cmd("LSET").arg(key).arg(index).arg(value)

Is there any reason for this omission?

how would we do connection pooling

I had couple of questions on how to use redis-rs

  1. how can we use/enable connection pooling ?
  2. why was connection trait exposed?
as in instead of using this way

let client = try!(redis::Client::open("redis://127.0.0.1/")); let con = try!(client.get_connection()); let _ : () = try!(con.set("my_key", 42));

we could have had

let client = try!(redis::Client::open("redis://127.0.0.1/")); let _ : () = try!(client.set("my_key", 42).execute());

and under the hood client could borrow one connection object from the pool and once used; it could have been given back to pool.
I just wanted to understand what was the de

crates.io release with fixed versions

The newly published url crate v1.0.0 comes with breaking changes. As of right now, the latest version of the redis crate on crates.io still has wildcard dependencies. On fresh projects or after a cargo update, you'll get errors like this

redis-0.5.2/src/connection.rs:138:17: 138:23 error: attempted to take value of method `scheme` on type `url::Url`
redis-0.5.2/src/connection.rs:138         if self.scheme == "redis" {

Sure, it's easy enough to work around this by

  • instead depending on the GitHub version of the redis crate
  • fixing the Cargo.lock file by hand (url 0.5.9 worked for me)

but I think it would be worthwhile to have a version of the redis crate with fixed dependencies on crates.io.

Deadlock on connection loss

Hi there,

if a connection to redis was successfully established and is lost afterwards, this library will deadlock on the next query.
It would be nice if an error is thrown instead of breaking the thread.
And maybe a reconnect loop in the background. :)

[question] FromRedisValue implementation troubles

How can I get rid of:

  • unwrap
  • panic

in the snippet below?

impl redis::FromRedisValue for Dashboard {
    fn from_redis_value(v: &redis::Value) -> redis::RedisResult<Dashboard> {
        match *v {
            redis::Value::Data(ref val) => {
                // if anyone tells me how to map serde error to  RedisError i'll get rid of
                // `unwrap` here and `panic` below
                Ok(serde_json::from_slice(val).unwrap())
            },
            _ => {
                panic!( "Response type not Dashboard compatible.")
            },
        }
    }
}

I tried to use RedisError directly but it needs ErrorRepr which is private making the whole concept impossible
https://github.com/mitsuhiko/redis-rs/blob/15a2ce6b15b3311c7ccc0c6e82d6b853dff1763b/src/types.rs#L145

Does not compile with rustc beta

C:\Users\kot\.cargo\registry\src\github.com-1ecc6299db9ec823\redis-0.2.0\src\macros.rs:11:20: 11:55
error: unresolved name `std::error::FromError::from_error`
C:\Users\kot\.cargo\registry\src\github.com-1ecc6299db9ec823\redis-0.2.0\src\macros.rs:11         re
turn Err(::std::error::FromError::from_error($expr));

         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I think it's important to make it compilable with beta compiler because it'll soon become release 1.0.

Slow read performance

Reading a hash (with 90K keys with each hash-value ~1KiB) using the below Rust function is slow. Pull request #63 already provided a significant speed-up. However, the same function implemented in Java using the Jedis driver, is still twice as fast as Rust (rustc 1.4.0-nightly (7d78f2d33 2015-09-01)) on my machine (after jvm warm up; jvm: java version "1.8.0_60"; Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)).

Rust

fn calc_hsize(client: &redis::Client, hkey: &str) -> Result<(), Error> {
    let conn = try!(client.get_connection());

    let sw = Stopwatch::start_new();
    let iter: Vec<(Vec<u8>, Vec<u8>)> = try!(conn.hgetall(hkey));
    let mut key_size = 0usize;
    let mut val_size = 0usize;
    for (key, val) in iter {
        key_size += key.len();
        val_size += val.len();
    }
    let exec_time = sw.elapsed_ms();
    debug!("Executed in {} millis", exec_time);

    let _ = write!(stdout(), "key_size:\t{}\nval_size:\t{}\ntotal_size:\t{}\n",
                   key_size, val_size, key_size + val_size);

    Ok(())
}

Java

  private static void calc_hash_size(Jedis driver, String key) {
    long start = System.nanoTime();

    Map<byte[], byte[]> data = driver.hgetAll(key.getBytes(StandardCharsets.UTF_8));
    int key_size = 0;
    int val_size = 0;
    for (Map.Entry<byte [], byte []> dat : data.entrySet()) {
      key_size += dat.getKey().length;
      val_size += dat.getValue().length;
    }
    long end = System.nanoTime();

    System.out.println("key_size:\t" + key_size);
    System.out.println("val_size:\t" + val_size);
    System.out.println("total_size:\t" + (key_size + val_size));
    System.err.println("Executed in " + TimeUnit.NANOSECONDS.toMillis(end-start));
  }

Trying @astro's pull request #61 provides some more speed up making the rust version (not faster but at least) comparable to the java version.

Error when querying redis to get ordered set data

Hi,
I was trying to query redis to get an ordered data set. I used the "zrangebyscore_withscores" function and it gives me this error.:

e ->Response was of incompatible type: "Response type not convertible to numeric." (response was bulk(string-data('"400.4"'), string-data('"1437421736000"'), string-data('"33.4"'), string-data('"1437420523000"')))

Am I doing something wrong??

Thanks in advance.

Specifying the wrong type for a SCAN silently fails

If you specify a redis::Iter<isize> (for example) over keys that are strings, the iter will make all the scan calls, but return nothing.

Small example

    let client = try!(redis::Client::open("redis://127.0.0.1/"));
    let con = try!(client.get_connection());
    let iter: redis::Iter<isize> = try!(con.scan());
    for x in iter {
        println!("{}",x);
    }

Assuming you have keys that can't be converted to an isize, this will print nothing, or if a subset can be converted, only those can can will be included in the iter.

I would suggest at least some error be given that the conversion could not take place, otherwise this is very mysterious behavior.

Implement PubSub

PubSub will need some special handling. The intended API is this:

let mut pubsub = con.pubsub();
pubsub.subscribe("foo");
pubsub.subscribe("bar");

// is a message currently already pending?
while pubsub.message_available() {
    // get the next message, block forever
    let msg = pubsub.next_message(None).unwrap();
}

// get next message, block 1 second
let msg = pubsub.next_message(Some(1.0)).unwrap();

If the message cannot be received we need a new error code.

Add support for ToRedisArgs for &std::string::String

right now i need to do

connection.pfadd(
     my_key.as_bytes(),
     my_value.as_bytes(),
)

it would be great if i could do

connection.pfadd(
     &my_key,
     &my_value,
)

if there's no underlying reason blocking things, I can propose you a PR

Allow multiple key arguments in certain high level commands (ex. SUNIONSTORE)

Example: SUNIONSTORE.

The two arguments should implement ToRedisArgs, but should not be the same type:

    |               .sunionstore(String::from("something"), lists.iter()
    |  _____________________________________________________^ starting here...
    | |                 .map(|item| format!("{}{}", item, "test"))
    | |                 .collect::<Vec<String>>()
    | |_________________________________________^ ...ending here: expected struct
 `std::string::String`, found struct `std::vec::Vec`
    |
    = note: expected type `std::string::String`
               found type `std::vec::Vec<std::string::String>`
fn sunionstore<K: ToRedisArgs>(dstkey: K, keys: K) {...}

Here, both String and Vec<String> implement ToRedisArgs, so they should satisfy the function generics, except that the function requires that they be the same type K: ToRedisArgs. Since the first argument is String, the function expects the second argument to be String as well.

There are a few other such functions, such as sdiffstore, which have the same problem.

Scoped PubSub API

Currently, pubsub support is only available only by creating a client and then asking for a PubSub API. This neatly avoids the issue that only certain commands are available once calling subscribe or psubscribe. However, it's very unfriendly to connection poolers such as r2d2.

I'd like to propose adding a PubSubCommands trait which will make pub/sub usable from a borrowed connection.

/// Allows pubsub callbacks to stop receiving messages.
///
/// Arbitraty data may be returned from `Break`.
enum ControlFlow<U> {
    Continue,
    Break(U),
}

/// The PubSub trait allows subscribing to one or more items
/// and receiving a callback whenever a message arrives.
///
/// The trait is generic so that it may be implemented for multiple
/// types such as `&str` and `&[u8]`. The `self` argument is a mutable
/// reference to prevent the implementing type from being used
/// while subscribed to messages.
///
/// Each method handles subscribing to the list of keys, waiting for
/// messages, and unsubscribing from the same list of keys once
/// a ControlFlow::Break is encountered.
///
/// Once (p)subscribe returns Ok(U), the connection is again safe to use
/// for calling other methods.
trait PubSub<T> {
    fn subscribe<F, U>(&mut self, _: &[T], _: F) -> Result<U>
        where F: FnMut(Msg) -> ControlFlow<U>;
        
    fn psubscribe<F, U>(&mut self, _: &[T], _: F) -> Result<U>
        where F: FnMut(Msg) -> ControlFlow<U>;
}

Another design I'm considering is to have an API which borrows a &mut Connection(Like) and returns a new type which only has pubsub commands. Again, this supports connection pooling but would be a lot more manual than the above proposition. It is, however, much closer to the current implementation.

For example,

impl Connection {
    /// Get a PubSub API for this connection
    ///
    /// Lifetimes are included to clearly show ownership
    fn as_pubsub<'a>(&'a mut self) -> PubSubRef<'a>;
}

PubSubRef would then basically be today's PubSub type but using a borrowed connection. Again, using a mutable borrow prevents the connection from having disallowed commands called on it while a subscription is active.

Both of these approaches can coexist! The scoped subscribe and psubscribe can be provided for convenience while also exposing the ownership based approach. In fact, the scoped convenience methods would probably be implemented using a PubSubRef.

I wanted to get some feedback about this before going too far down an implementation. Now that I've gone through the design, this actually doesn't seem like too much work, so I may just put something together and see how it feels.

[dependencies.redis-rs] in cargo.toml causes version required: * error.

Upon compiling using the example in the README.md

[dependencies.redis-rs]
git = "https://github.com/mitsuhiko/redis-rs.git"

causes

Updating git repository `https://github.com/mitsuhiko/redis-rs.git`
no package named `redis-rs` found (required by `my-project`)
location searched: https://github.com/mitsuhiko/redis-rs.git
version required: *

the required text for the cargo.toml is now:

[dependencies.redis]
git = "https://github.com/mitsuhiko/redis-rs.git"

Notice the lack of '-rs' in [dependencies.redis]

Retrieving error response string from a RedisError

Is there a way to look up the server's response when a RedisError of kind ResponseError is returned? I believe the detail can be printed out, but I'm not sure if there's another way I can retrieve it?

Inverted argument order for zadd()

The current Redis documentation for ZADD states that the order of the member-score-pair arguments is: score member

But redis-rs expects: fn zadd<K: ToRedisArgs, S: ToRedisArgs, M: ToRedisArgs>(key: K, member: M, score: S)

Not sure what the correct solution here is since reversing the order would break every piece of code that uses zadd() at the moment.

std::io has been moved

Which now yields

src/lib.rs:20:15: 20:24 error: unresolved import `std::io::MemWriter`. Could not find `io` in `std`

error: struct `Connection` is private

Hi there,

I'm trying to create my own struct with a redis::connection::Connection property. Am I supposed to be able to do that?

// src/lib.rs
extern crate redis;
use redis::connection::Connection;
...

I'm getting the following error:

src/lib.rs:2:5: 2:34 error: struct `Connection` is private
src/lib.rs:2 use redis::connection::Connection;
                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I found this error pretty weird because the Connection struct seems to be public.

Any help is appreciated. Thank you very much!

Performance Issues

I just created a comparison using this library with @stefanwille's redis-client-benchmarks:
stefanwille/redis-client-benchmarks#5

I was surprised to see the not only the Crystal implementation, but more notably the Go implementation seemed to be significantly faster (about 2x at least in this particular micro benchmark):

ruby_redisrb_hiredis_performance.rb  4.99s user 0.24s system 89% cpu 5.856 total
go_redigo_performance                0.38s user 0.04s system 43% cpu 0.973 total
crystal_redis_performance            0.37s user 0.08s system 44% cpu 0.993 total
rust-redis-client-performance        1.21s user 0.11s system 61% cpu 2.146 total

I would appreciate any feedback on the way I implemented the benchmark. E.g. is this a case of potential profiling work that could be done to improve the performance this library, or am I perhaps misunderstanding something?

Panic while running tests

cargo test fails with panic in panic.

Versions of my software:

rustc 1.0.0-beta (built 2015-04-03)
cargo 0.0.1-pre-nightly (aff068a 2015-04-13) (built 2015-04-13)
Xaviers-MacBook-Pro:redis-rs xavierlange$ cargo test
     Running target/debug/redis-7041b44dbf62952c

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

     Running target/debug/test_basic-a38151b82821c47a

running 23 tests
test test_empty_pipeline ... ok
test test_incr ... ok
test test_getset ... FAILED
test test_auto_m_versions ... FAILED
test test_args ... FAILED
test test_hash_ops ... FAILED
stack backtrace:
   1:        0x10aa41317 - sys::backtrace::write::h04d44e44bc5d65761LC
   2:        0x10aa4712a - panicking::on_panic::h7f13e38298cbf2d0HAI
   3:        0x10aa2c1de - rt::unwind::begin_unwind_inner::h006846de1d0c4368iiI
   4:        0x10aa2c6a6 - rt::unwind::begin_unwind_fmt::h9ff39a3bc3f81a36TgI
   5:        0x10aa4696d - rust_begin_unwind
   6:        0x10aa67ce5 - panicking::panic_fmt::hf8b5849246aabe305jC
   7:        0x10a90457a - result::Result<T, E>::unwrap::h11076843804536881027
   8:        0x10a9043b9 - RedisServer.Drop::drop::ha668c991e368e7c7kba
   9:        0x10a90580a - RedisServer::drop.4136::h94675363501f3ece
  10:        0x10a909485 - TestContext::drop.4337::ha54afc2b25b204e4
  11:        0x10a92f97e - test_json::h8495c6bdd1c894d8ANa
  12:        0x10a9d8670 - boxed::F.FnBox<A>::call_box::h7779837395221082017
  13:        0x10a9dba87 - boxed::F.FnBox<A>::call_box::h336021066823149433
  14:        0x10a9d8e57 - rt::unwind::try::try_fn::h15784714474036424216
  15:        0x10aa484d8 - rust_try_inner
  16:        0x10aa484c5 - rust_try
  17:        0x10a9d914a - boxed::F.FnBox<A>::call_box::h5466409690577409441
  18:        0x10aa4552d - sys::thread::create::thread_start::hf1de55c8951cb2cdihH
  19:     0x7fff98943898 - _pthread_body
  20:     0x7fff98943729 - _pthread_start
thread panicked while panicking. aborting.
stack backtrace:
test test_nice_api ... FAILED
  Process didn't exit successfully: `/Users/xavierlange/code/rust/redis-rs/target/debug/test_basic-a38151b82821c47a` (signal: 4)

con.brpop always return ()

I am writing a rust tool to load a set from csv into a redis set on demand. I choose to "listen" for demands on redis key with brpop as can be seen at https://github.com/mmacedoeu/csvtoredis.rs/blob/master/src/main.rs

        let pop = try!(con.brpop(REQUEST_KEY, TIMEOUT));
        println!("{:?}", pop);

Prepopulating the REQUEST_KEY or at runtime println always print '()'.
I could see the set is consumed but couldnt see it on display.

Still i didnt figure out how to match pop so I parse only non empty demands.

"make test" doesn't work under freebsd

[ ~/redis-rs]$ make test
make: exec(./runtests.sh) failed (No such file or directory)
*** Error code 1

Stop.
make: stopped in /usr/home/user/redis-rs
[~/redis-rs]$ head -1 ./runtests.sh

!/bin/bash

[ ~/redis-rs]$ which bash
/usr/local/bin/bash

Question: Handling interrupted Connections properly

Hey,

I wondered what happens with a redis::Connection if the connection to Redis was interrupted somehow, e.g Redis-server is not available. Do I need to create a new redis::Connection or can I use the old instance? What's the proper way to handle this?

scan_match early termination

The SCAN command returns a cursor that redis-rs automatically updates and uses to keep gathering data through an iterator.

And this is really great ๐Ÿ‘

SCAN ... MATCH performs matching after having retrieved a chunk of data. An empty result doesn't mean that there are no results, and that iterating should end. It just means that there was no matching keys in the current chunk, but the client library should keep iterating no matter what.

But it looks like a redis iterator ends when there are no results, even though, when there is a MATCH clause, it should actually keep iterating.

Build fails with rust-nightly

[~/code/redis-rs (master)]% cargo build --verbose
   Compiling redis-rs v0.2.0 (file:///home/badboy/code/redis-rs)
     Running `rustc src/redis/lib.rs --crate-name redis --crate-type lib -g -C metadata=932e09b8c48ece67 -C extra-filename=-932e09b8c48ece67 --out-dir /home/badboy/code/redis-rs/target --dep-info /home/badboy/code/redis-rs/target/.fingerprint/redis-rs-932e09b8c48ece67/dep-lib-redis -L /home/badboy/code/redis-rs/target -L /home/badboy/code/redis-rs/target/deps`
src/redis/connection.rs:167:32: 167:33 error: unexpected token: `]`
src/redis/connection.rs:167             chan.push_all(item[]);
                                                           ^
Could not compile `redis-rs`.

Caused by:
  Process didn't exit successfully: `rustc src/redis/lib.rs --crate-name redis --crate-type lib -g -C metadata=932e09b8c48ece67 -C extra-filename=-932e09b8c48ece67 --out-dir /home/badboy/code/redis-rs/target --dep-info /home/badboy/code/redis-rs/target/.fingerprint/redis-rs-932e09b8c48ece67/dep-lib-redis -L /home/badboy/code/redis-rs/target -L /home/badboy/code/redis-rs/target/deps` (status=101)
[~/code/redis-rs (master)]% rustc --version
rustc 0.12.0-nightly (af3889f69 2014-09-18 21:20:38 +0000)

I'm not a Rust-Pro and don't know what exactly changed between latest stable and nightly. Thought you should know.

Thanks for this project!

rpush / lpush: unable to infer enough type information about `_`

I'm trying to do a simple rpush / lpush operation, but the compiler don't let me use strings as argument for those commands.

use redis::{Connection, Commands};
...
self.conn.rpush("my_queue", "value");

Compiler error:

error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
src/lib.rs:41     self.conn.rpush("my_queue", "value");
                            ^~~~~~~~~~~~~~~
src/lib.rs:41:15: 41:30 help: run `rustc --explain E0282` to see a detailed explanation
error: aborting due to previous error

What am I missing here?

Can not build on Windows

I got this error when trying to build on Windows:

error: no associated item named `Unix` found for type `connection::ConnectionInfo` in the curre
nt scope
ConnectionInfo::Unix(_) => false,

It was building fine previously (version 0.5.3 I think), seems like the new version introduced a new dependency on Unix stream? If so, can we use conditional compilation to make it build on Windows again?

Additional collaborators?

Hey everyone,

It seems like velocity has slowed down on this project in recent months. Completely understandably, current maintainers are presumably busy with other projects/life/etc. I'm not here to critique. Rather, I am curious if the project would be open to additional maintainers to keep PRs/issues advancing and getting bug fixes and patches shipped.

To that end, I'm willing to volunteer some of my own time. I'm also open to other solutions if the project prefers not to add more maintainers. At the very least, let's start a dialog about it.

naming confusion in redis protocol parsing

Redis protocol specification says that:

In RESP, the type of some data depends on the first byte:

  • For Simple Strings the first byte of the reply is "+"
  • For Errors the first byte of the reply is "-"
  • For Integers the first byte of the reply is ":"
  • For Bulk Strings the first byte of the reply is "$"
  • For Arrays the first byte of the reply is "*"

That's why I think the following code is inconsistent.

match b as char {
    '+' => self.parse_status(),
    ':' => self.parse_int(),
    '$' => self.parse_data(),
    '*' => self.parse_bulk(),
    '-' => self.parse_error(),
    _ => fail!((ResponseError, "Invalid response when parsing value")),
}

Besides, In RESP, different parts of the protocol are always terminated with "\r\n" (CRLF), which I think is the newline in redis response strings. it may be not approprate to mix the '\n' within the dispose of CRLF.

report a bug on connect

i read you source code found on connect method has wrong logic, first command must be AUTH, cause i meet a exception, there is error details: Redis server refused to switch database!

Handling BULK responses

I'm trying to parse a response from HGETALL command:

let res: redis::RedisResult<String> = redis::cmd("HGETALL").arg(key.clone()).query(&con);

But this fails at run-time with:

Response was of incompatible type: "Response type not string compatible." (response was bulk(string-data('"some_string"'), string-data('"other_string"')))

I've tried replacing the String type with <redis::types::Value as Trait>::Bulk, but couldn't get it to compile. What is the right way to do this?

set_ex args order

the format of setex should be setex key expire value
but the library uses setex key value expire, so calling con.set_ex will return value is not an integer or out of range error

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.