Skip to content

Commit

Permalink
9.1.0 (#253)
Browse files Browse the repository at this point in the history
* feat: add redisearch interface
* ci: adapt testing and ci processes to use both redis and valkey
* feat: add `FromIterator` impl to `RedisMap`
* feat: add `ExclusivePool` client
* fix: filter nil fields in hmset, hset
* feat: switch trust-dns-resolver to hickory-dns
* fix: remove infinitely recursing `From<&Box<[u8]>>` impl
* feat: add PEXPIRE and PEXPIREAT
* feat: add redis+unix from_url scheme
* fix: empty hostname in cluster slots response
* fix: do not ignore invalid client config to cluster on reconnect

---------

Co-authored-by: Cass Fridkin <[email protected]>
Co-authored-by: katul979 <[email protected]>
Co-authored-by: Ivan Prisyazhnyy <[email protected]>
Co-authored-by: Ethra <[email protected]>
Co-authored-by: Shamir Khodzha <[email protected]>
Co-authored-by: Stepan Tubanov <[email protected]>
  • Loading branch information
7 people authored Aug 18, 2024
1 parent 7689bda commit c6d299d
Show file tree
Hide file tree
Showing 108 changed files with 5,135 additions and 1,899 deletions.
31 changes: 21 additions & 10 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,7 @@ commands:
- restore-cargo-deps-cache
- run:
name: Run tests against local valkey deployments
command: |
source tests/environ
USE_VALKEY=1 tests/scripts/install_redis_centralized.sh
USE_VALKEY=1 tests/scripts/install_redis_clustered.sh
tests/runners/default-features.sh
command: source tests/environ && tests/runners/valkey-all-features.sh
- save-cargo-deps-cache

jobs:
Expand Down Expand Up @@ -233,11 +229,12 @@ jobs:
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
VALKEY_VERSION: 7.2.5
steps:
- test_valkey
test-misc:
docker:
- image: cimg/rust:1.77.1
- image: cimg/rust:1.78
environment:
CARGO_NET_GIT_FETCH_WITH_CLI: true
steps:
Expand All @@ -249,7 +246,7 @@ jobs:
command: cargo check --features partial-tracing
check-all-interface-features:
docker:
- image: cimg/rust:1.77.1
- image: cimg/rust:1.78
environment:
CARGO_NET_GIT_FETCH_WITH_CLI: true
steps:
Expand All @@ -259,15 +256,27 @@ jobs:
command: tests/scripts/check_features.sh
clippy-lint:
docker:
- image: cimg/rust:1.77.1
- image: cimg/rust:1.78
environment:
CARGO_NET_GIT_FETCH_WITH_CLI: true
steps:
- checkout
- run:
name: Clippy
command: cargo clippy --all-features --lib -p fred -- -Dwarnings

cargo-fmt:
docker:
- image: cimg/rust:1.78
environment:
CARGO_NET_GIT_FETCH_WITH_CLI: true
steps:
- checkout
- run:
name: Install nightly toolchain
command: rustup toolchain install nightly
- run:
name: Cargo fmt
command: cargo +nightly fmt --all --check

workflows:
version: 2
Expand All @@ -280,9 +289,11 @@ workflows:
- test-redis-stack-7_2
- test-sentinel-7_2
- test-unix-socket-7_2
- test-valkey-7_2
- test-misc
- test-cluster-tls-features-7_2
- test-cluster-rustls-features-7_2
- test-cluster-rustls-ring-features-7_2
- clippy-lint
- check-all-interface-features
- cargo-fmt
- check-all-interface-features
10 changes: 10 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"rust-analyzer.cargo.features": "all",
"rust-analyzer.check.command": "clippy",
"rust-analyzer.rustfmt.overrideCommand": [
"rustup",
"run",
"nightly",
"rustfmt"
]
}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 9.1.0

* Add [RediSearch](https://github.com/RediSearch/RediSearch) interface.
* Adapt testing and CI processes to test Redis and Valkey
* Add `FromIterator` impl to `RedisMap`
* Add `ExclusivePool` client
* Support `redis+unix` config URLs for Unix domain sockets.
* Add `PEXPIRE` and `PEXPIREAT`
* Replace `trust-dns-resolver` with `hickory-resolver`

## 9.0.3

* Fix `bytes_utils` min version
Expand Down
205 changes: 102 additions & 103 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
Contributing
===========
# Contributing

This document gives some background on how the library is structured and how to contribute. It focuses primarily on how
to add new commands. See the [design doc](docs/README.md) for more info.
This document gives some background on how the library is structured and how to contribute.

# General
## General

* Use 2 spaces instead of tabs.
* Run rustfmt and clippy before submitting any changes.
* Formatting should adhere to [rustfmt.toml](./rustfmt.toml), i.e. by running `cargo +nightly fmt --all`
* VS Code users should use the checked-in settings in the [.vscode](./.vscode) directory
* Please use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary).

# File Structure
## File Structure

The code has the following structure:

Expand Down Expand Up @@ -40,112 +39,113 @@ The code has the following structure:

## Examples

## Add A New Command
### Add A New Command

This example shows how to add `MGET` to the commands.

1. Add the new variant to the `RedisCommandKind` enum, if needed.

```rust
pub enum RedisCommandKind {
// ...
Mget,
// ...
}

impl RedisCommandKind {
// ..
```rust
pub enum RedisCommandKind {
// ...
Mget,
// ...
}

