Issue solved. Actually it was not an problem with Drab, but with the Demo page. I will describe it, as it was quite an interesting case.
On the demo page there is a part which shows you /var/log/nginx/access.log
in a realtime (to show how the server-side events can manipulate User Interface). This functionality uses Sentix which is a wrapper around command-line fswatch
. This is a demo, so the code should be easiest possible, not have to be efficient or sophisticated.
This weekend I moved everything (not only Drab, also some other projects) to the new server, running Debian. The old one was running on 10-years old Mac Mini (OSX Lion). Movement was quite smooth, everything worked so after few tests Iāve updated the DNS.
Then I realized that the beam
process of Drab Demo eats a memory, and it is growing very fast. Strange, as the same stuff worked without any issue on OSX. I recompiled Erlang and Elixir to the exact same version as on the old machine, without success. So I started :etop
and tried to analyze whatās going on internally. And Iāve found the massive (hundreds of thousands and quickly growing) messages queued on some Drab processes.
The only difference now was fswatch
- on OSX I used kqueue
, on Linux - inotify
. And bingo - that was the issue. The difference is that kqueue
monitors file updates only (by default) and inotify
is more complex: it can monitor every access to the file (describing it as :platform_specific).
The code of the demo page is simple, it runs an infinite loop and waits for messages when fswatch
find that the file was accesses:
Sentix.start_link(:watcher, ["/var/log/nginx/access.log"], monitor: :intotify_monitor, latency: 1)
Sentix.subscribe(:watcher)
file_change_loop(socket, file)
defp file_change_loop(socket, file_path) do
receive do
{_pid, {:fswatch, :file_event}, {^file_path, _opts}} ->
socket |> update(:text, set: last_n_lines(file_path, 8), on: "#log_file")
end
file_change_loop(socket, file_path)
end
It worked perfectly on OSX, because fswatch
sent :updated
message only when log file was changed. But on Linux, by default, it sents :platform_specific
message also when nginx access the file without updating itā¦
So what happened: when access log file was accesses/updated/touched, process received and message andā¦ sent websocket message back (with socket |> update(:text, set: last_n_lines(file_path, 8), on: "#log_file")
) to update #log_file
div. While doing it, Nginx access the log file, which issue fsevent
- and it finished with the classic positive feedback.
Solution was very simple, I just need to monitor only :update
events from fswatch
:
Sentix.start_link(:watcher, [file], monitor: monitor, latency: 1, filter: [:updated])
What is very interesting in this situation is that the server survived it and was able to handle all requests. Until it crash few hours later, because of the exhausted memory