Added locking for add-on database updates and new option to glmMembersConfigArraySetu...
authorChuck Scott <cscott@gaslightmedia.com>
Fri, 21 Dec 2018 21:12:14 +0000 (16:12 -0500)
committerChuck Scott <cscott@gaslightmedia.com>
Fri, 21 Dec 2018 21:12:14 +0000 (16:12 -0500)
classes/glmPluginSupport.php
controllers/admin.php
defines.php
index.php
readme.txt
views/front/error/databaseUpdateInProgress.html [new file with mode: 0644]

index 3db6196..8c40d80 100755 (executable)
@@ -332,7 +332,7 @@ return; // Off for now ** Need to make this switchable in management
  *      )
  * )
  */
-function glmMembersConfigArraySetup( $configTable, $configNumbTable = false, $configSelectedArray = false)
+function glmMembersConfigArraySetup( $configTable, $configNumbTable = false, $configSelectedArray = false, $useNameForKey = false)
 {
 
     // Check if config table array exits
@@ -369,6 +369,16 @@ function glmMembersConfigArraySetup( $configTable, $configNumbTable = false, $co
         }
     }
 
+    if ($useNameForKey) {
+
+        $nameArray = array();
+        foreach($conf as $c) {
+            $nameArray[$c['name']] = $c;
+        }
+        return $nameArray;
+
+    }
+
     return $conf;
 
 }
index 46100ca..8411a50 100755 (executable)
@@ -133,12 +133,6 @@ class glmMembersAdmin extends GlmPluginSupport
         // Save plugin configuration object
         $this->config = $config;
 
-        // Check the database - allow installation of tables for a new add-on
-/*        if (!$this->checkDatabase('install')) {
-            die('Database check failure');
-        }
-*/
-
         /*
          * Check if there's a request to bypass the WordPress Dashboard and
          * display directly to the browser using a specified
index 3b6ed84..4f70ca2 100644 (file)
@@ -91,6 +91,9 @@ define('GLM_MEMBERS_WORDPRESS_FILE_LIBRARY_URL', GLM_MEMBERS_PLUGIN_MEDIA_URL.'/
 global $wpdb;
 define('GLM_MEMBERS_PLUGIN_DB_PREFIX', $wpdb->prefix.'glm_members_');
 define('GLM_MEMBERS_PLUGIN_ACTIVE_DB_OPTION', 'glmMembersDatabaseDbVersion');
+// These two are used to ensure only one instance is doing a database update.
+define('GLM_MEMBERS_PLUGIN_DB_LOCK', 'glmMembersDbLock');
+define('GLM_MEMBERS_PLUGIN_DB_LOCK_TIMEOUT', 30);
 
 // Current Theme Information
 $glmCurrentTheme = wp_get_theme();
index 64253a9..8ecd0c3 100755 (executable)
--- a/index.php
+++ b/index.php
@@ -483,7 +483,7 @@ if ($startupNotices != '') {
  * @return boolean False if some failure
  * @access private
  */
