A Redis client for Elixir built on top of Redix with connection pooling via Poolboy.
- High-level API wrapping all Redis commands
- RedisJSON support - Full API for JSON document operations
- Connection pooling with configurable size and overflow
- Support for multiple named pools
- Custom socket options including TLS/SSL
- Lua script support with automatic loading
- Comprehensive error handling with telemetry
- Pipeline support for batch operations
Add exredis
to your list of dependencies in mix.exs
:
def deps do
[
{:exredis, "~> 0.1.0"}
]
end
# config/config.exs
config :exredis,
url: "redis://localhost:6379/0",
pool: [
size: 10,
max_overflow: 20
]
config :exredis,
url: "redis://localhost:6379/0",
pool: [
name: :my_redis_pool,
size: 10,
max_overflow: 20
],
socket_opts: [
:binary,
:inet6,
{:send_timeout, 5000},
{:recv_timeout, 10000},
{:keepalive, true}
]
config :exredis,
url: "rediss://localhost:6380/0",
socket_opts: [
{:certfile, "/path/to/cert.pem"},
{:keyfile, "/path/to/key.pem"},
{:cacertfile, "/path/to/ca.pem"},
{:verify, :verify_peer},
{:server_name_indication, 'redis.example.com'}
]
# Set and get
{:ok, :ok} = Exredis.set("key", "value")
{:ok, "value"} = Exredis.get("key")
# Increment
{:ok, 1} = Exredis.incr("counter")
# Hash operations
{:ok, 1} = Exredis.hset("user:1", "name", "Alice")
{:ok, "Alice"} = Exredis.hget("user:1", "name")
{:ok, %{"name" => "Alice"}} = Exredis.hgetall("user:1")
# Set operations
{:ok, 1} = Exredis.sadd("tags", "elixir")
{:ok, 1} = Exredis.scard("tags")
# Sorted sets with scores
{:ok, _} = Exredis.zadd("leaderboard", 100, "player1")
{:ok, [{"player1", 100}]} = Exredis.zrangewithscores("leaderboard", 0, -1)
Exredis supports all RedisJSON commands for working with JSON documents. Note: Requires Redis with the RedisJSON module loaded.
# Set JSON document
{:ok, :ok} = Exredis.json_set("user:1", "$", ~s({"name":"John","age":30,"city":"NYC"}))
# Get JSON document
{:ok, result} = Exredis.json_get("user:1", "$")
# Get specific field
{:ok, result} = Exredis.json_get("user:1", "$.name")
# Increment numeric value
{:ok, _} = Exredis.json_numincrby("user:1", "$.age", 1)
# Array operations
{:ok, :ok} = Exredis.json_set("user:1", "$", ~s({"hobbies":["reading","gaming"]}))
{:ok, _} = Exredis.json_arrappend("user:1", "$.hobbies", ~s("swimming"))
{:ok, length} = Exredis.json_arrlen("user:1", "$.hobbies")
# Object operations
{:ok, keys} = Exredis.json_objkeys("user:1", "$")
{:ok, count} = Exredis.json_objlen("user:1", "$")
# Delete field
{:ok, 1} = Exredis.json_del("user:1", "$.city")
Available JSON commands:
json_set/3
,json_get/1-2
,json_type/1-2
,json_del/1-2
json_mget/2
,json_mset/1
json_numincrby/3
,json_nummultby/3
json_arrappend/3
,json_arrindex/3
,json_arrinsert/4
,json_arrlen/1-2
,json_arrpop/1-3
,json_arrtrim/4
json_objkeys/1-2
,json_objlen/1-2
json_strappend/3
,json_strlen/1-2
json_clear/1-2
,json_merge/3
Many commands support omitting the path parameter to target the document root.
See RedisJSON documentation for detailed command descriptions.
All operations return {:ok, result}
or {:error, %Redix.Error{}}
:
case Exredis.get("key") do
{:ok, value} -> IO.puts("Got: #{value}")
{:error, %Redix.Error{message: msg}} -> IO.puts("Error: #{msg}")
end
commands = [
["SET", "key1", "value1"],
["SET", "key2", "value2"],
["GET", "key1"]
]
{:ok, ["OK", "OK", "value1"]} = Exredis.Runtime.pipeline(commands)
defmodule MyApp.RedisScripts do
use Exredis.Script
# Load from string
defredis_script :increment_by_key, """
local key = KEYS[1]
local amount = ARGV[1]
return redis.call('INCRBY', key, amount)
"""
# Load from file
defredis_script :complex_operation, file_path: "priv/scripts/operation.lua"
end
# Use the script
{:ok, 5} = MyApp.RedisScripts.increment_by_key(["counter"], ["5"])
Exredis supports multiple independent connection pools:
# In your application supervisor
def start(_type, _args) do
children = [
# Primary pool
Exredis.Runtime.supervisor(
url: "redis://localhost:6379/0",
pool: [name: :primary_pool, size: 10]
),
# Secondary pool (different database)
Exredis.Runtime.supervisor(
url: "redis://localhost:6379/1",
pool: [name: :secondary_pool, size: 5]
)
]
Supervisor.start_link(children, strategy: :one_for_one)
end
# Use specific pool
Exredis.Runtime.command(["GET", "key"], :secondary_pool)
Exredis supports three pool registration types:
# Local registration (default)
pool: [name: :my_pool]
# Global registration
pool: [name: {:global, :my_pool}]
# Via registration (with Registry)
pool: [name: {:via, Registry, {MyApp.Registry, :my_pool}}]
Exredis supports all Erlang socket options including:
:binary
,:inet6
- Atom flags{:packet, :raw}
- Packet mode{:send_timeout, ms}
- Send timeout{:recv_timeout, ms}
- Receive timeout{:keepalive, true}
- TCP keepalive (added automatically)
{:sni_fun, fun, state}
- SNI callback{:user_lookup_fun, fun, init_state}
- User lookup callback
Socket options from multiple sources are merged intelligently:
- URL-parsed options (e.g., TLS from
rediss://
) - User-provided options override URL options
keepalive: true
added automatically if not present
This fork includes several critical fixes and improvements:
- Fixed:
defredis
macro now properly handles Redis error responses instead of crashing withMatchError
- Fixed: Lua script execution handles all error types, not just
NOSCRIPT
- Improved: All operations return
{:ok, result} | {:error, %Redix.Error{}}
- Added: Support for multiple named connection pools
- Added: Support for
:global
and:via
pool registration - Fixed: Pool name configuration synchronization across sources
- Fixed:
Application.put_env
only called once inApplication.start/2
- Fixed: Socket options are now properly merged instead of replaced
- Fixed: TLS/SSL options from URLs are preserved
- Fixed: Mixed list format (atoms + tuples) correctly handled
- Added: Support for 3+ element tuples (e.g.,
{:sni_fun, fun, state}
) - Added: Automatic
keepalive: true
option (can be overridden)
- Fixed: Test configuration uses correct keys (
:url
,:pool
) - Fixed: Reply handler functions (
int_reply
,sts_reply
) restored - Fixed:
hgetall
properly returns maps instead of lists
- Added: Comprehensive test suite covering:
- Basic operations (set, get, hash, sorted sets)
- Error handling (type mismatches, non-existent keys)
- Pool configuration (local, global, via registration)
- Socket options (mixed formats, 3-element tuples, TLS)
- Multi-pool support
All Redis commands are available as functions. See the generated documentation for the complete list.
# Execute raw command
Exredis.Runtime.command(["GET", "key"], pool_name \\ nil)
# Execute pipeline
Exredis.Runtime.pipeline(commands, pool_name \\ nil)
# Get current pool name
Exredis.Runtime.get_pool_name()
# Generate supervisor child spec
Exredis.Runtime.supervisor(opts)
Bug reports and pull requests are welcome.
MIT License