monitor parser limits and handles demand
This commit is contained in:
1 parent
976fb3fa8c
commit
d591e7479e
1 file changed
+44
-1
@@ -78,10 +78,14 @@ defmodule FeedReader.Parser do
|
|||||||
defp start_parser(input, format, opts, chunk_size) do
|
defp start_parser(input, format, opts, chunk_size) do
|
||||||
parent = self()
|
parent = self()
|
||||||
reference = make_ref()
|
reference = make_ref()
|
||||||
|
max_queue_bytes = queue_limit(opts)
|
||||||
|
|
||||||
{pid, monitor} =
|
{pid, monitor} =
|
||||||
spawn_monitor(fn ->
|
spawn_monitor(fn ->
|
||||||
sink = fn event -> send(parent, {reference, {:event, event}}) end
|
owner_monitor = Process.monitor(parent)
|
||||||
|
|
||||||
|
sink = event_sink(parent, reference, owner_monitor, max_queue_bytes)
|
||||||
|
|
||||||
state = format.new_parser_state(sink, opts)
|
state = format.new_parser_state(sink, opts)
|
||||||
|
|
||||||
result =
|
result =
|
||||||
@@ -100,6 +104,8 @@ defmodule FeedReader.Parser do
|
|||||||
defp next_event(%{done?: true} = state), do: {:halt, state}
|
defp next_event(%{done?: true} = state), do: {:halt, state}
|
||||||
|
|
||||||
defp next_event(state) do
|
defp next_event(state) do
|
||||||
|
send(state.pid, {state.reference, :demand})
|
||||||
|
|
||||||
receive do
|
receive do
|
||||||
{reference, {:event, event}} when reference == state.reference ->
|
{reference, {:event, event}} when reference == state.reference ->
|
||||||
state = if match?({:error, _reason}, event), do: %{state | done?: true}, else: state
|
state = if match?({:error, _reason}, event), do: %{state | done?: true}, else: state
|
||||||
@@ -122,4 +128,41 @@ defmodule FeedReader.Parser do
|
|||||||
Process.demonitor(monitor, [:flush])
|
Process.demonitor(monitor, [:flush])
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp event_sink(parent, reference, owner_monitor, max_queue_bytes) do
|
||||||
|
fn event ->
|
||||||
|
await_demand(parent, reference, owner_monitor)
|
||||||
|
|
||||||
|
if byte_size(:erlang.term_to_binary(event)) > max_queue_bytes do
|
||||||
|
send(
|
||||||
|
parent,
|
||||||
|
{reference, {:event, {:error, {:resource_limit, :max_queue_bytes, max_queue_bytes}}}}
|
||||||
|
)
|
||||||
|
|
||||||
|
exit(:normal)
|
||||||
|
else
|
||||||
|
send(parent, {reference, {:event, event}})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp await_demand(parent, reference, owner_monitor) do
|
||||||
|
receive do
|
||||||
|
{^reference, :demand} -> :ok
|
||||||
|
{:DOWN, ^owner_monitor, :process, ^parent, _reason} -> exit(:normal)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp queue_limit(opts) do
|
||||||
|
default =
|
||||||
|
case Keyword.get(opts, :max_item_bytes, 4_194_304) do
|
||||||
|
value when is_integer(value) and value >= 0 -> value
|
||||||
|
_invalid -> 4_194_304
|
||||||
|
end
|
||||||
|
|
||||||
|
case Keyword.get(opts, :max_queue_bytes, default) do
|
||||||
|
value when is_integer(value) and value >= 0 -> value
|
||||||
|
_invalid -> default
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
Reference in new issue
Block a user