GenStage's ConsumerSupervisor max_restarts option doesn't work 😄

There’s something really strange going on here, and I’m sure someone will be able to point out a silly mistake somewhere in the code.

I’m using GenStage’s ConsumerSupervisor to subscribe to a Producer:

defmodule UserConsumerSupervisor do
  use ConsumerSupervisor

  @opts [
    strategy: :one_for_one,
    max_restarts: 2,
    subscribe_to: [{Producer, max_demand: 3}]
  ]

  def start_link(_) do
    ConsumerSupervisor.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
    children = [
      %{
        id: UserConsumer,
        start: {UserConsumer, :start_link, []},
        restart: :transient
      }
    ]

    ConsumerSupervisor.init(children, @opts)
  end
end

As specified in the @opts, I’d like the supervisor to restart a consumer only twice, if the consumer fails for whatever reason. The consumer itself is quite simple:

defmodule UserConsumer do

  def start_link(user) do
    Task.start_link(fn ->
      :timer.sleep(3000)
      if user == 3, do: raise "Ooops, error!"
    end)
  end
end

Despite the max_restarts option, when the consumer errors, the consumer process keeps getting restarted forever…

How is this possible? Am I misreading the configurations somehow :thinking:

Link to docs: https://hexdocs.pm/gen_stage/ConsumerSupervisor.html#c:init/1

From your link

:max_restarts - the maximum amount of restarts allowed in a time frame. Defaults to 3 times.

note “time frame”. Right after that one comes

:max_seconds - the time frame in which :max_restarts applies in seconds. Defaults to 5 seconds.

So the supervisor will restart the children up to 2 times (your setting) per 5 seconds. You have a 3 second wait in your start link, so the child will only fail every 3 seconds, and can’t fail twice in 5 seconds. So it will restart forever.

3 Likes

max_restarts is per unit time, as specified by :max_seconds. The default of :max_seconds is 5 seconds, and since you sleep for 3 seconds, you won’t exceed 2 restarts in 5 seconds.

2 Likes

Ahhh yes of course, thank you all!