r4047 Multi-process parallel 802.1Q sync support
authorAlexey Andriyanov <alan@al-an.info>
Tue, 14 Dec 2010 11:18:57 +0000 (11:18 +0000)
committerAlexey Andriyanov <alan@al-an.info>
Tue, 14 Dec 2010 11:18:57 +0000 (11:18 +0000)
inc/init.php: new function connectDB for reconnecting in childs
syncdomain.php: child workers supports (pcre extension dependent)

upgrade.php: new config variable 'SYNCDOMAIN_MAX_PROCESSES' added
inc/ophandlers.php: idem
install/init-dictbase.sql: idem

ChangeLog
inc/init.php
inc/ophandlers.php
install/init-dictbase.sql
syncdomain.php
upgrade.php

index e39ff8b..1e159a7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,6 +11,7 @@
        bugfix: when searching for IP not belonging to any known network, the ugly assertion failed page was shown.
        update: links to ports added to object search results
        update: custom rearch results provided by users' plugins now supported
+       new feature: syncdomain.php now can create child processes to speed up 802.1Q sync
 0.18.7
        bugfix: adjust 802.1Q command generation
        bugfix: fixed telnet session hanging in NX-OS4 connector
index 66baf1b..c3eed81 100644 (file)
@@ -53,7 +53,23 @@ function showWarning ($info = '', $location = 'N/A')
                echo "Additional information:<br><p>\n<pre>\n${info}\n</pre></p>";
 }
 
-
+// (re)connects to DB, stores PDO object in $dbxlink global var
+function connectDB()
+{
+       global $dbxlink, $pdo_dsn, $db_username, $db_password;
+       $dbxlink = NULL;
+       // Now try to connect...
+       try
+       {
+               $dbxlink = new PDO ($pdo_dsn, $db_username, $db_password);
+       }
+       catch (PDOException $e)
+       {
+               throw new RackTablesError ("Database connection failed:\n\n" . $e->getMessage(), RackTablesError::INTERNAL);
+       }
+       $dbxlink->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+       $dbxlink->exec ("set names 'utf8'");
+}
 
 if (file_exists ('inc/secret.php'))
        require_once 'inc/secret.php';
@@ -67,18 +83,7 @@ else
                RackTablesError::MISCONFIGURED
        );
 }
-
-// Now try to connect...
-try
-{
-       $dbxlink = new PDO ($pdo_dsn, $db_username, $db_password);
-}
-catch (PDOException $e)
-{
-       throw new RackTablesError ("Database connection failed:\n\n" . $e->getMessage(), RackTablesError::INTERNAL);
-}
-$dbxlink->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-$dbxlink->exec ("set names 'utf8'");
+connectDB();
 
 // Magic quotes feature is deprecated, but just in case the local system
 // still has it activated, reverse its effect.
index f72836d..f3bded5 100644 (file)
@@ -1195,6 +1195,7 @@ function resetUIConfig()
        setConfigVar ('HNDP_RUNNERS_LISTSRC', '');
        setConfigVar ('SHRINK_TAG_TREE_ON_CLICK', 'yes');
        setConfigVar ('MAX_UNFILTERED_ENTITIES', '0');
+       setConfigVar ('SYNCDOMAIN_MAX_PROCESSES', '0');
        return buildRedirectURL (__FUNCTION__, 'OK');
 }
 
index 0da19e6..da2e2d0 100644 (file)
@@ -409,6 +409,7 @@ INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, is_userdef
 ('HNDP_RUNNERS_LISTSRC', '', 'string', 'yes', 'no', 'no', 'List of devices running HNDP (RackCode)'),
 ('SHRINK_TAG_TREE_ON_CLICK','yes','string','no','no','yes','Dynamically hide useless tags in tagtree'),
 ('MAX_UNFILTERED_ENTITIES','0','uint','no','no','yes','Max item count to display on unfiltered result page'),
+('SYNCDOMAIN_MAX_PROCESSES','0','uint','yes','no', 'How many worker proceses syncdomain cron script should create'),
 ('DB_VERSION','0.18.5','string','no','yes','no','Database version.');
 
 INSERT INTO `Script` VALUES ('RackCode','allow {$userid_1}');
index 6cbd12c..40e4e15 100755 (executable)
@@ -76,14 +76,40 @@ ftruncate ($fp, 0);
 fwrite ($fp, getmypid() . "\n");
 fclose ($fp);
 
-$switchesdone = 0;
+// fetch all the needed data from DB (preparing for DB connection loss)
+$switch_queue = array();
 foreach ($mydomain['switchlist'] as $switch)
        if (in_array (detectVLANSwitchQueue (getVLANSwitchInfo ($switch['object_id'])), $todo[$options['mode']]))
+               $switch_queue[] = spotEntity ('object', $switch['object_id']);
+
+// YOU SHOULD NOT USE DB FUNCTIONS BELOW IN THE PARENT PROCESS
+// THE PARENT'S DB CONNECTION IS LOST DUE TO RECONNECTING IN THE CHILD
+$fork_slots = getConfigVar ('SYNCDOMAIN_MAX_PROCESSES');
+$do_fork = ($fork_slots > 1) and extension_loaded ('pcntl');
+if ($fork_slots > 1 and ! $do_fork)
+       throw new RackTablesError ('PHP extension \'pcntl\' not found, can not use childs', RackTablesError::MISCONFIGURED);
+$switches_working = 0;
+$switchesdone = 0;
+foreach ($switch_queue as $object)
+{
+       if ($do_fork)
+       {
+               // wait for the next free slot
+               while ($fork_slots <= $switches_working)
+               {
+                       pcntl_waitpid (-1, $wait_status);
+                       --$switches_working;
+               }
+               $i_am_child = (0 === $fork_res = pcntl_fork());
+       }
+       if (! $do_fork or $i_am_child)
        {
-               $object = spotEntity ('object', $switch['object_id']);
                try
                {
-                       $portsdone = exec8021QDeploy ($switch['object_id'], $do_push);
+                       // make a separate DB connection for correct concurrent transactions handling
+                       if ($i_am_child)
+                               connectDB();
+                       $portsdone = exec8021QDeploy ($object['id'], $do_push);
                        if ($portsdone or $verbose)
                                echo "Done '${object['dname']}': ${portsdone}\n";
                }
@@ -91,13 +117,26 @@ foreach ($mydomain['switchlist'] as $switch)
                {
                        echo "FAILED '${object['dname']}': " . $e->getMessage() . "\n";
                }
-               if (++$switchesdone == $max)
-               {
-                       if ($verbose)
-                               echo "Maximum of ${max} items reached, terminating\n";
-                       break;
-               }
+               if ($i_am_child)
+                       exit (0);
+       }
+       if (isset ($fork_res) and $fork_res > 0)
+               ++$switches_working;
+
+       if (++$switchesdone == $max)
+       {
+               if ($verbose)
+                       echo "Maximum of ${max} items reached, terminating\n";
+               break;
        }
+}
+
+// wait for all childs to exit
+while ($switches_working > 0)
+{
+       --$switches_working;
+       pcntl_waitpid (-1, $wait_status);
+}
 
 if (FALSE === unlink ($filename))
 {
index 8b31e89..83fc18f 100644 (file)
@@ -803,6 +803,7 @@ CREATE TABLE `VLANIPv6` (
                        $query[] = 'ALTER TABLE Link ADD COLUMN cable char(64) NULL AFTER portb';
                        $query[] = 'ALTER TABLE RackSpace ADD CONSTRAINT `RackSpace-FK-rack_id` FOREIGN KEY (rack_id) REFERENCES Rack (id)';
                        $query[] = "ALTER TABLE `IPv4Allocation` ADD CONSTRAINT `IPv4Allocation-FK-object_id` FOREIGN KEY (`object_id`) REFERENCES `RackObject` (`id`) ON DELETE CASCADE";
+                       $query[] = "INSERT INTO `Config` (varname, varvalue, vartype, emptyok, is_hidden, description) VALUES ('SYNCDOMAIN_MAX_PROCESSES','0','uint','yes','no', 'How many worker proceses syncdomain cron script should create')";
                        $query[] = "UPDATE Config SET varvalue = '0.19.0' WHERE varname = 'DB_VERSION'";
                        break;
                default: