pgx
PGX — Lightweight Postgres for AI agents. Written in Rust. Thousands of isolated databases in one runtime. 🐘
翻译准备中
In-memory key-value server in Rust that speaks RESP, so redis-cli and Redis clients connect. Locks, rate limits, job queues and a change feed are single commands; Raft-based failover. Clients for Rust, .NET, Go, Java, Python and Node.js.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗The shard that owns a key parses, records, applies, and replies. The hot path does not take a lock and does not hop to another thread.
| Strength | What it means |
|---|---|
| Owner-per-shard engine | One owner per shard, no lock on apply. A feature allocates only when a key uses it: twenty thousand leases added about 3.6 MB, twenty thousand versioned 64-byte strings about 7.2 MB, body included. |
| Durability you name | memory, snapshot, everysec or always, chosen at startup. A write is checked, appended, then applied. If the log cannot take the record, the key stays as it was and later writes return MISCONF. |
| Wire compatible | RESP2, and RESP3 after HELLO 3; redis-cli and existing RESP clients connect. The command list that CI checks is docs/compatibility-matrix.json. |
| Six first-party clients | Rust, C#, Go, Java, Python and Node.js, each with a typed method for every one of the 299 listed commands and cluster routing from one seed address. |
| Operator UI | A browser view of one running server: data, command forms, Pub/Sub and ACL users. |
A default process listens on 127.0.0.1:6379.
docker run --rm -p 6379:6379 \
-e RUVIO_REQUIREPASS=change-me \
-v ruvio-data:/data \
salihcantekin/ruvio:linux
redis-cli -h 127.0.0.1 -p 6379 -a change-me PING
linux and latest are the same Linux image, as is each version tag, built for linux/amd64 and linux/arm64 (Apple Silicon and other arm64 hosts run it natively). The image default durability is memory. A persistent deploy sets RUVIO_DURABILITY and keeps the volume.
Install the prebuilt binary package from the Arch User Repository (AUR):
yay -S ruvio-bin
# or
paru -S ruvio-bin
Start and enable the systemd service:
sudo systemctl enable --now ruvio
Download a binary from GitHub Releases. Each archive holds ruvio (the server), ruvio-ctl (backup, restore and checks), ruvio.example.toml, the README and the license. SHA256SUMS lists the checksums.
# Linux x64; swap the platform for linux-arm64 or macos-arm64
v=0.4.0
curl -LO https://github.com/salihcantekin/ruvio/releases/download/ruvio-$v/ruvio-$v-linux-x64.tar.gz
tar xzf ruvio-$v-linux-x64.tar.gz
cd ruvio-$v-linux-x64
./ruvio --requirepass change-me --data-dir ./data
On Windows, unzip ruvio-<version>-windows-x64.zip and run ruvio.exe. On macOS, a binary downloaded by a browser is quarantined; xattr -d com.apple.quarantine ruvio ruvio-ctl clears it (downloads with curl are not quarantined). ruvio --help lists every flag.
cargo run --release --bin ruvio
redis-cli -h 127.0.0.1 -p 6379 PING
The server is installed from the Docker image, a release binary, or built from this checkout; it is not published as a crate.
Settings merge in this order: built-in defaults, ruvio.toml (template: ruvio.example.toml), RUVIO_* environment variables, then CLI flags. The file is read once, at process start. See Configuration.
These commands run on the shard that owns the key. examples/09_building_blocks_tour.py runs the main ones in a few seconds against a local server; the use cases page shows each job with all six clients. A key that never uses them stores nothing extra. GET, SET, and INCR do not consult them.
| Command | What it replaces a second system for |
|---|---|
LEASE / RELEASE | A fencing token for one key. The shard is the holder. Tokens only grow: after expiry, RELEASE, DEL, a restart, a failover or a slot move, the next grant is larger. The grant is a WAL record. GET of a lease is WRONGTYPE. |
SEMAPHORE / RELEASE | At most N holders at once, each with its own TTL. A crashed holder frees its slot when its TTL passes. The first holder stays in the object; each further holder is 16 bytes. The key goes away with the last one. |
RENEW | Extends a LEASE or SEMAPHORE holder by its token without releasing it, so a long job keeps its slot and its fencing token. |
INCRBY ... MAX | A counter that never passes a capacity. A refused call returns nil and changes nothing, so no compensating DECR. |
SETV | A conditional string write. The call succeeds only when the caller still has the current version, so two clients cannot both turn 12 into 11. The version exists only on keys written with SETV. |
CHANGES | CHANGES START arms an in-memory feed of later SET, SETV, LEASE, and RELEASE notes on that shard. Nothing is recorded before START. The feed is not the WAL, and it is empty after restart. |
XADD DELAY | The stream entry is stored now. XREADGROUP skips it until the delay passes. XREAD can still see it. The hold is a WAL record and a snapshot trailer, and it is written only when a delay is set. |
SET ... IFEQ / IFGT / IFLT, DELEX ... IFEQ | A compare-and-set, a watermark that only moves forward or a delete that checks the value, without a script. A refused call writes nothing. |
ZPOPBYSCORE / ZMOVEDUE | Take the due members of a sorted set, or move them to a list, in one command: the due-job queue without a scheduler. |
LPUSHCAP / RPUSHCAP | Push to a list and trim it to a length in one call: a capped recent-items list. |
SET replaces a versioned string with a plain string. INCR does not accept a versioned string.
| Result | Setup | |
|---|---|---|
| Against Redis 8.10.2 | 59 of 60 commands at or above 0.97×, geometric mean 1.22× | 4-core Azure x86 VM, one shard, memory durability, 50 clients, pipeline 16, 64-byte values, five alternating rounds (2026-10-11) |
| Whole machine, 8 shards | against an 8-node Redis Cluster on the same 8 cores: geometric mean 1.13× over 59 commands at pipeline 16 (55 at or above 0.97×, none below 0.90×), 1.23× at pipeline 64 (GET 1.75×, SET 2.21×, 29M and 30M req/s), 1.14× with every write logged (everysec against AOF everysec); memory per shard 0.96× of one Redis | 16-core Azure EPYC 9V74 VM, one process with 8 shards (port_mode = "per-shard"), one redis-benchmark per shard, three alternating rounds (record, 2026-10-16) |
| Raw throughput | about 2.61M SET/s, 2.74M GET/s, 2.88M INCR/s | Apple silicon Mac, evented, memory durability, one shard, 50 clients, pipeline 16, 64-byte values, fixed key, median of 3 rounds (2026-10-07) |
Samples, flags and the rows below 1× are on the performance page; memory results are on the memory page. There is no Garnet measurement.
First-party clients speak RESP2, do not send CLIENT SETINFO, and learn CLUSTER SLOTS from one seed address. Guides: Clients · clients/.
| Language | Install | Package |
|---|---|---|
| Rust | cargo add ruvio-client | crates.io |
| C# | dotnet add package Ruvio.Client | NuGet |
| Node.js | npm install ruvio-client | npm |
| Python | pip install ruvio-client | PyPI |
| Java | io.github.salihcantekin:ruvio-client | Maven Central |
| Go | go get github.com/salihcantekin/ruvio/clients/go | pkg.go.dev |
use ruvio_client::Client;
let mut db = Client::connect("127.0.0.1", 6379)?;
db.set("session:ada", "hello")?;
let value = db.get_string("session:ada")?;
db.incr("visits")?;
using Ruvio.Client;
await using var connection = await RuvioConnection.ConnectAsync("127.0.0.1", 6379);
var db = await connection.GetDefaultDatabaseAsync();
await db.SetAsync("session:ada", "hello");
string? value = await db.GetStringAsync("session:ada");
The .NET integration packages provide an IDistributedCache provider, ASP.NET Core output caching and Data Protection key-ring storage, plus opt-in rate limiting, HTTP idempotency and distributed locks. They are separate packages; existing server commands and applications that do not enable them incur no new per-key feature state.
Native IDEM adds atomic reservation, ownership checks, fingerprint conflicts and binary result replay for any RESP client. The ASP.NET Core idempotency package uses this command, not Lua. ONCE remains the simpler TTL marker. Retention and durability bound deduplication; external side effects still require their own transaction/idempotency mechanism.
Releases are cut from main by hand (release workflow). Counts come live from the registries. All clients are in clients/. The Go module is released by git tag (clients/go/vX.Y.Z) and fetched through the Go module proxy.
| Package | What it is | Version | Downloads |
|---|---|---|---|
| ruvio-client | Rust SDK | ||
| Ruvio.Client | C# SDK | ||
| Ruvio.Client.AspNetCore | ASP.NET Core registration and health checks | ||
| Ruvio.Extensions.Caching | IDistributedCache provider | ||
| Ruvio.AspNetCore.OutputCaching | Output-cache store with tag invalidation | ||
| Ruvio.AspNetCore.DataProtection | Data Protection key-ring storage | ||
| Ruvio.AspNetCore.RateLimiting | Distributed rate limiting | ||
| Ruvio.AspNetCore.Idempotency | HTTP idempotency | ||
| Ruvio.Extensions.Idempotency | Run work once per key outside HTTP | ||
| Ruvio.AspNetCore.DistributedLock |
Live install lines and download counts: Packages.
salihcantekin/ruvio-ui is a browser for one running server. RUVIO_UI_TARGET_HOST and RUVIO_UI_TARGET_PORT are that Ruvio process, the same address redis-cli uses. A default server is 127.0.0.1:6379. Port 8080 is only the UI.
docker run --rm -p 8080:8080 \
-e RUVIO_UI_TARGET_HOST=127.0.0.1 \
-e RUVIO_UI_TARGET_PORT=6379 \
salihcantekin/ruvio-ui:linux
Open http://127.0.0.1:8080. Sign-in is the server ACL user. An empty user and password connect as default. If the server was started with RUVIO_REQUIREPASS, type that password on the sign-in screen, or set RUVIO_UI_TARGET_PASSWORD so the field is filled in. Tags linux and latest are the same image.
The Console tab runs commands directly, with formatted and size-capped replies, copy and download of the full result, and a confirmation before destructive commands.
All shards lists every key. One shard pins Data, the command screens, and Pub/Sub to that node. Users are ACL identities: default always exists, extra users that survive a restart live in ruvio.toml, and ACL SETUSER lasts until the next start. Install notes: Operator UI.
The differences are about ownership, the durability contract, and how much of the Redis surface is implemented. Measured results are in Performance. How a Ruvio process runs is on How it runs.
| Redis | Dragonfly | Garnet | Ruvio | |
|---|---|---|---|---|
| Runtime | C | C++ | .NET, Tsavorite | Rust, in-memory shard + WAL |
| Wire | RESP2/3, full surface | Redis and Memcached | Redis-compatible | RESP2 subset, listed in CI |
| Hot path | Shared engine, I/O threads | Single-node throughput | Separate string and object stores | One owner per shard, no lock on apply |
| Cluster | Bus, migration, failover | Its own scale story | Cluster-capable store | Static 16,384 slots, misrouted keys forwarded; automatic failover of a primary and its replicas with ruvio coordinator |
| Durability | RDB, AOF, combinations | Snapshots | Tsavorite on disk | memory, snapshot, everysec, always |
| Keep the other when | You need modules, the full Sentinel feature set, or a command Ruvio does not list | You want that single-node scale claim | You want a .NET stack and Tsavorite as the store | The written subset and the shard contract are enough |
Not claimed: a cluster bus, automatic rebalancing (slot ranges move between the shards of one process with RUVIO MOVESLOTS, and between machines when an operator asks ruvio coordinator), failover across a multi-node cluster (failover covers one primary and its replicas, through ruvio coordinator), or a hardware latency number. Replication copies a WAL tail to a bounded set of replicas. Classic PUBLISH reaches subscribers on every shard of one process, not other processes.
The capped Docker Desktop comparison (containers limited to 4 CPUs and 2 GiB, 2026-10-01) is in the 2026-10-07 benchmark record.
Docker RSS is not the bare-metal result; the memory detail is in the string memory density record and on the memory page. Natively on this Mac, 1,000,000 LIMIT keys added 148.9 MiB against 85.9 MiB for 1,000,000 plain 64-byte strings, about 1.7× per key.
| Path | Contents |
|---|---|
src/ | The server. |
clients/ | Rust, C#, Go, Java, Python and Node.js SDKs, and the operator UI. |
integrations/ | .NET framework integrations. |
docs/ | Configuration, compatibility, recovery, threat model. |
site-next/ | The website, ruviodb.com, built with Astro from docs/, clients/ and integrations/ and published by the manual site-azure workflow. |
deploy/ | systemd unit, Kubernetes example, local Docker stack. |
tests/, fuzz/, benches/ | Integration tests, libFuzzer targets, instruction-count benchmark. |
examples/, , |
| Page | Covers |
|---|---|
| Start | Checkout, adapters, Docker |
| Configuration | TOML, environment, ACL users |
| Commands | The supported surface |
| Durability | Acknowledgement boundaries |
| Replication | WAL tail, reads from replicas, REPLICAOF |
| Automatic failover | ruvio coordinator: Raft-agreed failover, Sentinel-compatible discovery |
| Operations | Limits, metrics, shutdown |
| Testing and evidence | Test coverage, recorded results, open production evidence gates |
| docs/THREAT_MODEL.md | What the process does and does not defend |
| docs/ROADMAP.md | Open gates, including a 24-hour soak and published bare-metal latency |
| docs/RECOVERY.md | Promote a caught-up replica by hand |
| docs/RESILIENCE.md |
Read CONTRIBUTING.md before opening a pull request. Report a vulnerability in private as described in SECURITY.md.
The server (src/, the ruvio binary and the server image) is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only). Copyright (c) 2026 Salih Cantekin.
In short: you can run, study and modify the server. If you offer a modified version to others, including as a network service, you must make your modified source available under the same license. Running an unmodified server, and talking to it from your own application, does not put that application under the AGPL.
The operator UI (clients/ruvio-ui/, the ruvio-ui image) is licensed under the AGPL-3.0 as well.
The client libraries and integrations are separate works under the MIT license: the Rust SDK and C# SDK in clients/, and integrations/ (license).
Versions published before the switch to AGPL-3.0 (server 0.3.0 and earlier) remain available under the MIT license to those who received them.
RUVIO HOTKEYS / RUVIO BIGKEYS | Which keys take the traffic and which values are large, sampled; off unless key_stats is on. |
LIMIT | A fixed-window, sliding-window or token-bucket rate limit in one call, with fixed state per key. A refused call writes nothing. |
IDEM | Request de-duplication on the shard that owns the key: BEGIN tells a caller to run, wait or conflict, and a repeat of a finished request gets the saved result. |
FILLGET / FILLSET | One caller refills a missing or stale cache entry while the others get BUSY or the stale value. The markers live in memory only. |
TRANSFER | Moves an amount between two integer strings of one slot in one WAL record, with an optional cap on the destination. |
CHANGES SUBSCRIBE | A resumable push stream of this shard's WAL, with a cursor to continue from after a disconnect or restart. |
JSON.INDEX | A field index over JSON documents with a key prefix, so a lookup by field does not scan. |
CLIENT DURABILITY | Holds this connection's replies until n replicas have the writes on disk; ruvio coordinator fails over only to a replica that has them. |
Distributed locks and [RuvioLock] |
| ruvio-client (npm) | Node.js SDK |
| ruvio-client (PyPI) | Python SDK |
| io.github.salihcantekin:ruvio-client | Java SDK |
| github.com/salihcantekin/ruvio/clients/go | Go SDK | clients/go/v0.2.0 |
| Server binaries | ruvio and ruvio-ctl for Linux, macOS, Windows |
| salihcantekin/ruvio | Server image (amd64, arm64) |
| salihcantekin/ruvio-ui | Operator UI image |
benchmark/| Contributor probes, check and release scripts, the benchmark script (result files stay local). |
ruvio.example.toml | Default server settings; written as ruvio.toml on first start. |
| Crash, power-loss and disk-fault testing and what it found |
| docs/TESTING.md | Contributor test guide |