Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GearmanJob::setExceptionCallback() not working #5

Closed
vojta opened this issue Feb 4, 2016 · 8 comments
Closed

GearmanJob::setExceptionCallback() not working #5

vojta opened this issue Feb 4, 2016 · 8 comments

Comments

@vojta
Copy link

vojta commented Feb 4, 2016

This is similar to #3

How to reproduce:

gearman_worker.php:

<?php

error_reporting(E_ALL | E_STRICT);

$fRun = function ($task) {

    $taskData = $task->workload();

    try {

        throw new \UnexpectedValueException("worker exception");

    } catch (\Exception $e) {
            $str = $e->__toString();
            $task->sendException($str);

            return \GEARMAN_ERRNO;
        }

    return GEARMAN_SUCCESS;
};

print "Worker started, pid " . getmypid();

$worker = new GearmanWorker();
$worker->addServer('127.0.0.1', 4730);

$worker->addFunction('myDefaultMethod', $fRun);

$worker->work();
if ($worker->returnCode() != GEARMAN_SUCCESS) {
    print("return code: " . $worker->returnCode() . PHP_EOL);
} else {
    print 'Worker OK' . PHP_EOL;
}

gearman_client.php:

<?php

error_reporting(E_ALL | E_STRICT);

class Client
{
    /**
     * \GearmanClient
     */
    protected $gc;

    public function __construct()
    {
        $gc = new \GearmanClient();
        $gc->addServer();
        $gc->setTimeout(1);

        $this->gc = $gc;
        print 'client created' . PHP_EOL;
    }

    public function ping($str)
    {
        return $this->gc->ping($str);
    }

    public function runJobs()
    {
        print 'runJobs called' . PHP_EOL;

        var_dump($this->gc->setCompleteCallback([$this, 'jobCallback']));
        var_dump($this->gc->setExceptionCallback([$this, 'exceptionCallback']));
        var_dump($this->gc->setFailCallback([$this, 'failCallback']));

        $jobStr = 'abcde';
        print('calling addTask()' . PHP_EOL);

        $gTask = $this->gc->addTask('myDefaultMethod', $jobStr, null, 'uniq1');

        if ($gTask instanceof \GearmanTask) {
            print 'OK' . PHP_EOL;
        }

        print 'runTasks call' . PHP_EOL;
        $this->gc->runTasks();
    }

    public function jobCallback($task)
    {
        print 'jobCallback called' . PHP_EOL;
        var_dump($task->data());

        return GEARMAN_SUCCESS;
    }

    public function exceptionCallback($task)
    {
        print 'exceptionCallback()' . PHP_EOL;
        var_dump($task->data());
        throw new \Exception($task->data());
    }

    public function failCallback($task)
    {
        print 'failCallback()' . PHP_EOL;
        var_dump($task->data());
    }
}



// ============= RUN =============

$bgc = new \Client();

$pingResult = $bgc->ping('xyz');
print 'ping called with result: ' . var_export($pingResult, 1) . PHP_EOL;

$bgc->runJobs();

To reproduce, just start the worker:

yes | xargs -n1 -I {} bash -c "php70 ./gearman_worker.php || echo 'WORKER FAILED'"

and run the client:

$ php70 ./gearman_client.php; echo $?
client created
ping called with result: true
runJobs called
bool(true)
bool(true)
bool(true)
calling addTask()
OK
runTasks call
PHP Warning:  Invalid callback Array, first array member is not a valid class name or object in /home/users/......./gearman_client.php on line 45
PHP Warning:  GearmanClient::runTasks(): Could not call the function [undefined] in /home/users/......./gearman_client.php on line 45
0

It runs OK on PHP 5.5 (calls the exceptionCallback):

$ php ./gearman_client.php; echo $?
client created
ping called with result: true
runJobs called
bool(true)
bool(true)
bool(true)
calling addTask()
OK
runTasks call
exceptionCallback()
string(293) "exception 'UnexpectedValueException' with message 'worker exception' in .......
@wcgallego
Copy link
Owner

both awesome that we have another clear reproduction and a bummer it's still happening.

Thanks for helping out on this @vojta ! I'll take a look soon.

@wcgallego
Copy link
Owner

hey @vojta, just pushed a fix I think takes care of this. Wasn't handling allocation of storing callbacks in zvals correctly, along with some other minor clean up. Give it a shot and lemme know if it works? This might clear up #6 as well (haven't had a change to set that up in a context to test, but fingers crossed).

