kevy-client
A URL-driven, blocking RESP client for kevy. The same code switches between an in-process backend and a remote TCP server by changing one URL string.
- Pure Rust, zero
crates.ioruntime dependencies. - Backends: in-process (anonymous, named, or persistent), kevy server, any Redis-protocol server.
- All five Redis data types, transactions with
WATCH, scan iteration, and pub/sub with a borrowing iterator.
use Connection;
let mut conn = open?;
conn.set?;
assert_eq!;
# Ok::
Install
URL backends
| URL | Backend |
|---|---|
mem:// |
Anonymous in-process, per-open fresh; no shared bus. |
mem://<name> |
Shared in-process bus keyed by <name> — two opens of the same name share one Store + pub/sub bus, even across threads. |
file:///abs/path |
Shared in-process with snapshot + AOF persistence in path. |
kevy://host:port |
TCP RESP server, kevy-native URL scheme. |
redis://host:port |
TCP RESP server, standard Redis URL. |
tcp://host:port |
TCP RESP, raw — no SELECT round-trip on connect. |
redis://user:pass@host (AUTH) and rediss:// (TLS) are rejected up
front — kevy ships without either. Front it with a TLS-terminating
sidecar and an authentication proxy if you need them.
Quick start
Same code, two backends
use Connection;
let url = var
.unwrap_or_else;
cache_smoke?;
# Ok::
Set KEVY_URL=mem://app for dev, KEVY_URL=kevy://prod:6379 for
production. No code change.
Transactions
use Connection;
#
For optimistic concurrency, watch a key before the transaction:
use Connection;
#
Transactions on the in-process backends return ErrorKind::Unsupported
— every Connection method already serialises on the embed mutex, so
MULTI's locking guarantee is a no-op in-process.
Pub/sub
use ;
#
Pattern subscriptions use psubscribe(&[&b"news.*"[..]]).
Subscriber::messages() and Subscriber::events() are borrowing
iterators. The messages() form auto-skips the
(p)?(un)?subscribe ack frames and yields (channel, payload) tuples
directly. Both iterators terminate on UnexpectedEof; transient
errors surface as Some(Err(_)) so callers decide whether to keep
going.
Anonymous mem:// (no name) is rejected by Subscriber::open —
no other producer can reach it. Use mem://<some-name> for a shared
bus.
Cluster-aware routing
ClusterClient discovers the topology of a cluster-mode kevy server
and routes each key straight to the owning shard, eliminating the
cross-shard forwarding hop:
use ClusterClient;
let mut cc = connect?; // any shard port as seed
cc.set?; // routed by CRC16
let v = cc.get?;
let removed = cc.del?; // multi-key may span shards
# Ok::
Full cluster-mode guide: docs/cluster.md.
Drop down to the raw backend
use Connection;
#
API surface
Connection / generic: ping, dbsize, flush, type_of,
exists, del, expire, persist, ttl_ms.
Strings: set, set_with_ttl, get, incr, incr_by.
Hashes: hset, hget, hdel, hlen, hgetall, hkeys,
hvals.
Lists: lpush, rpush, lpop, rpop, llen, lrange.
Sets: sadd, srem, smembers, scard, sismember.
Sorted sets: zadd, zrem, zscore, zcard, zrange.
Multi-key: mget, mset, sinter, sunion, sdiff.
Keyspace iteration: keys(pattern),
scan(cursor, pattern, count), randomkey. In-process backends
finish in one round (any non-zero cursor returns empty); the remote
backend honours the server's real cursor.
Transactions (remote only): Connection::multi returns a
Transaction. Queue commands via the typed builders (set, get,
del, exists, incr, incr_by, mget, mset), commit with
exec, exec_typed, exec_watched, or exec_watched_typed. The
typed cursor (exec_typed) hands back a TransactionReplies with
next_ok / next_int / next_bulk / next_array_of_bulks /
expect_empty.
Pub/sub: Connection::publish for the producer side;
Subscriber for the consumer side, with recv, recv_message,
subscribe, unsubscribe, psubscribe, punsubscribe, and the
messages() / events() iterators.
Cluster routing: ClusterClient::connect discovers topology and
routes by CRC16 slot.
Async mirror
For applications already running on tokio, smol, or async-std,
use kevy-client-async
— the API surface mirrors kevy-client exactly, with .await on
every call.
License
MIT OR Apache-2.0, at your option.