alumna-redis
Version, currently 0.1.02 versions
- 0.2.0latestSep 21, 2026
- 0.1.0not indexedSep 21, 2026
github.com/alumna/redis
Redis store for Alumna Backend
Nothing has been indexed for 0.1.0 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.
Installation
# Add this to your shard.yml
dependencies:
alumna-redis:
github: alumna/redis
version: ~> 0.1.0Then run:
shards installshard.yml
No shard.yml has been indexed for 0.1.0. You can read it on the repository.
Dependencies
Unknown: the shard.yml for this version has not been read yet.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
Alumna Redis
Redis stores for the Alumna Backend Framework.
Alumna::Redis holds one Redis client for the process:
redis.cache—Alumna::RedisCache(Alumna::Cache)redis.session_store—Alumna::RedisSessionStore(Alumna::SessionStore)redis.rate_limit_store—Alumna::RedisRateLimitStore(Alumna::RateLimitStore)
See ROADMAP.md.
Table of Contents
- Installation
- Connect
- Key prefixes
- Cache
- Service cache
- Session
- Rate limit
- Errors
- Security
- Testing
- License
1. Installation
Add it to your shard.yml:
dependencies:
alumna:
github: alumna/backend
version: ~> 0.8.0
alumna-redis:
github: alumna/redis
Then run shards install.
Needs Alumna Backend with StoreError on Cache, SessionStore, and RateLimitStore. Default is a single Redis server on port 6379. Pass cluster: true to use Redis Cluster (any node URI). Sentinel is not supported.
Until that backend is published, use gitignored shard.override.yml:
dependencies:
alumna:
path: ../backend
2. Connect
require "alumna-redis"
redis = Alumna::Redis.new(URI.parse(ENV["REDIS_URL"]))
if redis.is_a?(Alumna::Redis::Error)
# Handle the connect failure. The message has no URI userinfo.
else
redis.ping # => "PONG" or Error
end
Alumna::Redis.new accepts a URI or a String. Default topology is single-node Redis::Client. Logical database comes from the URI path (/0).
Redis Cluster is explicit. Pass cluster: true. The URI may be any node. The driver discovers the rest. Cluster uses db 0. A URI path /N does not select a logical database on Cluster.
redis = Alumna::Redis.new(URI.parse(ENV["REDIS_CLUSTER_URL"]), cluster: true)
| Scheme | Transport |
|---|---|
redis:// | TCP |
rediss:// | TLS |
User and password in the URI are Redis AUTH. There is no Unix socket. Sentinel is not supported. Redis Cluster in this driver has no MULTI. The cache, session, and rate-limit ports do not use MULTI.
From the environment (default REDIS_URL). Missing or empty env raises ArgumentError:
redis = Alumna::Redis.from_env
# or:
redis = Alumna::Redis.from_env("REDIS_URL")
# Cluster:
# redis = Alumna::Redis.from_env("REDIS_CLUSTER_URL", cluster: true)
Close the client when the process stops:
redis.close
Use one Alumna::Redis per process. Do not open a client per request.
3. Key prefixes
Redis is often shared. Every port adds a prefix:
| Port | Default prefix |
|---|---|
| Cache | alumna:cache: |
| Session | alumna:sid: |
| Rate limit | alumna:rl: |
You can set a global prefix and override a port prefix:
redis = Alumna::Redis.new(
"redis://127.0.0.1:6379/0",
prefix: "shop:",
cache_prefix: "alumna:cache:",
)
A cache key posts:1 then becomes shop:alumna:cache:posts:1.
Service cache keys from Alumna.cache get a hash-tag around the service path. Then get, find, and collection generation hash to one Cluster slot:
Logical Cache key | Redis key |
|---|---|
alumna:get:/posts:12 | …alumna:cache:{/posts}:get:12 |
alumna:fgen:/posts | …alumna:cache:{/posts}:fgen |
alumna:find:{gen}:/posts:{hash} | …alumna:cache:{/posts}:find:{gen}:{hash} |
This changes keys already stored in Redis under the untagged shape. Session and rate-limit keys stay one key with no hash-tag.
4. Cache
redis.cache is an Alumna::Cache. Values are Bytes. TTL uses Redis SET PX (milliseconds). ttl nil means no expiry. ttl must be greater than 0.
cache = redis.cache
got = cache.get("k")
if got.is_a?(Alumna::StoreError)
# Store down. Not a miss.
elsif got
# hit
end
cache.set("k", "hello".to_slice, 30.seconds)
cache.set_nx("k", "nope".to_slice) # => false or StoreError
cache.delete("k")
cache.incr("gen") # => 1 or StoreError
get copies the byte slice. Mutation of a returned slice does not change Redis.
incr is Redis INCR. A missing key becomes 1. A non-integer value returns Alumna::StoreError. Collection generation keys from Alumna.cache have no TTL. Do not pass a TTL on incr.
5. Service cache
Pass redis.cache to the backend rule. Attach before on :read and after on all methods except options.
require "alumna"
require "alumna-redis"
redis = Alumna::Redis.new(URI.parse(ENV["REDIS_URL"]))
if redis.is_a?(Alumna::Redis::Error)
# Handle the connect failure.
else
rule = Alumna.cache(redis.cache, ttl: 30.seconds)
app.use "/posts", Alumna.memory(PostSchema) {
before rule, on: :read
after rule
}
end
Two processes that share this Redis share get results (write-through on create/update/patch). Find cache is the list that one process loaded. Share find across processes only when those processes also share the document store.
6. Session
redis.session_store is an Alumna::SessionStore. Data is JSON via Alumna::JsonHelper. TTL uses Redis SET PX. TTL is absolute from set. get does not extend it. get returns a shallow copy of the top-level hash.
store = redis.session_store(ttl: 24.hours)
sessions = Alumna::Session.new(store, secure: true)
app.before sessions.rule
# In login:
started = sessions.start(ctx, Alumna.hash(user_id: id))
next Alumna::ServiceError.internal(started.message) if started.is_a?(Alumna::StoreError)
Two processes that share this Redis share the session. Login on instance A. A request on instance B with the same cookie is authenticated.
JSON encode does not keep Time as Time or Bytes as Bytes on read. Typical session fields are strings and integers.
7. Rate limit
redis.rate_limit_store is an Alumna::RateLimitStore. One Redis key per limiter key. Lua INCR + PEXPIRE on the first hit in a window. reset_at comes from PTTL.
The window lives on the store. Alumna.rate_limit(window_seconds:) sets the memory store only when store: is omitted.
store = redis.rate_limit_store(60.seconds)
app.before Alumna.rate_limit(limit: 100, store: store)
Two processes that share this Redis share the counters. One Redis key per limiter key, so the Lua script stays on one Cluster slot.
8. Errors
Alumna::Redis::Error is a struct, not an Exception. Holder new / from_uri / from_env / ping / close return T | Error. Cache, session, and rate-limit port methods return backend Alumna::StoreError on driver failure. The message never includes URI userinfo (user and password).
In the table, Error is Alumna::Redis::Error.
| Method | Type |
|---|---|
new, from_uri, from_env | Alumna::Redis | Error |
ping | String | Error |
close | Nil | Error |
RedisCache#get | Bytes? | StoreError |
RedisCache#set / delete | Nil | StoreError |
RedisCache#set_nx | Bool | StoreError |
RedisCache#incr | Int64 | StoreError |
RedisSessionStore#get | Hash? | StoreError |
RedisSessionStore#set / delete | Nil | StoreError |
RedisRateLimitStore#hit | {Int32, Time} | StoreError |
Cache#get nil is a miss. SessionStore#get nil is no session. StoreError is store down. Alumna.cache / Alumna.session / Alumna.rate_limit map StoreError to ServiceError.internal (HTTP 500).
These calls raise ArgumentError. They do not return Error.
| Mistake | Methods |
|---|---|
| Empty URL | new, from_uri |
| URI that does not parse | new, from_uri, from_env |
| Missing or empty environment variable | from_env |
ttl <= 0 | cache set / set_nx, session set / boot |
window <= 0 | rate-limit store boot |
9. Security
- Do not log the Redis URI. It may contain a password.
Alumna::Redis::Errorstrips//user:pass@from messages.- Put the URI in
REDIS_URLorREDIS_CLUSTER_URL. Do not commit a password. - Use
rediss://when the link is not trusted. - Use one client per process.
10. Testing
Specs need Redis. Set REDIS_URL or use redis://127.0.0.1:6379/0. If Redis is down, the spec process stops with a clear message.
Cluster examples need REDIS_CLUSTER_URL. They are pending when that variable is unset. They fail with a clear message when it is set and Cluster is down.
Start a local Cluster on 127.0.0.1:6380–6385 (3 masters, 1 replica each):
bash script/dev_cluster.sh
REDIS_CLUSTER_URL=redis://127.0.0.1:6380 crystal spec
GitHub Actions:
- Format check
- Specs against one Redis 8.0 on port 6379
- Specs against Redis Cluster (
REDIS_CLUSTER_URL=redis://127.0.0.1:6380) plus standalone 6379 - kcov on
src/(line-rate 1.000) against one Redis on port 6379 only
preview_mt + execution_context runs on the spec jobs.
11. License
MIT
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.1.0- Tagged
- Sep 21, 2026
- Commit
cddace647857- Indexed
- not yet
Dependents
No indexed shard depends on this one yet.
Repository
github.com/alumna/redis
Metadata
- Created
- Sep 22, 2026
- Updated
- Sep 22, 2026
- Synced
- Sep 22, 2026
- Versions
- 2