
Beş ayrı sistem değil, tek bir process.
redis-cli ile bağlandığın bir Rust veri deposu. Bir key üzerinde atomik karar vermen gereken her iş, o key’in sahibi shard’da, aynı WAL’da çözülüyor.Her backend’in başına gelen hikâye
Bir cache eklersin. Sonra “aynı anda tek worker çalışsın” diye bir lock servisi gelir. API’yi korumak için rate limiter, aynı ödeme iki kez işlenmesin diye idempotency katmanı, gecikmeli işler için bir kuyruk. Her biri ayrı process, ayrı config, ayrı monitoring, ayrı hata noktası.
Oysa hepsi aynı iş: bir key üzerinde atomik karar vermek. Ruvio’nun fikri bu kararı zaten o key’in sahibi olan shard’da vermek. Ayrı sistem kurmak yerine komut çağırıyorsun.
Nasıl çalışıyor: her key’in tek sahibi var
Klasik çok thread’li veri depolarında birden çok thread aynı map’e dokunur ve bedelini kilitlerle öder. Ruvio paylaşımsız çalışıyor: key’ler hash slot ile shard’lara dağılıyor, bir key’i tutan shard onun tek sahibi. Parse, WAL kaydı, uygulama ve cevap aynı thread’de oluyor.
Sonuç: çalışma kümesi o çekirdeğin cache’inde sıcak kalıyor, istekler thread’ler arasında zıplamıyor. Bedeli de saklamayayım: shard’lar arası iş koordinasyon ister, bu yüzden komut başına iş sınırlı tutuluyor.
Tek process’te neler var
Bunların hepsi key’in sahibi shard’da çalışır. Kullanmayan key’in ekstra bellek maliyeti sıfırdır.
LEASE / RELEASESEMAPHOREINCRBY … MAXIDEMXADD … DELAYCHANGES“Yuh” dedirten dört senaryo
1. Aynı anda en fazla 3 import job’ı
# Slot varsa token döner, doluysa nil
SEMAPHORE job:import 3 30000
# İş bitince slotu bırak
RELEASE job:import <token>
Worker çökerse slot 30 saniye sonra kendiliğinden boşalır. Temizlik job’ı yazmana gerek yok.
2. 50 koltuk, 51. satış yok
# Sınırı aşan çağrı nil döner, hiçbir şey değişmez
INCRBY seats:show-7 1 MAX 50
Önce say sonra geri al döngüsü yok. Reddedilen çağrı hiçbir iz bırakmaz.
3. Kullanıcı başına dakikada 100 istek
# Kalan hakkı döner, pencere doluysa nil
LIMIT api:user:42 100 60000
4. Ödeme isteği iki kez çalışmasın
# İlk deneme: ["acquired"]. 24 saat saklanır.
IDEM pay:order-77 BEGIN <fingerprint> <owner-token> 86400000
# İş bitince sonucu yayınla
IDEM pay:order-77 COMPLETE <fingerprint> <owner-token> <result-bytes>
# Aynı istek tekrar gelirse: ["completed", result]
IDEM pay:order-77 BEGIN <fingerprint> <owner-token> 86400000
BEGIN dört cevaptan birini verir:
Not: bu mekanizma tekrarı saklama süresi ve dayanıklılık kadar engeller; dış sistemlere giden yan etkiler kendi idempotency mekanizmanı yine ister.
Kullanmadığın özellik için ödeme yapmıyorsun
Projenin en sert kuralı bu: yeni bir özellik onu kullanmayan key’lere maliyet bindirmez. Lease, semafor, versiyon ve idempotency kaydı ancak ilgili key onları kullanınca bellek ayırır. GET, SET ve INCR sıcak yolda bu durumlara hiç bakmaz.
Dayanıklılığı sen seçiyorsun
Durability modunu başlangıçta açıkça belirliyorsun. Bir yazma önce kontrol edilir, WAL’a eklenir, en son belleğe uygulanır. WAL kaydı alamazsa key eski halinde kalır ve sonraki yazmalar MISCONF döner.
| Mod | Kaybedilebilecek onaylı yazma |
|---|---|
memory | Tüm bellek durumu (process ömrü) |
snapshot | Son kalıcı checkpoint’ten sonraki yazmalar |
everysec | Kernel’in kabul edip henüz sync etmediği yazmalar (varsayılan pencere 1000 ms) |
always | Yalnızca hiç onaylanmamış bir yazma |
Dene: bir dakika
Docker ile ayağa kaldır, redis-cli ile bağlan:
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
Altı dilde resmi client
Hepsi tek seed adresten cluster yönlendirmesini kendisi öğreniyor, tüm 292 komut için tipli metot sunuyor.
cargo add ruvio-clientdotnet add package Ruvio.Clientnpm install ruvio-clientpip install ruvio-clientio.github.salihcantekin:ruvio-clientgo get github.com/salihcantekin/ruvio/clients/gouse 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");
.NET tarafında hazır entegrasyonlar
Operatör arayüzü
Çalışan bir sunucuyu tarayıcıdan gezmek için ayrı bir arayüz imajı var: veri, komut formları, Pub/Sub ve ACL kullanıcıları. Konsol sekmesi komutları doğrudan çalıştırır, yıkıcı komutlardan önce onay ister.



Dürüst olalım
Sık sorulanlar

