2

I have a simple GenServer based app that parses a webpage and plays with the data that it got from it. The code looks roughly like this:

def start_link do
  GenServer.start_link(__MODULE__, %{})
end

def init(state) do
  schedule_work(1)
  {:ok, state}
end

def handle_info(:work, state) do
  // Request webpage using HTTPoisson
  // Parse data using Floki
  // Use it

  schedule_work(10 * 60) # Reschedule after 10 minutes
  {:noreply, state}
end

defp schedule_work(timeout) do
  Process.send_after(self(), :work, timeout * 1000)
end

Today the 3rd party site stopped delivering the correct markup for some time and the parsing failed and crashed the GenServer. Supervisor restarted it and it crashed immediatley. After :max_restarts the Supervisor eventually stopped the application.

I don't want simply increase the :max_restarts. Is there a way to tell Supervisor to attempt restarts after certain amount of time? Or should I do more error handling in my parsing code to prevent the process from crashing in the first place (I assume this would be against the "Let it crash" philosophy)?

3
  • 1
    Unfortunately, this is how supervisor behaviour is implemented. It doesn't support delayed restarts, so you might need to implement the custom solution yourself (highly recommended, with this you would learn a lot how to detect failures with monitors or links), or you can use github.com/appcues/gen_retry Commented Jul 10, 2018 at 19:02
  • 1
    You could also try github.com/kbrw/delayed_otp Commented Jul 10, 2018 at 21:37
  • After your suggestions I Googled a bit and found this article about implementing a custom Supervisor: freecontent.manning.com/… I used it to learn about that and implemented my custom Supervisor that does exactly what I wanted. Thank you!
    – slashrsm
    Commented Jul 22, 2018 at 20:15

1 Answer 1

1

I'd say the best thing to do is to both handle errors better and crash better.

You mentioned you didn't want to simply increase the max_restarts option. You could alternatively lower the max_seconds option which defaults to 5.

Also, you could trap exits with Process.flag(:trap_exit, true) and, in case of parsing errors, exit with a specific message (e.g., Process.exit(pid, :bad_parsing) and use that to reschedule. This might need little changes here and there in the way you supervise things, though.

Not the answer you're looking for? Browse other questions tagged or ask your own question.