Skip to content

xbinxu/exredis

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Exredis

A Redis client for Elixir built on top of Redix with connection pooling via Poolboy.

Features

  • 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

Installation

Add exredis to your list of dependencies in mix.exs:

def deps do
  [
    {:exredis, "~> 0.1.0"}
  ]
end

Configuration

Basic Configuration

# config/config.exs
config :exredis,
  url: "redis://localhost:6379/0",
  pool: [
    size: 10,
    max_overflow: 20
  ]

Advanced Configuration

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}
  ]

TLS/SSL Configuration

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'}
  ]

Usage

Basic Operations

# 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)

JSON Operations (RedisJSON)

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.

Error Handling

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

Pipeline Operations

commands = [
  ["SET", "key1", "value1"],
  ["SET", "key2", "value2"],
  ["GET", "key1"]
]

{:ok, ["OK", "OK", "value1"]} = Exredis.Runtime.pipeline(commands)

Lua Scripts

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"])

Multiple Pools

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)

Pool Registration Types

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}}]

Socket Options

Exredis supports all Erlang socket options including:

Common Options

  • :binary, :inet6 - Atom flags
  • {:packet, :raw} - Packet mode
  • {:send_timeout, ms} - Send timeout
  • {:recv_timeout, ms} - Receive timeout
  • {:keepalive, true} - TCP keepalive (added automatically)

TLS Options (3+ element tuples)

  • {:sni_fun, fun, state} - SNI callback
  • {:user_lookup_fun, fun, init_state} - User lookup callback

Socket options from multiple sources are merged intelligently:

  1. URL-parsed options (e.g., TLS from rediss://)
  2. User-provided options override URL options
  3. keepalive: true added automatically if not present

Recent Changes

This fork includes several critical fixes and improvements:

Error Handling (v0.1.1)

  • Fixed: defredis macro now properly handles Redis error responses instead of crashing with MatchError
  • Fixed: Lua script execution handles all error types, not just NOSCRIPT
  • Improved: All operations return {:ok, result} | {:error, %Redix.Error{}}

Pool Management (v0.1.1)

  • 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 in Application.start/2

Socket Options (v0.1.1)

  • 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)

Configuration (v0.1.1)

  • 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

Testing (v0.1.1)

  • 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

API Reference

Core Functions

All Redis commands are available as functions. See the generated documentation for the complete list.

Helper Functions

# 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)

Contributing

Bug reports and pull requests are welcome.

License

MIT License

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages