0

I installed fork on my Ubuntu Server (using PHP-Apache-Codeigniter), and checked if it's working using var_dump (extension_loaded('pcntl')); and got a "true" output (How to check PCNTL module exists).

I have this code:

public function add_keyword() {

    $keyword_p = $this->input->post('key_word');

    $prod      = $this->input->post('prod_name');
    $prod      = $this->kas_model->search_prod_name($prod);
    $prod      = $prod[0]->prod_id;

    $country   = $this->input->post('key_country');

    $keyword = explode(", ", $keyword_p);
    var_dump($keyword); 
    $keyword_count = count($keyword);
    echo "the keyword count: $keyword_count";

    // Create   fork
    $pid = pcntl_fork();
    if(!$pid){

        for ($i=0; $i < $keyword_count ; $i++) { 

            // Inserts the inputs to the "keywords" table
            $this->kas_model->insert_keyword($keyword[$i], $prod, $country);

            // Gets relevant IDs for the inserted prod and keyword
            $last_inserted_key = $this->kas_model->get_last_rec('keywords');
            $keyword_id        = $last_inserted_key[0]->key_id;
            $prod_id           = $last_inserted_key[0]->key_prod;
            $prod_id_query     = $this->kas_model->get_prod_row_by_id($prod_id);
            $prod_id_a  = $prod_id_query[0]->prod_a_id;
            $prod_id_b  = $prod_id_query[0]->prod_b_id; 

            // Run the keyword query (on API) for today on each one of the keys and insert to DB aslong that the ID isn't 0.  
            if ( ($prod_id_a != 0) || ( !empty($prod_id_a) ) ) {
                $a_tdr = $this->get_var1_a_by_id_and_kw( $prod_id_a, $keyword[$i], $country);
            } else {
                $a_tdr['var1'] = 0;
                $a_tdr['var2'] = 0;
                $a_tdr['var3'] = 0;
            }

            if ( ($prod_id_b != 0) || ( !empty($prod_id_b) ) ) {
                $b_tdr = $this->get_var1_b_by_id_and_kw($prod_id_b, $keyword[$i], $country);
            } else {
                $b_tdr['var1'] = 0;
                $b_tdr['var2'] = 0;
                $b_tdr['var3'] = 0;     
            }

            $this->kas_model->insert_new_key_to_db($keyword_id, $a_tdr['var1'], $b_tdr['var1'], $a_tdr['var2'], $b_tdr['var2'], $a_tdr['var3'], $b_tdr['var3']);

        }
        exit($i);
    }


    // we are the parent (main), check child's (optional)
    while(pcntl_waitpid(0, $status) != -1){
        $status = pcntl_wexitstatus($status);
         // echo "Child $status completed\n";
        redirect('main/kas'); 
    }

    redirect('main/kas'); 
}

What the function does?

This function gets 1 or more keyword/s, a country var, and a product ID, and runs a query on an external slow API getting variables (runs other functions from within that same controller), and adds them to the database.

Problem: When running this function, and if I insert a lot of keywords, the page loads, and loads, and loads, for a long time, until it's done, and only then - I can continue browsing my website. So I was told to fork it since it's just sending a request to process it in the background, so whenever clicking the submit button, I get redirected to "main/kas".

Currently: I don't get redirected but the function runs without any errors.

I was told that it suppose to work but it's not - so I'm guessing I am doing something wrong within the code (?), or something else isn't working from within the server (???). This is my first time working with fork so I don't know a lot of how to operate with in (in syntax or from within the server).

Can you please help me debug the problem?

Community
  • 1
  • 1