If something's amiss, reopen this. Will leave #6 open while you test

@vojta
Copy link
Author

vojta commented Feb 8, 2016

It calls the exceptionCallback, but after a few dozen successful requests, I get a segfault:

Program received signal SIGSEGV, Segmentation fault.
0x00000000005e22e1 in _zval_dtor_func ()

@wcgallego
Copy link
Owner

Ah sounds like poorly allocated memory location. I'll hit it a bunch of times and see if I can replicate it. Isolating it to a zval_dtor helps though, thanks!

@wcgallego wcgallego reopened this Feb 8, 2016
@vojta
Copy link
Author

vojta commented Feb 8, 2016

I added #7, might be connected to this one.

@wcgallego
Copy link
Owner

hey @vojta, thanks again for your patience here!

I left notes in the commit, but here's the scoop - with this test function, it looks like it's going to call setCompleteCallback (among other callbacks) over and over again unnecessarily. I wasn't taking that into account! So it was just doing a copy over the previous callback without properly deallocating and cleaning up. Hence, memory leaks! This should be fixed for all the callback methods.

I do agree, this seems very likely linked to #7 as well. It wasn't deallocating the callback and so it was losing lots of memory until it hit the limit. If #6 was having issues allocating memory as well, that might be tied in too.

This is a bug, straight up. It should've handled your code in a cleaner manner. But you might be able to do a little bit less work if you don't set the callback multiple times too! There might be very good reason why you're doing that in your code, so please ignore if that's the case.

As always, if you could test those out again, it would be much appreciated! Going to close this out, but should issues persist please open up again. Thanks for helping make the pecl package better!

@vojta
Copy link
Author

vojta commented Feb 22, 2016

hey @wcgallego , I never call setCompleteCallback() multiple times. There must be some another unfixed problem. I leave this issue ( #5 ) closed but the #7 is still repeatable using the latest version.

@ziyaindia
Copy link

ziyaindia commented May 23, 2020

My gearman client code is as below:

`class FNSGearman {
public $gmclient;
public $taskCount = 0;

public static $results = array();
public static function reset(){
    GearmanResults::$results = array();
}

public static function completeCallback($task) {
    $unique = $task->unique();
    if ($unique != null) {
        FNSGearman::$results[$unique] = $task->data();
    }else {
        array_push(FNSGearman::$results, $task->data());
    }
}

public function addTask($func_name, $arguments, $context = null, $uid = null) {
    $this->taskCount++;
    $this->gmclient->addTask($func_name, $arguments, $context, $uid);
    if ($this->taskCount == MAX_GEARMAN_TASKS) {
        $this->gmclient->runTasks();
        $this->taskCount = 0;
    }
}

public function runTasks() {
    var_dump($this->gmclient);
    if ($this->taskCount > 0) 
    {
        /*echo $this->taskCount;die(' >> In runTasks function');
        if (! $this->gmclient->runTasks())
        {
            echo "ERROR " . $this->gmclient->error() . "\n";
            exit;
        }*/
        try {
             // run the tasks in parallel (assuming multiple workers)
            $this->gmclient->runTasks();
         } catch (GearmanException $e) {
             var_dump($e);
         }
    }
}

public function __construct() {
    echo 'FNSGearman construct called.....';
    $this->gmclient= new GearmanClient();
    /* add the default server */
    //$this->gmclient->addServer('localhost',4730);
    $this->gmclient->addServer('127.0.0.1'); 
    $this->gmclient->setCompleteCallback("FNSGearman::completeCallback");
}

}`
When I call runTasks it hangs my application execution

`$gm = new FNSGearman();

    //$cdt = new FNSDateTime($_SESSION['timezone']);
    //echo "1st timestamp: ".$cdt->dateTime->getTimestamp()."<br>";
    //echo "1st timestamp: ".time()."<br>";

    $tcount = count ( $trackers );
    
    for ($i = 0, $counter = 0; $i < $tcount; $i++, $counter++) {
        $arguments = array('tid' => $trackers[$i], 'ts' => $timestamps, 'session' => $_SESSION);
        //print_r($arguments);
        $gm->addTask('dashboardAnalytics', json_encode($arguments), null, $trackers[$i]);//Commented By Ziya
        
    }
    $gm->runTasks();`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants