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

TTR Does not work as advertised. #249

Open
SamMousa opened this issue Jul 10, 2018 · 7 comments
Open

TTR Does not work as advertised. #249

SamMousa opened this issue Jul 10, 2018 · 7 comments
Labels

Comments

@SamMousa
Copy link
Contributor

What steps will reproduce the problem?

Set up any queue with TTR 30.
Push a job that does this:

for ($i = 0; $i < 1000; $i++) {
    echo $i . "\n";
    sleep 1;
}

Run 2 queue runners.

What's expected?

Based on the docs:

The TTR option sets the time to reserve for job execution. If the execution of a job takes longer than this 
time, execution will be stopped and it will be returned to the queue for later retry. 

What do you get instead?

After the 30 seconds have passed the job is released by the queue (beanstalkd).
The second worker picks it up and starts executing it.
The first worker continues to execute it.

There are 2 solutions:

Proposal 1: Fix docs

Fixing the documentation will make it work as expected.

Proposal 2: Fix functionality

There are 2 methods of executing jobs, in-process and isolated process.
In the case of using an isolated process the solution is simple: kill it just before the TTR expires.
In case of in-process execution I have created a proof of concept to handle the TTR:

<?php
declare(ticks=1);

$end = microtime(true) + 5;
$handler = function() use ($end) {
    $remaining = $end - microtime(true);
    echo ".";
    if ($remaining < 0) {
        throw new \Exception('time limit exceeded');
    }
};
register_tick_function($handler);
try {
$i = 0;

    while ($i < 1000) {
        // This handler suppresses errors.
        try {
            echo "\$i is $i\n";
            sleep(1);
            $i++;
        } catch (\Throwable $t) {
//            echo "E: {$t->getMessage()}\n";
        }
    }
} catch(\Throwable $t) {
    unregister_tick_function($handler);
    echo "Caught: {$t->getMessage()}\n";
} finally {
    unregister_tick_function($handler);
}
..$i is 0
....$i is 1
....$i is 2
....$i is 3
....$i is 4
...Caught: time limit exceeded

The idea is simple:

  • Register a tick function that keeps track of time.
  • As soon as time is over we start throwing exceptions.
  • Job code might be catching exceptions, but as soon as their try clause is called we throw another one (this is repeated for nested try..catch).
  • Finally the exception reaches our own catch clause where immediately unregister the tick function.

This way we can guarantee that no 2 workers are executing the same task at any point in time.

@SamMousa
Copy link
Contributor Author

@samdark This is not driver specific.
There is no driver that limits the execution time of a job.
In some drivers (at least beanstalk and db) it results in the job being released to multiple workers.

@acidka
Copy link

acidka commented Jul 10, 2018

@SamMousa there Symfony/Process is used and interrupts after timeout (TTR).
Just tested it - works as expected.

https://github.com/yiisoft/yii2-queue/blob/master/src/cli/Command.php#L183
https://github.com/symfony/process/blob/master/Process.php#L140

What OS you use ?

@SamMousa
Copy link
Contributor Author

@acidka I'm on linux, but I don't use the out of process execution so If you say it works there then I believe you, I didn't check.

I just noted that if it isn't supported there, that case is easy to implement.
For the in-process execution it never works.

@acidka
Copy link

acidka commented Jul 10, 2018

@SamMousa it is because in-process execution runs directly without any control just in try-catch
https://github.com/yiisoft/yii2-queue/blob/master/src/Queue.php#L214

@SamMousa
Copy link
Contributor Author

Yes, I know why it is, I'm just proposing to fix that either by fixing the functionality or by fixing documentation

@cebe
Copy link
Member

cebe commented Aug 8, 2018

related to #229

@githubjeka
Copy link

The "touch" command allows a worker to request more time to work on a job.
This is useful for jobs that potentially take a long time, but you still want
the benefits of a TTR pulling a job away from an unresponsive worker.  A worker
may periodically tell the server that it's still alive and processing a job
(e.g. it may do this on DEADLINE_SOON). The command postpones the auto
release of a reserved job until TTR seconds from when the command is issued.

The touch command looks like this:

    touch <id>\r\n

 - <id> is the ID of a job reserved by the current connection.

There are two possible responses:

 - "TOUCHED\r\n" to indicate success.

 - "NOT_FOUND\r\n" if the job does not exist or is not reserved by the client.

https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt#L328-L345

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

No branches or pull requests

5 participants