Austinb / GameQ

A PHP Gameserver Status Query Library
https://austinb.github.io/GameQ/
GNU Lesser General Public License v3.0
403 stars 134 forks source link

Memory Consumption #56

Closed aknock closed 11 years ago

aknock commented 11 years ago

Hi,

I use your library in a PHP daemon to have informations about my game servers. But after 2/3 days, the daemon use 53 percent of memory (1 go) and i need to restart it.

I've checked that it's not the daemon but the GameQ library ...

I've thought about sockets or another thing who is not closed properly.

Any idea ?

Thank's a lot. Antoine K

Ruok2bu commented 11 years ago

Hmm, this worries me too as im using it too but on a much larger scale.

When you say php daemon, do you mean a php file that's run non-stop or a file that's called at regular intervals?

aknock commented 11 years ago

A daemon using System_Daemon library so a non-stop running script.

Austinb commented 11 years ago

Can you provide any more information? I cleared as many variables as possible when the loops are finished however it is possible i missed something. Also what version of PHP?

aknock commented 11 years ago

I run it on PHP 5.3.3-7+squeeze14 with Suhosin-Patch (cli)

I've a mysql table with a list of game servers (Ip / port / GameQ protocol) I fetch gameservers and I get gameQ return to put informations on the database.

It's really an infinite loop. Yesterday, i've tried to force the garbage collector to clean memory but it has no effect.

Do you want some specific informations about my configuration ?

Antoine K

Austinb commented 11 years ago

If you provide the script or an overview of how it is being run. I have run GameQ in the past as a daemon but it was run as a parent -> child where the children did all the actual querying using GameQ.

Any info you can provide will help. If you are using the same pattern I assume your zombie children are being killed once they are done and the next loop in the parent happens?

Things like memory leaks are hard to track down so the more info the better.

aknock commented 11 years ago

Here's an overview of my script :

// Garbage collector
gc_enable();

// My Daemon class
$d = new FlexDaemon();

// GameQ instance
$gq = new GameQ();

while (!$d->isDying()) {

    try {
        // Getting the game servers
        $game_servers_q = $d->getGameServers();

        // Foreach GameServer
        while ($game_server = $game_servers_q->fetch(PDO::FETCH_ASSOC)) {

            // Getting informations about the game
            $game_installed  = $this->getGame($game_server);

            // Creating configuration for GameQ
            $servers = array(
                array(
                    'id' => 'GameServer',
                    'type' => $game_installed['gameq_code'],
                    'host' => $game_server['ip_address'].':'.$game_server['port']
                    )
            );

            // Launching the request
            $gq->addServers($servers);
            $gq->setOption('timeout', TIMEOUT); // Seconds
            $gq->setFilter('normalise');
            $gq->setFilter('stripcolor');
            $results = $gq->requestData();
            $gq->clearServers();

            // Now updating the server with GameQ informations
            $d->updateGameServer($game_server, $results);

            unset($servers);
            unset($game_installed);
            unset($game_server);
        }
        unset($game_servers_q);
        unset($game_server);

    } catch (Exception $e) {
        $d->log("[Exception] An Exception has occurred: " . $e->getMessage());
        $d->error(0, "[Exception] An Exception has occurred: " . $e->getMessage());
    }
    // Cleaning memory with the Garbage Collector
    gc_collect_cycles();

    // Relax the system by sleeping for a little bit
    // iterate also clears statcache
    $d->iterate(5);
}

Excuse me, my english level sucks a lot ... But, not really. The child never dies. I launch the daemon directly on my server in CLI. Then the daemon fork a child who take an other identity on the server (To run as an other user).

This child make an infinite loop as shown here. I hope it will help you.

Thanks a lot for watching this ;-)

Antoine K

Austinb commented 11 years ago

As a quick hack can you move the instancing of $gq into the loop and then kill it on the end of each loop? I would move it into the big loop that never ends, so instance after while (!$d->isDying()) { and then unset($gq) before the closing } for that while loop and before the iterate so you kill it while the loop is sleeping.

This is just a quick and dirty test but it should help if the memory issue is specific to gameq or some underlying php call like stream_select().

Please let me know what you find out.

Thanks

Austinb commented 11 years ago

Also just using your code as a guide for speed I would iterate thru the list of servers before adding them to gameq (assuming the list is not too big).

So you can

$servers = array();

while ($game_server = $game_servers_q->fetch(PDO::FETCH_ASSOC)) {
            // Getting informations about the game
            $game_installed  = $this->getGame($game_server);

            // Creating configuration for GameQ
            $servers[] = array(
                array(
                    'id' => 'row_id', // Use the row id or something unique so you can look up this server on the result side.
                    'type' => $game_installed['gameq_code'],
                    'host' => $game_server['ip_address'].':'.$game_server['port']
                    )
            );
}

$gq = GameQ::factory();
... settings here...

$gq->addServers($servers);
$results = $gq->requestData();

foreach($results AS $result)
{
    // You have each array item for each server added here
    // Now just update back into the db using $result['id'] to look up the specific row or you can write a bulk update query as well
}

Its just an idea and depends on how many servers you have each time you run the while loop.

aknock commented 11 years ago

Hi,

I've already tried to set the GameQ instance into the big loop and unset it at end of this loop.

This change has no effect.

I will try to use your sample code to query the GameServers. Thank's for help.

aknock commented 11 years ago

Hi,

I tried with the sample code you give me and it really change anything. The memory consumption of the daemon always grows up.

Actually i must restart the daemon once a day to keep my games servers monitored.

Antoine

Austinb commented 11 years ago

I am not sure. I will have to see if I can setup some test daemons to run for several days and check the memory usage.

Anyone else using GameQ as part of a daemon and having memory usage issues?

Blackskyliner commented 11 years ago

In the end PHP (sadly but true) is really not designed for something like this, each and every PHP update can break demonized - or long time running - php applications. Maybe restart them after some fixed time to circumvent the memory-fails of many php functions is the best idea.

At least those are my experiences with PHP in the long term.

Or monitor them with some cron with 5 minutes delay, like Nagios and Ichinga 'live-monitor' other services since ages. realtime-live-monitoring is in most cases not necessary.

Austinb commented 11 years ago

I am closing this issue since it is more related to php internals and daemons than GameQ itself. If you find memory usage a problem within the actual library (i.e. memory leaks or variables that need to be unset) please create a new issue or send a pull request with your changes.