Imnotapotato
  • 5,308
  • 13
  • 80
  • 147
  • Does it enter the child at all? – Murillio4 Apr 19 '16 at 12:38
  • Yes it does! It does all the process but just don't redirect and junp to the parent. I still get the form stuck until it finishes to load everything, and it doesn't redirect to `main/kas` and stated in the code :X – Imnotapotato Apr 19 '16 at 12:40
  • I saw some examples of fork and I think almost all of them where within the for loop and not outside of it, does that matter in any way? or i CAN run a whole for look within a fork task? – Imnotapotato Apr 19 '16 at 12:42
  • You are waiting for the child process to finish befor redirecting. Is that what you want? – Murillio4 Apr 19 '16 at 12:43
  • no, whenever submitting the form i want it to instantly redirect and enable browsing the website while the processes are running in the background. – Imnotapotato Apr 19 '16 at 12:45
  • If you do that u are in the risk of having a zombie process tho – Murillio4 Apr 19 '16 at 12:53
  • I'm not sure what's a zombie process but i got an error from within my model: MySQL server has gone away `INSERT INTO 'keywords' ('key_word', 'key_prod', 'kay_country', 'key_is_wr') VALUES ('var', '17', 'US', 1)` what tthe hell – Imnotapotato Apr 19 '16 at 12:56
  • I tried getting the forking inside the for loop and I get that same error <. – Imnotapotato Apr 19 '16 at 12:59
  • "a zombie process or defunct process is a process that has completed execution but still has an entry in the process table" - Wikipedia. And now you cant accsess the MySQL server? – Murillio4 Apr 19 '16 at 13:00
  • You can try doing this tho: http://stackoverflow.com/questions/3833013/continue-php-execution-after-sending-http-response – Murillio4 Apr 19 '16 at 13:03
  • Using queue? Yes someone told me something about that and it's like cheating. So what actually happened in this case? after running the first query, and doing the first process adding a var/s to the db (which btw, did move only one var) - it lost connection because it lost the DB connection var from CodeIgniters system (since it moved to the server and runs externally) ? – Imnotapotato Apr 19 '16 at 13:14
  • Did you use the while loop waiting for the child? if not you need to do `if($pid){Put code here}`, so you are in the parent. And there are some problems with using fork and connecting to db, more about it here: http://stackoverflow.com/questions/3668615/pcntl-fork-and-the-mysql-connection-is-gone – Murillio4 Apr 19 '16 at 13:22
  • @Murillio4 almost there! I found this: http://www.php.net/manual/en/function.pcntl-fork.php#70721 so I added `$this->load->database();` (https://ellislab.com/codeigniter/user-guide/database/connecting.html) right after `if(!$pid){` and I don't get the error anymore, but now nothing is inserted to the DB I tried `$this->db->reconnect();` and it's not solving the problem too: `Message: Call to a member function real_escape_string() on boolean---Filename: mysqli/mysqli_driver.php` – Imnotapotato Apr 19 '16 at 14:16
  • I tried `} else if ( $pid ) {// $this->load->database(); $this->db->reconnect();}` and it's not working too. – Imnotapotato Apr 19 '16 at 14:34
  • If you do `if ( $pid )` then you get into the parent thread. Why do you want to do `$this->load->database(); $this->db->reconnect();` in the parent? So you have to find a way to check that the child don't become a zombie and redirect to the place u want to go. OR dont bother checking the child and just do `else if($pid){redirect('main/kas'); }`. – Murillio4 Apr 19 '16 at 15:05
  • About the db connection, think you have to create a new connection and not reload the connection created in the main thread. I'ts the only explenation i can come up with – Murillio4 Apr 19 '16 at 15:13
  • I removed the WHILE and now it's really redirecting to 'main/kas' the thing is that it's disconnecting from the DB - so following the link you sent me i found this in one of the comments: http://www.php.net/manual/en/function.pcntl-fork.php#70721 which says that I have to reconnect within the parent and that's how I wont lose the connection. I tried the reconnect function and `$this->load->database();` and it's not working. I pasted the code I continued after the if to show you `else if ($pid){ ..`which is the parent - and it's not working. any idea why? – Imnotapotato Apr 19 '16 at 15:18
  • You could try running it as a deamon. A process that lays in the background waiting for data. See: http://kvz.io/blog/2009/01/09/create-daemons-in-php/ – Murillio4 Apr 19 '16 at 15:25
  • What parameters did you send with the `$this->load->database(Parameters)` function? https://ellislab.com/codeigniter/user-guide/database/connecting.html – Murillio4 Apr 19 '16 at 15:49
  • Duplicate, http://stackoverflow.com/questions/8414964/php-fork-unexpected-behaviour-parent-process-waits-till-the-child-process-exits – Stian Skjelstad Apr 20 '16 at 08:01
  • @Murillio4 from what I know, `$this->load->database();` gets variables that are already set in the database config file of CodeIgniter, and if I want to use the database in a controller, I include `$this->load->database();` in the construct function. If I call it again, should I set variables in it? – Imnotapotato Apr 20 '16 at 10:32
  • @Murillio4 I commented a solution. Thanks A LOT my dude. I learnt new stuff with your help! :) – Imnotapotato Apr 21 '16 at 11:24

1 Answers1

0

http://www.electrictoolbox.com/mysql-connection-php-fork/

Reason for the error The parent and child processes all share the same database connection. When the first child process exits it will disconnect from the database, which means the same connection all processes are using will be disconnected, causing any further queries to fail.

The solution The solution is to disconnect from the database before forking the sub processes and then establish a new connection in each process. The fourth parameter also should be passed to the mysql_connect function as "true" to ensure a new link is established; the default is to share an existing connection is the login details are the same.

The question is!

Is that efficient to connect to the server in the child and if there are any other alternative ways to do this better.

Imnotapotato
  • 5,308
  • 13
  • 80
  • 147