RedlockEx

Distributed locking mechanism using Redis for BEAM applications.

Description

RedlockEx provides a mechanism to attempt to acquire a distributed lock using Redis.

Why Do I Need RedlockEx?

In distributed environments, such as those managed by Kubernetes, it's common to have multiple replicas of your BEAM application running simultaneously. While this redundancy provides resilience and horizontal scalability, it can also introduce challenges when certain operations need to be executed by only one instance, avoiding redundant or conflicting operations.

For instance, consider tasks like:

Executing these tasks on every instance would be inefficient, redundant, or even potential dangerous. That's where RedlockEx comes in.

By using RedlockEx, you can ensure that specific operations in your BEAM applications are executed only once across the entire distributed setup, no matter how many replicas you have. It leverages Redis as a distributed lock mechanism, ensuring atomicity and consistency in these operations.

Installation

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

def deps do
  [
    {:redlock_ex, "~> 0.1.0"}
  ]
e

Ensure RedlockEx is started before your application:

def application do
  [applications: [:redlock_ex]]
end

Usage

Establishing a Redis Connection

First, establish a connection to your Redis instance. Replace REDIS_HOST and REDIS_PORT with relevant values:

{:ok, conn} = Redix.start_link("redis://REDIS_HOST:REDIS_PORT")

Acquiring the Lock

To attempt to acquire a lock, you can use the attempt_acquire_lock/3 function:

case RedlockEx.attempt_acquire_lock(conn, "my_unique_lock", 60) do
  :acquired -> IO.puts("Lock acquired!")
  :not_acquired -> IO.puts("Lock is held by another process.")
end

Understanding Lock TTL (Time To Live)

In the example above, 60 denotes the TTL of the lock in seconds. This means that if the process that holds the lock crashes or doesn't release the lock for any reason, it will automatically be released after 60 seconds.

This automatic expiration mechanism serves a crucial role:

  1. Preventing Deadlocks: Deadlocks occur when processes permanently compete for resources, causing them to be stuck indefinitely. By having a TTL, we ensure that even if a process holding the lock dies unexpectedly or is delayed, other processes can still eventually acquire the lock. This ensures system progress, making sure no operation is indefinitely halted.
  2. Ensuring Freshness of the Lock: Sometimes, operations might take longer than anticipated. By setting a TTL, the lock's "freshness" is ensured, meaning no process can hold onto a resource for an unreasonably long time without renewing its claim.
  3. Minimizing Resource Starvation: In distributed systems, one process might consistently acquire a lock just a split second before another, leading to "starvation" of the latter. TTL ensures that such monopolies are temporary.

Note: It's essential to set a TTL that makes sense for your application. It should be long enough for the locked operation to complete under normal conditions but short enough to prevent extended resource monopolies or deadlocks.

Configuration

Ensure you have a Redis instance running and reachable at the REDIS_HOST and REDIS_PORT values from above.

Testing

To run tests:

mix test

Contributing

  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request.

License

This project is licensed under the MIT License.