This easy code eats system memory and kill VM - is there a way to protect against such memory leaks?

I think the thing to realise that there is actually just ONE function perform/1. While it may look like two functions each with its own def ... do ...end there is only function with two clauses. When you call that function pattern matching is used to select which clause is used and selection is done in the order of the clauses which is why in this case the first clause always wins.

6 Likes

my mistake! I know the different between tuples and struts, that was typed in a hurry.

I think this is a much rarer problem nowadays, not so much because Erlang changed but because tools and practices have advanced a lot. In the “old days” where you would write an application from scratch, it was quite possible to overload the logger, or create a server that doesn’t limit concurrent connections, or build an async processing pipeline where the message queue can grow unbounded
 but nowadays popular libraries mostly do the right thing under the hood. Elixir’s built-in logger switches to synchronous mode if its queue grows too much, Cowboy limits concurrent connections, GenStage takes care of backpressure, etc.

Chapter 3 of Erlang in Anger deals with some of the possible problems if you’re curious.

For your specific case max_heap_size (which was added in 2016) should work, but I don’t think it’s common to use it in production. Infinite recursion like this would show up easily in testing anyway.

3 Likes
  1. The 2 perform function clauses both take 1 argument (pay attention to the curly braces in the second clause).
  2. The first perform is a “catch-all” clause, and it makes the second perform clause unable to be called.

So the consequence is that when you call perform("http://..."),
it calls perform({"http://...", 0}),
which in turn calls perform({{"http://...", 0}, 0})
which in turn calls perform({{{"http://...", 0}, 0}, 0})



4 Likes

I don’t think we need any more explanations about how the functions work, that has been done to death already. :slight_smile:

Thank You! You explained in a nice way.