-function glmCheckDatabase ()
+function glmCheckDatabase()
 {
     global $startupNotices, $config, $wpdb;
 
@@ -617,80 +617,140 @@ function glmCheckDatabase ()
             // Otherwise, check if we need to update the database
             } elseif ($dbVersion != $a['database']['dbCurrentVersion']) {
 
-                // Include an admin message that we're updating the database
-                $startupNotices .= '<p>The '.$a['name'].' database tables require updating...</p>';
+                /*
+                 * This part of the code checks to see if another instance is doing the database update.
+                 * If there is, then it won't be done by this instance and this instance will instead
+                 * wait till the database update is done before continuing.
+                 *
+                 * If there's no other instance doing the update, this instance will try to set and
+                 * verify that it is going to do it by setting and verifying a WordPress option.
+                 */
 
-                // Traverse version list to find any required updates
-                $curVerFound = false;
-                $db_setup_status = true;
-                foreach($a['database']['dbVersions'] as $version) {
+                $myId = rand();
 
-                    $ver = $version['version'];
+                // Try to get any current DB Lock
+                $lockName = GLM_MEMBERS_PLUGIN_DB_LOCK."_".$a['slug'];
+                $lock = get_option($lockName);
 
-                    // Find the current version of the database
-                    if ($ver == $dbVersion) {
-    //                    $startupNotices .= '<p>The database version installed for the '.GLM_MEMBERS_PLUGIN_NAME
-    //                            .' plugin is current and does not require updating.</p>';
-                        $db_setup_status = true;
-                        $curVerFound = true;
+                // If another instance is doing a database update
+                // (If we got something back that's a 2 el array that doesn't have my ID and hasn't expired)
+                if ($lock && is_array($lock) && count($lock) == 2 && $lock[0] != $myId && $lock[1] > microtime(true)) {
 
-                        // Otherwise if it's already been found and $ver is not the new target version
-                    } elseif ($curVerFound && $dbVersion != $a['database']['dbCurrentVersion']) {
+                    trigger_error("glmCheckDatabase() - Lock Name: $lockName - Instance: $myId - Another instance is updating the database for: ".$a['short_name']."",E_USER_NOTICE);
 
-                        // Read in Database creation script
-                        $sqlFile = $a['database']['dbScriptPath'].'/update_database_V'.$ver.'.sql';
-                        $sql = file_get_contents($sqlFile);
+                    // Unfortunately things get mucked up for other instances, even if we hold them for a bit, so they go here instead.
+                    $output = file_get_contents(GLM_MEMBERS_PLUGIN_PATH.'/views/front/error/databaseUpdateInProgress.html');
+                    wp_die($output);
 
-                        // Replace {prefix} with table name prefix
-                        $sql = str_replace('{prefix}', $a['database']['dbPrefix'], $sql);
+                } else {
 
-                        // Split script into separate queries by looking for lines with only "---"
-                        $queries = preg_split('/^----$/m', $sql);
+                    trigger_error("glmCheckDatabase() - Lock Name: $lockName - Instance: $myId - This instance trying to secure database update lock for: ".$a['short_name'],E_USER_NOTICE);
 
-                        // Try executing all queries to update database
-                        do {
-                            $q = current($queries);
-                            $wpdb->query($q);
-                            $queryError = $wpdb->last_error;
-                        } while ($queryError == '' && next($queries));
+                    // Add our ID and timeout then wait a second
+                    add_option($lockName, array($myId, (microtime(true)+GLM_MEMBERS_PLUGIN_DB_LOCK_TIMEOUT)));
+                    sleep(1);
 
-                        // Check for PHP script to update database
-                        $phpScript = $a['database']['dbScriptPath'].'/update_database_V'.$ver.'.php';
-                        if (is_file($phpScript)) {
-                            require_once $phpScript;
-                        }
+                    // Make sure this instance has the lock
+                    $lock = get_option($lockName);
+                    if ($lock && is_array($lock) && count($lock) == 2 && $lock[0] == $myId && $lock[1] > microtime(true)) {
 
-                        // If there were no errors
-                        if ($queryError == '') {
-                            $startupNotices .= '<p>The database for the '.$a['name'].' plugin has been updated '
-                                    .'from V'.$dbVersion.' to V'.$ver.'.</p>';
-                        } else {
-                            $startupNotices .= '<p>Failure updating the database tables for the '.$a['name'].' plugin '
-                                    .'from V'.$dbVersion.' to V'.$ver.'.</p>';
-                            $db_setup_status = false;
-                            $startupNotices .= '<p>Database Update Error:</b> '.$queryError.'</p>';
-                        }
+                        /*
+                         * At this point this instance should have a verified lock on doing the database
+                         * update and all other instances should be waiting for this to complete.
+                         */
 
-                        $dbVersion = $ver;
-                        $db_setup_status = true;
+                        trigger_error("glmCheckDatabase() - Lock Name: $lockName - Instance: $myId - This instance has a verified database update lock for: ".$a['short_name'],E_USER_NOTICE);
 
-                    }
+                        // Include an admin message that we're updating the database
+                        $startupNotices .= '<p>The '.$a['name'].' database tables require updating...</p>';
 
-                    // Save the new version. If we've had a problem updating the database, then stop here.
-                    if ($db_setup_status) {
+                        // Traverse version list to find any required updates
+                        $curVerFound = false;
+                        $db_setup_status = true;
+                        foreach($a['database']['dbVersions'] as $version) {
+
+                            $ver = $version['version'];
+
+                            // Find the current version of the database
+                            if ($ver == $dbVersion) {
+
+                                $db_setup_status = true;
+                                $curVerFound = true;
+
+                            // Otherwise if it's already been found and $ver is not the new target version
+                            } elseif ($curVerFound && $dbVersion != $a['database']['dbCurrentVersion']) {
+
+                                // Read in Database creation script
+                                $sqlFile = $a['database']['dbScriptPath'].'/update_database_V'.$ver.'.sql';
+                                $sql = file_get_contents($sqlFile);
+
+                                // Replace {prefix} with table name prefix
+                                $sql = str_replace('{prefix}', $a['database']['dbPrefix'], $sql);
+
+                                // Split script into separate queries by looking for lines with only "---"
+                                $queries = preg_split('/^----$/m', $sql);
+
+                                // Try executing all queries to update database
+                                do {
+                                    $q = current($queries);
+                                    $wpdb->query($q);
+                                    $queryError = $wpdb->last_error;
+                                } while ($queryError == '' && next($queries));
+
+                                // Check for PHP script to update database
+                                $phpScript = $a['database']['dbScriptPath'].'/update_database_V'.$ver.'.php';
+                                if (is_file($phpScript)) {
+                                    require_once $phpScript;
+                                }
+
+                                // If there were no errors
+                                if ($queryError == '') {
+                                    $startupNotices .= '<p>The database for the '.$a['name'].' plugin has been updated '
+                                            .'from V'.$dbVersion.' to V'.$ver.'.</p>';
+                                } else {
+                                    $startupNotices .= '<p>Failure updating the database tables for the '.$a['name'].' plugin '
+                                            .'from V'.$dbVersion.' to V'.$ver.'.</p>';
+                                    $db_setup_status = false;
+                                    $startupNotices .= '<p>Database Update Error:</b> '.$queryError.'</p>';
+                                }
+
+                                $dbVersion = $ver;
+                                $db_setup_status = true;
+
+                            }
+
+                            // Save the new version.
+                            if ($db_setup_status) {
+                                update_option($a['database']['dbActiveVersionOption'], $dbVersion);
+                            } else {
+                                // There was a problem updating the database, so don't do any more.
+                                break;
+                            }
 
-                        // Save the version of the installed database
-                        update_option($a['database']['dbActiveVersionOption'], $dbVersion);
+                        }
 
-                    } else {
-                        break;
-                    }
+                        if ($db_setup_status) {
+                            trigger_error("glmCheckDatabase() - Lock Name: $lockName - Instance: $myId - This instance has updated the database for: ".$a['short_name'],E_USER_NOTICE);
+                            $startupNotices .= '<p><b>Database tables updated.</b></p>';
+                        } else {
+                            trigger_error("glmCheckDatabase() - Lock Name: $lockName - Instance: $myId - This instance had a problem updating the database for: ".$a['short_name'],E_USER_NOTICE);
+                        }
 
-                }
+                        /*
+                         * This instance has completed the database update (or failed) and will remove
+                         * the WordPress option that's locking out the other instances so they can
+                         * continue.
+                         *
+                         * If there was a failure doing the update, this plugin will be deactivated
+                         * at the bottom of this function.
+                         */
+
+                        // Remove our lock
+                        delete_option($lockName);
 
-                if ($db_setup_status) {
+                        trigger_error("glmCheckDatabase() - Lock Name: $lockName - Instance: $myId - This instance has removed database update lock for: ".$a['short_name'],E_USER_NOTICE);
 
-                    $startupNotices .= '<p><b>Database tables updated.</b></p>';
+                    }
                 }
 
             } else {
@@ -713,7 +773,7 @@ function glmCheckDatabase ()
             // Deactivate this add-on
             include_once ABSPATH . 'wp-admin/includes/plugin.php';
             deactivate_plugins($a['slug'].'/index.php');
-            $startupNotices .= '<p>Plugin '.$a['name'].' Deactivated.</p>';
+            $startupNotices .= '<p>Plugin '.$a['name'].' Deactivated due to serious database update problem.</p>';
 
         }
     } // For each plugin
index aa3e09f..c5587d8 100755 (executable)
@@ -66,6 +66,10 @@ There is of course much more to this.
 (none)
 
 == Changelog ==
+= PENDING =
+* Now locking out other processes from database update on an Add-On when one process is doing the update.
+* Added parameter to glmMembersConfigArraySetup() in glmPluginSupport.php to use names for resulting array if needed.
+
 = 2.10.46 =
 * Moved session startup from adminHooks.php to index.php to ensure there's always a session started.
 * Fixed member listings not using selected sort order setting past first page.
diff --git a/views/front/error/databaseUpdateInProgress.html b/views/front/error/databaseUpdateInProgress.html
new file mode 100644 (file)
index 0000000..63918af
--- /dev/null
@@ -0,0 +1,24 @@
+<html>
+    <head>
+    </head>
+    <body>
+        <center>
+            <h1 class="glm-error">Aw shucks!</h1>
+            <h3>We're sorry about this, but we had to do a quick database update.</h3>
+            <div style="width: 600px; text-align: left;">
+                <p>
+                    Unfortunately you came in here just as we started doing that and if we let you continue
+                    it might really muck up the works here. Fortunately these things are usually
+                    really quick and rarely does anyone get caught like this. That makes you special, so
+                    enjoy our database update page for a minute.
+                </p>
+                <p>
+                    If you'd like to get back to our Website, you can probably do that now by clicking
+                    the link below. If that doesn't work it means we really messed things up and it
+                    might be a good time for a snack.
+                </p>
+                <center><h2><a href="javascript:history.back()">Try Again</a></h2></center>
+            </div>
+        </center>
+    </body>
+</html>
\ No newline at end of file