From 49ae7b7dc57bac73830625486ceadf5fdffd1e33 Mon Sep 17 00:00:00 2001 From: Michael Ries Date: Sun, 16 Oct 2022 20:49:21 +0100 Subject: [PATCH] handle no_responders with request_multi --- lib/gnat.ex | 23 ++++++++++++++++++----- test/gnat_test.exs | 6 ++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/gnat.ex b/lib/gnat.ex index 70ba7d9..16be10b 100644 --- a/lib/gnat.ex +++ b/lib/gnat.ex @@ -32,7 +32,7 @@ defmodule Gnat do * `tcp_opts` - Options for connecting over TCP * `tls` - If the server should use a TLS connection * `inbox_prefix` - Prefix to use for the message inbox of this connection - * `no_responders` - Used to indicate if you want to get an immediate `:no_responders` return when sending requests to a topic that has no responders registered + * `no_responders` - Enable the no responders behavior (see `Gnat.request/4`) """ @type connection_settings :: %{ optional(:connection_timeout) => non_neg_integer(), @@ -231,8 +231,15 @@ defmodule Gnat do {:error, :timeout} -> :sad_cat end ``` + + ## No Responders + + If you send a request to a topic that has no registered listeners, it is sometimes convenient to find out + right away, rather than waiting for a timeout to occur. In order to support this use-case, you can start + your Gnat connection with the `no_responders: true` option and this function will return very quickly with + an `{:error, :no_responders}` value. This behavior also works with `request_multi/4` """ - @spec request(t(), String.t, binary(), keyword()) :: {:ok, message} | {:error, :timeout} + @spec request(t(), String.t, binary(), keyword()) :: {:ok, message} | {:error, :timeout} | {:error, :no_responders} def request(pid, topic, body, opts \\ []) do start = :erlang.monotonic_time() receive_timeout = Keyword.get(opts, :receive_timeout, 60_000) @@ -269,7 +276,7 @@ defmodule Gnat do Enum.count(responses) #=> 5 ``` """ - @spec request_multi(t(), String.t(), binary(), keyword()) :: {:ok, list(message())} + @spec request_multi(t(), String.t(), binary(), keyword()) :: {:ok, list(message())} | {:error, :no_responders} def request_multi(pid, topic, body, opts \\ []) do start = :erlang.monotonic_time() receive_timeout_ms = Keyword.get(opts, :receive_timeout, 60_000) @@ -285,11 +292,15 @@ defmodule Gnat do end {:ok, subscription} = GenServer.call(pid, {:request, req}) - responses = receive_multi_request_responses(subscription, expiration, max_messages) + result = + case receive_multi_request_responses(subscription, expiration, max_messages) do + {:error, :no_responders} -> {:error, :no_responders} + responses when is_list(responses) -> {:ok, responses} + end :ok = unsub(pid, subscription) latency = :erlang.monotonic_time() - start :telemetry.execute([:gnat, :request_multi], %{latency: latency}, %{topic: topic}) - {:ok, responses} + result end @doc """ @@ -625,6 +636,8 @@ defmodule Gnat do [] true -> case receive_request_response(subscription, timeout) do + {:error, :no_responders} -> + {:error, :no_responders} {:error, :timeout} -> [] {:ok, msg} -> diff --git a/test/gnat_test.exs b/test/gnat_test.exs index a9a6bb4..00242d2 100644 --- a/test/gnat_test.exs +++ b/test/gnat_test.exs @@ -266,6 +266,12 @@ defmodule GnatTest do assert Enum.all?(messages, fn msg -> msg.body == "ohai" end) end + test "request_multi with no_responders" do + topic = "nobody.is.home" + {:ok, pid} = Gnat.start_link(%{no_responders: true}) + assert {:error, :no_responders} = Gnat.request_multi(pid, topic, "ohai", max_messages: 2) + end + defp spin_up_echo_server_on_topic(ready, gnat, topic) do spawn(fn -> {:ok, subscription} = Gnat.sub(gnat, self(), topic)