Jump to content

Problem with hooks


poposito

Recommended Posts


Hello,

 

I'm having a big problem with a module that I've been developing. The main problem is that the code is been executed twice instead of once. The goal of this code is that it should be executed only once after 3 minutes since the last execution (something similar to this: http://doc.prestashop.com/pages/viewpage.action?pageId=5374186)

 

The problem is that the user is executing this hook four times in a row, so, after calling the code to be executed, a new hook function is started and the values are not updated. Is there a better way to do this ? 

 

Note: This function is been used to change several order values.

 

 

public function hookBackOfficeFooter($params)

{

$fecha = Configuration::get($this->rename . 'LAST_EXECUTE');

$dif = $this->getTimeDif($fecha);

 

if (((int) ($dif['min']) > 3 ) )

{

$fecha = Configuration::get($this->rename . 'LAST_EXECUTE');

$dif = $this->getTimeDif($fecha);

/*Ticket is always set to 1 in this test*/

if (Configuration::get($this->rename . 'TICKET') == 1 && ((int) ($dif['min']) > 3 ))

{

Configuration::updatevalue($this->rename . 'LAST_EXECUTE', date("Y-m-d H:i:s") );

$fecha = Configuration::get($this->rename . 'LAST_EXECUTE');

sleep(30); /*Code to be executed */

}

Configuration::updateValue($this->rename . 'LAST_EXECUTE', date("Y-m-d H:i:s") );

$fecha = Configuration::get($this->rename . 'LAST_EXECUTE');

}

 

 

 

Link to comment
Share on other sites

the function should always execute after 3 minutes, or at most every 3 minutes?

 

in the second case try something like this

public function checkTime($minutes){
    $time = time();
    $lastupdated =  Configuration::get('LASTUPDATE');
    if ($lastupdated < ($time-(60*$minutes))) { //3 minutes have passed
       Configuration::updateValue('LASTUPDATE', $time);
       return true;
    }
    return false;
}

public function hookBackOfficeFooter() {
   $minutes = 3;
    if ($this->checkTime($minutes)) {
        //EXECUTE YOUR FUNCTION HERE
    }
}
Link to comment
Share on other sites

Hello, 

 

I've tried the code, but the problem remains. The main problem is that the hook is executed twice at the same time, so the if doesn't work properly, the code is executed more than once. I need the code to be executed once after 3 minutes, but only once.

 public function checkTime($minutes)
     {
        $time = time();
        $this->writeToFileParam('CheckTime value -> $time=' . $time .''); 
        $lastupdated =  Configuration::get($this->rename . 'LAST_EXECUTE'); 
        if ($lastupdated < ($time-(60*$minutes))) { //3 minutes have passed
           Configuration::updateValue($this->rename .'LAST_EXECUTE', $time);
           return true;
        }
        return false;
    }

    public function hookBackOfficeFooter($params) 
    {
        $minutes = 3;        
        $fecha = Configuration::get($this->rename . 'LAST_EXECUTE'); 
        $this->writeToFileParam('Entering the function with:-> $fecha=' . $fecha .''); 
                
        $this->writeToFileParam('Before the IF clause:-> $fecha=' . $fecha .''); 
       
        if ($this->checkTime($minutes))
        {                         
            $this->writeToFileParam('Executing the code'); 
            sleep(30);
            $fecha = Configuration::get($this->rename . 'LAST_EXECUTE'); 
            $this->writeToFileParam('After executing the info -> $fecha=' . $fecha .'');        
        }
        else
            $this->writeToFileParam('Else hookBackOfficeFooter :-> No code executed');       
    }

The logs are:

 

05-10-2014 21:06:09 Entering the function with:-> $fecha=1412535487
05-10-2014 21:06:09 Before the IF clause:-> $fecha=1412535487
05-10-2014 21:06:09 CheckTime value -> $time=1412535969
05-10-2014 21:06:09 Executing the code
05-10-2014 21:06:10 Entering the function with:-> $fecha=1412535487
05-10-2014 21:06:10 Before the IF clause:-> $fecha=1412535487
05-10-2014 21:06:10 CheckTime value -> $time=1412535970
05-10-2014 21:06:10 Executing the code
05-10-2014 21:06:10 Entering the function with:-> $fecha=1412535487
05-10-2014 21:06:10 Before the IF clause:-> $fecha=1412535487
05-10-2014 21:06:10 CheckTime value -> $time=1412535970
05-10-2014 21:06:10 Executing the code
05-10-2014 21:06:39 After executing the info -> $fecha=1412535969
05-10-2014 21:06:40 After executing the info -> $fecha=1412535970
 
 
Edited by poposito (see edit history)
Link to comment
Share on other sites

first of all is very strange the hook is called twice, there is a problem somwhere, a double call to the hook or a loop in the code, you can try to lock it with a variable, untested, but something like:

private $executing = false;

public function checkTime($minutes)
{
if ($this->executing )
   return false;
$this->executing = true;
$time = time();
$this->writeToFileParam('CheckTime value -> $time=' . $time .'');
$lastupdated = Configuration::get($this->rename . 'LAST_EXECUTE'); 
if ($lastupdated < ($time-(60*$minutes))) { //3 minutes have passed
Configuration::updateValue($this->rename .'LAST_EXECUTE', $time);
$this->executing = false;
return true;
}
return false;
}

public function hookBackOfficeFooter($params) 
{
$minutes = 3;
$fecha = Configuration::get($this->rename . 'LAST_EXECUTE');
$this->writeToFileParam('Entering the function with:-> $fecha=' . $fecha .'');

$this->writeToFileParam('Before the IF clause:-> $fecha=' . $fecha .''); 

if ($this->checkTime($minutes))
{
$this->writeToFileParam('Executing the code');
sleep(30);
$fecha = Configuration::get($this->rename . 'LAST_EXECUTE');
$this->writeToFileParam('After executing the info -> $fecha=' . $fecha .''); 

}
else
    $this->writeToFileParam('Else hookBackOfficeFooter :-> No code executed'); 
}

or try playing with the $executing position, anyway I think there is something wrong somwhere else

Link to comment
Share on other sites

Thx Mistero, but it doesn't work. How to execute the hook twice? While you have the list of orders, click several time in different orders (in a short period of time) to check the orderDetail. That's how you can execute the hook several times. I

 

t seems like if the code is executed in parallel, so the updated variables were not updated until the function is finished. I don't know how to execute the hook once after 3 minutes or more since the last time.

Link to comment
Share on other sites

but that is still very odd, I have some modules that make similar checks, for example checking for updates once every hour at most, I can put the variable to something like 30 seconds, an it still works even refreshing and reloading the page very fast, I even tried opening 2 tabs in the browser and reload them both at the same time, only one fire the function. no matter how fast you are.

 

what is in the variable : $this->rename? did you checked what values you have in database?

 

and why you have a sleep(30) in there?

Edited by misthero (see edit history)
Link to comment
Share on other sites

Hi, 

 

$this->rename is the prefix for all the variables, all the tables. Is the name of the company ;)

 

The reason why I have sleep(30) is a way to simulate a task that is too heavy like the real code. I don't know the reason why several time the hook is activated, that's the issue. I have been testing in prestashop 1.4 because our main client have this. 

 

Is there any way to block the amount of times a hook is executed in a row?

 

 

PS: If you want to simulate the situation:

 

  • Orders -> Show all orders
  • Press in more than one order with the mouse. -> Try to press as fast as you can.
Edited by poposito (see edit history)
Link to comment
Share on other sites

you cannot prevent the hook from launching, you can only control your code attached to that hook.

 

I understood what is happening and in my testing I cannot reproduce it (at least is not executed twice with my functions), but I have not your full code, and not working with 1.4.

 

maybe you can add some more condition, for example limiting the pages where the user should be when the function is executed.

$controller = Tools::getValue('controller');
$orderId = Tools::getValue('id_order', 'not_set');

if ($controller == 'AdminOrders' && $orderId == 'not_set' ){
   //execute only if watching order list page...
} 

I will look more into it tonight, now I'm at work, if you find the cause of the problem let me know, I'm curious.

Link to comment
Share on other sites

Hello,

 

There's a partial solution with a Session Control variable. But, there's another problem. Due to the fact that the function is way too long a MySQL error is launched, so the Session get caught. I'm stil investigating how to do this. Thx in advanced.

Link to comment
Share on other sites

yep using sessions could work but if your purpose is executing the funcions once, no matter how many users are logged in that will not work.

 

One more thing, what are you triyng to do exactly, does it needs to be triggered by users? if it is some heavy query woudn't be better using a cron?

Edited by misthero (see edit history)
Link to comment
Share on other sites

I did a little module and in my testing it works, try it yourself is attached

 

it will write a file in the module dir everytime you visit a backoffice page, but at most every 15 second and you can see a text telling it it was launched or not.

 

it use a timer AND a lock both stored in database, the lock depends from the sleeping function it unlocks only when it finishes. The lock is not really necessary, but is a good option if your function lasts more than the timeout and you don't want it to be executed if the previous one is still working.

 

the sleeping function sleep 10 seconds, but you can increase it.

 

You will see the file is never written more often than 15 seconds.

 

I tested it opening 2 browser and 2 browser tabs for each one, every tab on the same page, than I used a plugin for firefox to reaload both tabs every 5 seconds and on chrome I was clicking randomly

 

timetest.zip

Edited by misthero (see edit history)
Link to comment
Share on other sites

  • 6 years later...

It's an old post, but for people searching for the same thing, here's what I use to avoid this.
Note that I haven't taken much time to find the cause of this double execution, but I do know that my module is the only module listening to that hook.
Also I know my module isn't listed twice for the same hook in the ps_hook_module table.

Instead of using a session, I use the context which is available in your yourmodule.php file

if (isset($this->context->controller->blockDoubleStockUpdateExecution))
    return true;
else
    $this->context->controller->blockDoubleStockUpdateExecution = true;

Hope it helps someone.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...