Last active
February 14, 2017 03:43
-
-
Save mager/0ec23e49b70676b5d17fb0e33a99f902 to your computer and use it in GitHub Desktop.
Consul backend for mutex library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule ConsulMutEx.Backends.ConsulBackend do | |
@moduledoc """ | |
Use Hashicorp's Consul KV store to acquire and release locks. | |
[Consul documentation](https://www.consul.io/docs/agent/http/kv.html) | |
""" | |
alias Consul.Session | |
@timeout 1000 | |
@doc """ | |
Initialize this backend. | |
""" | |
@spec init() :: :ok | |
def init() do | |
:ok | |
end | |
@doc """ | |
Acquire a lock. | |
## Arguments: | |
* `key`: A key to identify the lock | |
* `opts`: Options | |
* `acquire`: The Consul session ID of the lock | |
* `max_retries`: Maximum number of retries, defaults to 0. | |
* `cooldown`: Milliseconds to sleep between retries, defaults to 1000. | |
""" | |
@spec acquire_lock(String.t, keyword()) :: {:ok, Lock.t} | :error | |
def acquire_lock(key, opts \\ []) do | |
do_acquire_lock(key, opts, 0) | |
end | |
defp do_acquire_lock(key, opts, retries) do | |
session = create_session() | |
if Consul.Kv.put(key, session, acquire: session) do | |
{:ok, new_lock(key, session)} | |
else | |
if retries < Keyword.get(opts, :max_retries, 0) do | |
:timer.sleep(Keyword.get(opts, :cooldown, @timeout)) | |
do_acquire_lock(key, opts, retries + 1) | |
else | |
:error | |
end | |
end | |
end | |
@doc """ | |
Release a lock. | |
""" | |
@spec release_lock(Lock.t) :: :ok | |
def release_lock(lock) do | |
if Consul.Kv.put(lock.key, lock.session, release: lock.session) do | |
:ok | |
else | |
:error | |
end | |
end | |
@doc """ | |
Verify a lock. | |
""" | |
@spec verify_lock(Lock.t) :: :ok | {:error, any()} | |
def verify_lock(lock) do | |
{:ok, resp} = Consul.Kv.fetch(lock.key) | |
session_id = resp.body | |
|> List.first | |
|> Map.get("Session") | |
cond do | |
session_id == lock.session -> :ok | |
is_nil(session_id) -> {:error, nil} | |
true -> {:error, session_id} | |
end | |
end | |
defp new_lock(key, session) do | |
%ConsulMutEx.Lock{ | |
key: key, | |
owner: self(), | |
session: session | |
} | |
end | |
def create_session() do | |
{:ok, | |
%HTTPoison.Response{body: %{"ID" => session_id}} | |
} = Session.create(%{}) | |
session_id | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment