vibe-d / vibe.d

Official vibe.d development
MIT License
1.15k stars 284 forks source link

Hight memory usage, process doesn't shut down properly #2459

Open ddcovery opened 4 years ago

ddcovery commented 4 years ago

I am studying using vibe.d in place of node and I begun to do some tests

My platform:

The first one is to "stress" the "hello world" example:

Screenshot from 2020-07-26 18-30-40

Screenshot from 2020-07-26 18-32-52

Finally, If I stop runing dub (CTRL+C) and consoles logs "Received signal 2. Shutting down.", but process continues running and I need to kill it (kill -s 9 ...).

If I run the compiled one (without dub) and I try to finish then using CTRL+C (without any firefox request), process logs on console "Received signa 2. Shutting down.", but it doesn't finish properly... I can press CTRL+C repetidely and message repetidely is shown on terminal... but process doesn't stop (and memory continues without recovering).

Screenshot from 2020-07-26 18-37-17

Any sugestion about how to solve the problem?

rikkimax commented 4 years ago

Vibe.d by itself does not interact with the GC (from a quick search).

import core.memory; Basically GC.collect; and a GC.minimize; will get it to decrease in memory usage reflected by the OS.

ddcovery commented 4 years ago

Thanks, How exactly have I to include GC in this code ? I imported the library

import vibe.vibe;
import core.memory;

void main()
{
        auto settings = new HTTPServerSettings;
        settings.port = 8080;
        settings.bindAddresses = ["::1", "127.0.0.1"];
        listenHTTP(settings, &hello);

        logInfo("Please open http://127.0.0.1:8080/ in your browser.");
        runApplication();
}

void hello(HTTPServerRequest req, HTTPServerResponse res)
{
        res.writeBody("Hello, World!");
}

But memory leak continues...

Thank you

rikkimax commented 4 years ago

Is there any way to use vibe.d without GC.

No, that would only make the situation worse.

How GC must be imported and used in the example app to work properly? (explicit call to GC.collect after each res.writeBody() doesn't solve de problem.

Inside of a timer use:

import core.memory;
GC.collect;
GC.minimize;

The one that matters is the GC.minimize; which will tell the GC to give back memory to the OS.

Make sure it has a fairly high delay between each execution, to minimize performance cost to doing this.

ddcovery commented 4 years ago

Thankou rikkimax, but memory usage continues growing...

This is the code now (I suppose that timer must be stopped in some moment, but this is not important for the first test):

import vibe.vibe;
import std.stdio;
void main()
{
  Timer  timer = setTimer(250.msecs, {
    import core.memory;
    GC.collect;
    GC.minimize;
    writeln("Timer tick");
  }, true);

  auto settings = new HTTPServerSettings;
  settings.port = 8080;
  settings.bindAddresses = ["::1", "127.0.0.1"];
  listenHTTP(settings, &hello);

  logInfo("Please open http://127.0.0.1:8080/ in your browser.");
  runApplication();
}

void hello(HTTPServerRequest req, HTTPServerResponse res)
{
        res.writeBody("Hello, World!");
}

After 5-10 seconds of CTRL+F5 on firefox the application memory starts growing and never decreases.

wilzbach commented 4 years ago

Could you try the newly released 0.9.0 version of vibe.d to see whether the issue still persists?

ddcovery commented 4 years ago

The memory problem persists.

This is a gif showing what happens just refreshing pages (clicking on refresh button)... all works properly (firefox/chrome). (note: The "timer tick" message is a timer running GC each 250ms)

Peek 2020-07-27 12-35

And this is the result if I hold CTRL+F5 on Firefox

Peek 2020-07-27 12-37

FeepingCreature commented 4 years ago

Can confirm this issue. We have a Vibe-based service and if I even just hammer a static route with repeated wgets, the memory usage quickly creeps up.

This is a GC failure. If the GC was operating correctly, it should just be reusing the same memory over and over, collect/minimize be damned.

edit: May have hit some sort of steady state around 750MB.. gonna leave it hammering a bit and see if it fills up again.

edit: It's once again creeping upwards. I think sometimes it manages to free a large GC area, and then it spends some time filling it up again. Small leaks adding up by keeping large data structures alive?

edit: Poking at the memory dump with strings seems to show it's largely a long list of HTTP request bodies. May be reasonable if the GC is reusing that memory.

edit: Hm. May be a memory fragmentation issue?

ddcovery commented 4 years ago

May be a problem with SIGPIPE management?

Using CTRL+F5 repetidely causes Firefox to abandon sockets causing server to receive a SIGPIPE when tries to write the response.

vibe.d manages this signal manually (to avoid process termination) but... -Is it possible that something is not correctly managed (i.e. some callback) when a SIGPIPE is raised and function managing the response doesn't end nicely?

edit: From linux write man page https://linux.die.net/man/2/write

EPIPE fd is connected to a pipe or socket whose reading end is closed. When this happens the writing process will also receive a SIGPIPE signal. (Thus, the write return value is seen only if the program catches, blocks or ignores this signal.)

ddcovery commented 4 years ago

Hi wilzbach

I tested today with last version and there is not memory problem. May be I didn't test correctly the version 0.9.0 25 days ago.

I suppose Issue can be closed.

Thanks a lot

FeepingCreature commented 4 years ago

It may be helpful if there was a way, mode or flag to overwrite a request with BADFOOD or whatever on completion, or force-free the region, or maybe mprotect it as PROT_NONE to salt the earth. I suspect we have string pointers into the request object keeping the entire request alive. A "crash on access to old request memory" mode would be handy to ferret them out.

kookman commented 4 years ago

I wonder if #2484 is the root problem here? Would be interesting to re-test with this now fixed.