Skip to content

Commit

Permalink
9.3.0 (#301)
Browse files Browse the repository at this point in the history
* fix: prevent active_connections from blocking
* feat: add SETNX, ECHO, TYPE, EXPIRETIME, PEXPIRETIME, HTTL, HPTTL, HEXPIRE, HPEXIRE, HEXPIREAT, HPEXPIREAT, HEXPIRETIME, HPEXPIRETIME, HPERSIST

---------

Co-authored-by: AverageADF <[email protected]>
Co-authored-by: ArtemIsmagilov <[email protected]>
  • Loading branch information
3 people authored Oct 20, 2024
1 parent ffcb21a commit 4413043
Show file tree
Hide file tree
Showing 47 changed files with 829 additions and 104 deletions.
66 changes: 33 additions & 33 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,106 +129,106 @@ commands:
- save-cargo-deps-cache

jobs:
test-default-nil-types-7_2:
test-default-nil-types-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
steps:
- test_default_nil_types_features
test-default-7_2:
test-default-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
steps:
- test_default_features
test-unix-socket-7_2:
test-unix-socket-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
steps:
- test_unix_socket
test-redis-stack-7_2:
test-redis-stack-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
steps:
- test_redis_stack
test-no-features-7_2:
test-no-features-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
steps:
- test_no_features
test-all-features-7_2:
test-all-features-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
steps:
- test_all_features
test-cluster-tls-features-7_2:
test-cluster-tls-features-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
FRED_CI_TLS: true
steps:
- test_tls_cluster
test-cluster-rustls-features-7_2:
test-cluster-rustls-features-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
FRED_CI_TLS: true
steps:
- test_rustls_cluster
test-cluster-rustls-ring-features-7_2:
test-cluster-rustls-ring-features-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
FRED_CI_TLS: true
steps:
- test_rustls_cluster_ring
test-sentinel-7_2:
test-sentinel-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
steps:
- test_sentinel
test-valkey-7_2:
test-valkey-7:
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: true
resource_class: medium
environment:
REDIS_VERSION: 7.2.4
REDIS_VERSION: 7.4.0
VALKEY_VERSION: 7.2.5
steps:
- test_valkey
Expand Down Expand Up @@ -308,18 +308,18 @@ workflows:
version: 2
build:
jobs:
- test-default-7_2
- test-all-features-7_2
- test-no-features-7_2
- test-default-nil-types-7_2
- test-redis-stack-7_2
- test-sentinel-7_2
- test-unix-socket-7_2
- test-valkey-7_2
- test-default-7
- test-all-features-7
- test-no-features-7
- test-default-nil-types-7
- test-redis-stack-7
- test-sentinel-7
- test-unix-socket-7
- test-valkey-7
- test-misc
- test-cluster-tls-features-7_2
- test-cluster-rustls-features-7_2
- test-cluster-rustls-ring-features-7_2
- test-cluster-tls-features-7
- test-cluster-rustls-features-7
- test-cluster-rustls-ring-features-7
- clippy-lint
- cargo-fmt
- check-all-interface-features
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 9.3.0

* Add `SETNX`, `ECHO`, `TYPE`, `EXPIRETIME`, and `PEXPIRETIME` commands
* Add hashmap expiration commands (`HTTL`, `HEXPIRE`, etc)
* Change `active_connections` to preempt reconnections

## 9.2.1

* Fix docs.rs documentation features
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ name = "fred"
readme = "README.md"
repository = "https://github.com/aembke/fred.rs"
rust-version = "1.75"
version = "9.2.1"
version = "9.3.0"

[package.metadata.docs.rs]
# do not show the glommio version of the docs
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ begin with `i-` and control which public interfaces are built.
| `i-redisearch` | | Enable a [RediSearch](https://github.com/RediSearch/RediSearch) interface. |
| `i-redis-stack` | | Enable the [Redis Stack](https://github.com/redis-stack) interfaces (`i-redis-json`, `i-time-series`, etc). |

If a specific high level command function is not supported callers can use the `custom` function as a workaround until
the higher level interface is added. See the [custom](https://github.com/aembke/fred.rs/blob/main/examples/custom.rs)
example for more info.

## Debugging Features

| Name | Default | Description |
Expand Down
2 changes: 1 addition & 1 deletion examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async fn main() -> Result<(), RedisError> {
let foo: Option<String> = client.get("foo").await?;
println!("Foo: {:?}", foo);

client
let _: () = client
.set("foo", "bar", Some(Expiration::EX(1)), Some(SetOptions::NX), false)
.await?;

Expand Down
2 changes: 1 addition & 1 deletion examples/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async fn main() -> Result<(), RedisError> {
});

for idx in 0 .. 30 {
publisher_client.rpush("foo", idx).await?;
let _: () = publisher_client.rpush("foo", idx).await?;
sleep(Duration::from_secs(1)).await;
}

Expand Down
8 changes: 4 additions & 4 deletions examples/client_tracking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async fn resp3_tracking_interface_example() -> Result<(), RedisError> {

// enable client tracking on all connections. it's usually a good idea to do this in an `on_reconnect` block.
client.start_tracking(None, false, false, false, false).await?;
client.get("foo").await?;
client.get::<(), _>("foo").await?;

// send `CLIENT CACHING yes|no` before subsequent commands. the preceding `CLIENT CACHING yes|no` command will be
// sent when the command is retried as well.
Expand Down Expand Up @@ -73,7 +73,7 @@ async fn resp2_basic_interface_example() -> Result<(), RedisError> {
.into_iter()
.next()
.expect("Failed to read subscriber connection ID");
client
let _: () = client
.client_tracking("on", Some(connection_id), None, false, false, false, false)
.await?;

Expand All @@ -82,8 +82,8 @@ async fn resp2_basic_interface_example() -> Result<(), RedisError> {

let pipeline = client.pipeline();
// it's recommended to pipeline `CLIENT CACHING yes|no` if the client is used across multiple tasks
pipeline.client_caching(true).await?;
pipeline.incr("foo").await?;
let _: () = pipeline.client_caching(true).await?;
let _: () = pipeline.incr("foo").await?;
println!("Foo: {}", pipeline.last::<i64>().await?);

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion examples/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::convert::TryInto;
async fn main() -> Result<(), RedisError> {
let client = Builder::default_centralized().build()?;
client.init().await?;
client.lpush("foo", vec![1, 2, 3]).await?;
let _: () = client.lpush("foo", vec![1, 2, 3]).await?;

let result: Vec<String> = client.custom(cmd!("LRANGE"), vec!["foo", "0", "3"]).await?;
println!("LRANGE Values: {:?}", result);
Expand Down
6 changes: 3 additions & 3 deletions examples/keyspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ async fn fake_traffic(client: &RedisClient, amount: usize) -> Result<(), RedisEr
for idx in 0 .. amount {
let key: RedisKey = format!("foo-{}", idx).into();

client.set(&key, 1, None, None, false).await?;
client.incr(&key).await?;
client.del(&key).await?;
let _: () = client.set(&key, 1, None, None, false).await?;
let _: () = client.incr(&key).await?;
let _: () = client.del(&key).await?;
}

client.quit().await?;
Expand Down
4 changes: 2 additions & 2 deletions examples/lua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async fn main() -> Result<(), RedisError> {

let hash = fred_utils::sha1_hash(SCRIPT);
if !client.script_exists::<bool, _>(&hash).await? {
client.script_load(SCRIPT).await?;
let _: () = client.script_load(SCRIPT).await?;
}

let results: RedisValue = client.evalsha(&hash, vec!["foo", "bar"], vec![1, 2]).await?;
Expand All @@ -37,7 +37,7 @@ async fn scripts() -> Result<(), RedisError> {

let script = Script::from_lua(SCRIPT);
script.load(&client).await?;
script.evalsha(&client, vec!["foo", "bar"], vec![1, 2]).await?;
let _result: Vec<RedisValue> = script.evalsha(&client, vec!["foo", "bar"], vec![1, 2]).await?;
// retry after calling SCRIPT LOAD, if needed
let (key1, key2, arg1, arg2): (String, String, i64, i64) = script
.evalsha_with_reload(&client, vec!["foo", "bar"], vec![1, 2])
Expand Down
8 changes: 4 additions & 4 deletions examples/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ async fn main() -> Result<(), RedisError> {

// apply custom options to a pipeline
let pipeline = client.pipeline().with_options(&options);
pipeline.get("foo").await?;
pipeline.get("bar").await?;
let _: () = pipeline.get("foo").await?;
let _: () = pipeline.get("bar").await?;
let (_, _): (Option<i64>, Option<i64>) = pipeline.all().await?;

// reuse pipelines
let pipeline = client.pipeline();
pipeline.incr("foo").await?;
pipeline.incr("foo").await?;
let _: () = pipeline.incr("foo").await?;
let _: () = pipeline.incr("foo").await?;
assert_eq!(pipeline.last::<i64>().await?, 2);
assert_eq!(pipeline.last::<i64>().await?, 4);
assert_eq!(pipeline.last::<i64>().await?, 6);
Expand Down
2 changes: 1 addition & 1 deletion examples/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async fn main() -> Result<(), RedisError> {
client.init().await?;

for idx in 0 .. 50 {
client.set("foo", idx, Some(Expiration::EX(10)), None, false).await?;
let _: () = client.set("foo", idx, Some(Expiration::EX(10)), None, false).await?;
}
client.quit().await?;

Expand Down
12 changes: 6 additions & 6 deletions examples/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ async fn main() -> Result<(), RedisError> {
let (first, second): (i64, i64) = pipeline.all().await?;
assert_eq!((first, second), (1, 2));

client.del("foo").await?;
let _: () = client.del("foo").await?;
// or send the pipeline and only return the last result
let pipeline = client.pipeline();
pipeline.incr("foo").await?;
pipeline.incr("foo").await?;
let _: () = pipeline.incr("foo").await?;
let _: () = pipeline.incr("foo").await?;
assert_eq!(pipeline.last::<i64>().await?, 2);

client.del("foo").await?;
let _: () = client.del("foo").await?;
// or handle each command result individually
let pipeline = client.pipeline();
pipeline.incr("foo").await?;
pipeline.hgetall("foo").await?; // this will result in a `WRONGTYPE` error
let _: () = pipeline.incr("foo").await?;
let _: () = pipeline.hgetall("foo").await?; // this will result in a `WRONGTYPE` error
let results = pipeline.try_all::<i64>().await;
assert_eq!(results[0].clone().unwrap(), 1);
assert!(results[1].is_err());
Expand Down
8 changes: 4 additions & 4 deletions examples/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ async fn main() -> Result<(), RedisError> {
// all client types, including `RedisPool`, implement the same command interface traits so callers can often use
// them interchangeably. in this example each command below will be sent round-robin to the underlying 5 clients.
assert!(pool.get::<Option<String>, _>("foo").await?.is_none());
pool.set("foo", "bar", None, None, false).await?;
let _: () = pool.set("foo", "bar", None, None, false).await?;
assert_eq!(pool.get::<String, _>("foo").await?, "bar");

pool.del("foo").await?;
let _: () = pool.del("foo").await?;
// interact with specific clients via next(), last(), or clients()
let pipeline = pool.next().pipeline();
pipeline.incr("foo").await?;
pipeline.incr("foo").await?;
let _: () = pipeline.incr("foo").await?;
let _: () = pipeline.incr("foo").await?;
assert_eq!(pipeline.last::<i64>().await?, 2);

for client in pool.clients() {
Expand Down
2 changes: 1 addition & 1 deletion examples/pubsub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async fn main() -> Result<(), RedisError> {
});

for idx in 0 .. 50 {
publisher_client.publish("foo", idx).await?;
let _: () = publisher_client.publish("foo", idx).await?;
sleep(Duration::from_secs(1)).await;
}

Expand Down
8 changes: 4 additions & 4 deletions examples/replicas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ async fn main() -> Result<(), RedisError> {

// send WAIT to the cluster node that received SET
let pipeline = pool.next().pipeline();
pipeline.set(&key, idx, None, None, false).await?;
pipeline
let _: () = pipeline.set(&key, idx, None, None, false).await?;
let _: () = pipeline
.with_options(&Options {
cluster_hash: Some(cluster_hash),
..Default::default()
})
.wait(1, 10_000)
.await?;
pipeline.all().await?;
let _: () = pipeline.all().await?;

assert_eq!(pool.replicas().get::<i64, _>(&key).await?, idx);
Ok::<_, RedisError>(())
Expand All @@ -80,7 +80,7 @@ async fn lazy_connection_example(client: &RedisClient) -> Result<(), RedisError>
let old_connections: HashSet<_> = client.active_connections().await?.into_iter().collect();

// if `lazy_connections: true` the client creates the connection here
client.replicas().get("foo").await?;
let _: () = client.replicas().get("foo").await?;
let new_connections: HashSet<_> = client.active_connections().await?.into_iter().collect();
let new_servers: Vec<_> = new_connections.difference(&old_connections).collect();
// verify that 1 new connection was created, and that it's in the replica map as a replica of the expected primary
Expand Down
Loading

0 comments on commit 4413043

Please sign in to comment.