Skip to content

Instantly share code, notes, and snippets.

@myronmarston
Last active September 23, 2016 17:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save myronmarston/6fd65606246c842c9420460637debf88 to your computer and use it in GitHub Desktop.
Save myronmarston/6fd65606246c842c9420460637debf88 to your computer and use it in GitHub Desktop.
Example of handling errors when using `GenServer.reply/2`
defmodule Registry do
# ...
def handle_call({:execute_against_shard, campaign_id, fun}, from, state) do
{shard_process, state} = find_or_create_shard(campaign_id, state)
ShardProcess.execute_and_reply(shard_process, fun, from)
{:noreply, state}
end
# ...
end
defmodule ShardProcess do
# ...
def handle_cast({:execute, fun, {client_pid, monitor_ref} = reply_to}, state) do
{result, state} = try do
execute_fun(fun, state)
rescue error ->
# This is a bit weird, but is necessary to ensure that the client that
# made the `GenServer.call` is properly notified of the failure and aborts
# immediately (instead of waiting on the timeout). `GenServer.call` sets
# a monitor and, after sending the message, waits for either a response
# or a `:DOWN` message matching the monitor ref. To cause its `receive`
# to match a message (and not just wait for the timeout), we have to send
# a `:DOWN` message matching what it is looking for.
#
# Normally this kind of thing is unnecessary because GenServers typically
# reply from `handle_call` (that is, the process which the client is monitoring)
# but in this case, the client is monitoring the registry but we are replying
# from a ShardProcess. So we have to do a non-standard thing here.
stacktrace = System.stacktrace()
send(client_pid, {:DOWN, monitor_ref, :process, self(), error})
reraise(error, stacktrace)
end
GenServer.reply(reply_to, result)
{:noreply, state}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment