Packages
gnat
1.10.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.1
1.14.0
1.14.0-rc1
1.14.0-rc0
1.13.1
1.13.0
1.12.1
1.12.0
1.11.1
1.11.0
1.10.2
1.10.2-rc1
1.10.2-rc0
1.10.1
1.10.0
1.9.1
1.9.0
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.7.1
1.7.0
1.6.0
1.5.2
1.5.1
1.5.0
1.4.0
1.3.0
1.3.0-rc0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
0.7.0
0.6.1
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
A nats client in pure elixir. Resilience, Performance, Ease-of-Use.
Current section
Files
Jump to
Current section
Files
lib/gnat/jetstream/pager.ex
defmodule Gnat.Jetstream.Pager do
@moduledoc false
alias Gnat.Jetstream
alias Gnat.Jetstream.API.{Consumer, Util}
@opaque pager :: map()
@type message :: Gnat.message()
def init(conn, stream_name, opts) do
name = "gnat_stream_pager_#{Util.nuid()}"
first_seq = Keyword.fetch!(opts, :from_seq)
consumer = %Consumer{
stream_name: stream_name,
durable_name: name,
ack_policy: :explicit,
ack_wait: 30_000_000_000,
deliver_policy: :by_start_sequence,
description: "Gnat Stream Pager",
opt_start_seq: first_seq,
replay_policy: :instant,
inactive_threshold: 30_000_000_000
}
inbox = Util.reply_inbox()
with {:ok, _config} <- Consumer.create(conn, consumer),
{:ok, sub} <- Gnat.sub(conn, self(), inbox) do
state =
%{
conn: conn,
stream_name: stream_name,
consumer_name: name,
domain: nil,
inbox: inbox,
batch: 10,
sub: sub
}
{:ok, state}
end
end
@spec page(pager()) :: {:page, list(message())} | {:done, list(message())} | {:error, term()}
def page(%{conn: conn, batch: batch} = state) do
opts = [batch: batch, no_wait: true]
with :ok <- Consumer.request_next_message(conn, state.stream_name, state.consumer_name, state.inbox, state.domain, opts) do
receive_messages(state, [])
end
end
def cleanup(%{conn: conn} = state) do
with :ok <- Gnat.unsub(conn, state.sub),
:ok <- Consumer.delete(conn, state.stream_name, state.consumer_name, state.domain) do
:ok
end
end
def reduce(conn, stream_name, opts, initial_state, fun) do
with {:ok, pager} <- init(conn, stream_name, opts) do
page_through(pager, initial_state, fun)
end
end
defp page_through(pager, state, fun) do
case page(pager) do
{:page, messages} ->
new_state = Enum.reduce(messages, state, fun)
page_through(pager, new_state, fun)
{:done, messages} ->
new_state = Enum.reduce(messages, state, fun)
:ok = cleanup(pager)
{:ok, new_state}
{:error, error} ->
{:error, error}
end
end
defp receive_messages(%{batch: batch}, messages) when length(messages) == batch do
{:page, Enum.reverse(messages)}
end
@terminals ["404", "408"]
defp receive_messages(%{sub: sid} = state, messages) do
receive do
{:msg, %{sid: ^sid, status: status}} when status in @terminals ->
{:done, Enum.reverse(messages)}
{:msg, %{sid: ^sid, reply_to: nil} = msg} ->
IO.inspect(msg)
{:done, Enum.reverse(messages)}
{:msg, %{sid: ^sid} = message} ->
with :ok <- Jetstream.ack(message) do
receive_messages(state, [message | messages ])
end
end
end
end