pub fn to_str_debug(&self) -> &str {
match *self {
impl RedisCommandKind {
// ..
RedisCommandKind::Mget => "MGET",

pub fn to_str_debug(&self) -> &str {
match *self {
// ..
RedisCommandKind::Mget => "MGET",
// ..
}
}

// ..
}
}

// ..
pub fn cmd_str(&self) -> Str {
match *self {
// ..
RedisCommandKind::Mget => "MGET"
// ..
}
}

pub fn cmd_str(&self) -> Str {
match *self {
// ..
RedisCommandKind::Mget => "MGET"
// ..
}
}

// ..
}
```
```

2. Create the private function implementing the command in [src/commands/impls/keys.rs](src/commands/impls/keys.rs).

```rust
pub async fn mget<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
// maybe do some kind of validation
utils::check_empty_keys(&keys)?;
```rust
pub async fn mget<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
// maybe do some kind of validation
utils::check_empty_keys(&keys)?;

let frame = utils::request_response(client, move || {
// time spent here will show up in traces in the `prepare_command` span
Ok((RedisCommandKind::Mget, keys.into_values()))
})
.await?;
let frame = utils::request_response(client, move || {
// time spent here will show up in traces in the `prepare_command` span
Ok((RedisCommandKind::Mget, keys.into_values()))
})
.await?;

protocol_utils::frame_to_results(frame)
}
```
protocol_utils::frame_to_results(frame)
}
```

Or use one of the shorthand helper functions or macros.
Or use one of the shorthand helper functions or macros.

```rust
pub async fn mget<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
utils::check_empty_keys(&keys)?;
args_values_cmd(client, keys.into_values()).await
}
```
```rust
pub async fn mget<C: ClientLike>(client: &C, keys: MultipleKeys) -> Result<RedisValue, RedisError> {
utils::check_empty_keys(&keys)?;
args_values_cmd(client, keys.into_values()).await
}
```

3. Create the public function in the [src/commands/interfaces/keys.rs](src/commands/interfaces/keys.rs) file.

```rust
```rust

// ...
// ...

pub trait KeysInterface: ClientLike {
pub trait KeysInterface: ClientLike {

// ...
// ...

/// Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the special value nil is returned.
///
/// <https://redis.io/commands/mget>
fn mget<R, K>(&self, keys: K) -> impl Future<Output=RedisResult<R>> + Send
where
R: FromRedis,
K: Into<MultipleKeys> + Send,
{
async move {
into!(keys);
commands::keys::mget(self, keys).await?.convert()
/// Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the special value nil is returned.
///
/// <https://redis.io/commands/mget>
fn mget<R, K>(&self, keys: K) -> impl Future<Output=RedisResult<R>> + Send
where
R: FromRedis,
K: Into<MultipleKeys> + Send,
{
async move {
into!(keys);
commands::keys::mget(self, keys).await?.convert()
}
}

// ...
}
}
// ...
}
```
```

4. Implement the interface on the client structs, if needed.

In the [RedisClient](src/clients/redis.rs) file.
In the [RedisClient](src/clients/redis.rs) file.

```rust
impl KeysInterface for RedisClient {}
```
```rust
impl KeysInterface for RedisClient {}
```

In the [transaction](src/clients/transaction.rs) file.
In the [transaction](src/clients/transaction.rs) file.

```rust
impl KeysInterface for Transaction {}
```
```rust
impl KeysInterface for Transaction {}
```

# Adding Tests
## Adding Tests

Integration tests are in the [tests/integration](tests/integration) folder organized by category. See the
tests [README](tests/README.md) for more information.
Expand All @@ -154,41 +154,40 @@ Using `MGET` as an example again:

1. Write tests in the [keys](tests/integration/keys/mod.rs) file.

```rust
pub async fn should_mget_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> {
let expected: Vec<(&str, RedisValue)> = vec![("a{1}", 1.into()), ("b{1}", 2.into()), ("c{1}", 3.into())];
for (key, value) in expected.iter() {
let _: () = client.set(key, value.clone(), None, None, false).await?;
}
let values: Vec<i64> = client.mget(vec!["a{1}", "b{1}", "c{1}"]).await?;
assert_eq!(values, vec![1, 2, 3]);
```rust
pub async fn should_mget_values(client: RedisClient, _: RedisConfig) -> Result<(), RedisError> {
let expected: Vec<(&str, RedisValue)> = vec![("a{1}", 1.into()), ("b{1}", 2.into()), ("c{1}", 3.into())];
for (key, value) in expected.iter() {
let _: () = client.set(key, value.clone(), None, None, false).await?;
}
let values: Vec<i64> = client.mget(vec!["a{1}", "b{1}", "c{1}"]).await?;
assert_eq!(values, vec![1, 2, 3]);

Ok(())
}
```
Ok(())
}
```

2. Call the tests from the [centralized server tests](tests/integration/centralized.rs).

```rust
mod keys {
// ..
centralized_test!(keys, should_mget_values);
}
```rust
mod keys {
// ..
centralized_test!(keys, should_mget_values);
}

```
```

3. Call the tests from the [cluster server tests](tests/integration/clustered.rs).

```rust
mod keys {
// ..
cluster_test!(keys, should_mget_values);
}
```
```rust
mod keys {
// ..
cluster_test!(keys, should_mget_values);
}
```

These macros will generate test wrapper functions to call your test 8 times based on the following options:

* Clustered vs centralized deployments
* Pipelined vs non-pipelined clients
* RESP2 vs RESP3 protocol modes

Loading

0 comments on commit c6d299d

Please sign in to comment.