mikunn

mikunn

Cowboy memory spike when sending JSON as iodata through Plug

I’m trying to figure out what might cause Cowboy to consume a lot of memory when sending JSON data (about 3.5 megs) as iodata through Plug, but with JSON string the memory footprint is very small.

I have a schema with around 10 fields and three of “many” type of associations as a list of around 12.2k items. I noticed that when I do render(conn, "index.json", fairly_large_list: list) in the controller and make a request, Observer shows that cowboy_clear:connection_process/4 consumes almost 50 megs of memory when running cowboy_http:loop/1.

So I started to dig a bit deeper and replaced the standard render with the following

json =
  Phoenix.View.render_to_iodata(MyView, "index.json", %{fairly_large_list: list})

conn
  |> Plug.Conn.put_resp_header("content-type", "application/json")
  |> Plug.Conn.send_resp(200, json)

Running the request again shows the same memory consumption. However, when I change that to

json =
  Phoenix.View.render(MyView, "index.json", %{fairly_large_list: list})
  |> Jason.encode!()
  
conn
  |> Plug.Conn.put_resp_header("content-type", "application/json")
  |> Plug.Conn.send_resp(conn.status || 200, json)

Then the Cowboy process maxes to around 40k of memory.

So basically, when I send an iolist, Cowboy consumes 1000 times more memory with iolist than if I send a string. I took response compression off, but it didn’t have any effect. The frontend is directly connected to Cowboy.

Any ideas? Thanks!

Marked As Solved

NobbZ

NobbZ

We know nothing about how the iolist is actually structured, though lists, especially with many elements, can explode pretty quickly.

On a 64 bit machine a list with 10 elements will take 8 byte for the empty list, 80 bytes for the individual “boxes” and the space of the elements itself.

If those are lists as well (as iolists can be arbitrary nested).

So, [[['a']]] which is 4 lists of length 1, 4 empty lists and a small integer will take 4 * 8 + 4 * 8 + 8 = 9 * 8 = 72 bytes for a single resulting <<?a>> after “writing” it out.

Similar binaries have an overhead of 3 to 6 bytes per binary for the fat pointer reaching either into the process heap or the binary heap. Actual data might be or might not be shared.

Also Liked

NobbZ

NobbZ

Binaries of a certain size are not stored in the process, but on a special binary heap, therefore the consumption will remain roughly the same, it just won’t be accounted to the process.

NobbZ

NobbZ

You can use :erts_debug.size/1 and flat_size/1 to get actual size in words, depending on your architecture you need to multiply them with either 4 or 8 to get actual byte sizes.

Where Next?

Popular in Questions Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
yawaramin
In the Dialyzer docs ( dialyzer — OTP 29.0.2 (dialyzer 6.0.1) ), there is a way to turn off a specific warning for a function: -dialyzer...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

We're in Beta

About us Mission Statement