Skip to content

Instantly share code, notes, and snippets.

@ericgoodwin
Last active August 9, 2016 21:38
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 ericgoodwin/0d7dcd9d0f43c75337dc43a5df0423b2 to your computer and use it in GitHub Desktop.
Save ericgoodwin/0d7dcd9d0f43c75337dc43a5df0423b2 to your computer and use it in GitHub Desktop.
defmodule Search do
use GenServer
@pool_name :search_pool
@client_options [pool: false, timeout: 30_000, recv_timeout: 5_000, with_body: true]
defmodule Config do
defstruct uri: nil, index: nil, client: nil, headers: [], transport: :hackney_ssl_transport
@type t :: %Config{
uri: URI.t,
index: binary,
client: any, # Hackney client
headers: list, # Default HTTP headers to include on requests
transport: atom # Transport to use in the Hackney client
}
end
@doc "Supervisor child spec for a pool of search workers"
def child_spec do
pool_options = [
name: {:local, @pool_name},
worker_module: __MODULE__,
size: 5,
max_overflow: 0
]
:poolboy.child_spec(@pool_name, pool_options, [])
end
# ----------------------------------------------------------------------------
# GenServer Setup
# ----------------------------------------------------------------------------
def start_link([]) do
GenServer.start_link(__MODULE__, [])
end
def init(_) do
Process.flag(:trap_exit, true)
{:ok, get_config()}
end
def terminate(_, %Config{client: nil}), do:
:ok
def terminate(_, %Config{client: client}) do
:hackney.close(client)
:ok
end
# ----------------------------------------------------------------------------
# Public Interface
# ----------------------------------------------------------------------------
def index(path, body) do
:poolboy.transaction(@pool_name, &GenServer.call(&1, {:index, path, body}, @timeout_ms), @transaction_timeout_ms)
end
# ----------------------------------------------------------------------------
# GenServer Callbacks
# ----------------------------------------------------------------------------
def handle_call({:index, path, body}, _, state = %Config{index: index}) do
{:ok, _, _, new_state} = do_request(:put, path, body, state)
{:reply, :ok, new_state}
end
# ----------------------------------------------------------------------------
# Private Functions
# ----------------------------------------------------------------------------
@spec do_request(method, binary, map | binary, Config.t) :: response
defp do_request(method, path, body, state) when is_map(body), do:
do_request(method, path, Poison.encode!(body), state)
defp do_request(method, path, body, state) do
%{headers: headers, client: client} = new_state = maybe_connect(state)
case :hackney.send_request(client, {method, path, headers, body}) do
{:ok, status, _headers, body} ->
{:ok, status, Poison.Parser.parse!(body), new_state}
{:error, :closed} ->
:hackney.close(client)
do_request(method, path, body, %{new_state | client: nil})
{:error, reason} ->
{:error, reason, new_state}
end
end
defp maybe_connect(%Config{uri: uri, client: nil, transport: transport} = state) do
{:ok, client} = :hackney.connect(transport, uri.host, uri.port, @client_options)
%{state | client: client}
end
defp maybe_connect(%Config{} = state) do
state
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment