Replication patch from http://cvs.drupal.org/viewcvs/drupal/contributions/sandbox/crackerjm/replication/ --- /dev/null 2005-02-16 21:06:48.000000000 +0200 +++ ./README.replication 2005-08-15 16:08:52.000000000 +0300 @@ -0,0 +1,34 @@ + +This README.txt and patch are for Mysql Replication. This assumes that you have +a master replication server and slave server(s) up and running already. If +you don't, then there is no purpose for reading this. If you need help for +replication, please read this link. + http://dev.mysql.com/doc/mysql/en/Replication.html + +NOTE: This is meant for drupal 4.6.0 + +Now, in includes/conf.php you have a line that looks simalar to this + $db_url = "mysql://username:password@localhost/database"; +Comment at the line so that it looks like this + //$db_url = "mysql://username:password@localhost/database"; + +Add the following lines below it to look like this: + $db_url = array(); + $db_url['default'] = "mysql://username:password@localhost/database"; + $db_url['slave1'] = "mysql://username:password@host/database"; + //$db_url['slave2'] = "mysql://username:password@host/database"; + +The 'default' is your MASTER server, the slaves are just that. make +sure that you have added the appropriate users to the servers so that +if you are using round robin DNS or something to that effect, that +way you don't end up with permission denied. + +patch your code with replication.patch and there ya go + +*********** +NEW: This revision of code supports the dead slave servers! + caveat though, if ALL of your slaves are dead, then you will + end up in a infinitely recursive loop searching for slave servers. + Hopefully in the next revision of this, I will try to come up with + a way to stop this recursion without the use of flags. +*********** diff -urN drupal-4.6.0/includes/database.inc drupal-test/includes/database.inc --- drupal-4.6.0/includes/database.inc 2005-04-08 09:24:10.000000000 -0500 +++ drupal-test/includes/database.inc 2005-05-29 09:30:18.710563836 -0500 @@ -119,11 +119,98 @@ die('Unsupported database type'); } - $db_conns[$name] = db_connect($connect_url); - + if($name == 'default') { + // die on failure + $db_conns[$name] = db_connect($connect_url, true); + } + else { + $db_conns[$name] = db_connect($connect_url); + } + } // Set the active connection. - $active_db = $db_conns[$name]; + if($name != 'default' && $db_conns[$name] === false){ + print "Bad slave, killing $name\n"; + db_find_slave(true); // kill bad slave + db_set_active(db_find_slave()); + } + else { + $active_db = $db_conns[$name]; + } +} + +function _is_commit($query) { + $commits = array('insert', 'alter', 'update', 'delete', 'flush', 'lock'); + foreach($commits as $type) { + if(preg_match("/^$type/i", $query)) { + return true; + } + } + return false; +} + +function db_find_slave($killslave = false) { + global $db_url; + static $slave ; + + if(!_replication_ready()) + return 'default'; + + if(!is_array($slave)){ + $slave = array(); + // initialize a local copy + foreach($db_url as $key=>$value) { + if(stristr($key, 'slave')) { + $slave[] = $key; + } + } + /** + * Since this is the first iteration of the loop, we reset the array to + * ensure we return the first element. + */ + return reset($slave); + } + + if(empty($slave)) { + return 'default'; + } + + // Failed to connect, remove from the list + if($killslave){ + array_shift($slave); + } + + $url = next($slave); + + if($url === false){ + // walked past of the end of the array, start over at first element + $url = reset($slave); + } + + return $url; +} + +function _replication_ready() { + global $db_url; + static $ready; + + if(isset($ready)) { + return $ready; + } + + if(is_array($db_url)) { + foreach($db_url as $key=>$value) { + if(stristr($key, 'slave')) { + // Found at least ONE element with the word slave in it + // break the search to save on cycles + $ready = true; + return true; + } + } + } + + $ready = false; + return false; } /** diff -urN drupal-4.6.0/includes/database.mysql.inc drupal-test/includes/database.mysql.inc --- drupal-4.6.0/includes/database.mysql.inc 2005-04-14 13:50:23.000000000 -0500 +++ drupal-test/includes/database.mysql.inc 2005-05-29 09:28:22.490546558 -0500 @@ -20,7 +20,7 @@ * performance, however, when the overhead to connect to your database is high * (e.g. your database and web server live on different machines). */ -function db_connect($url) { +function db_connect($url, $critical = false) { $url = parse_url($url); // Allow for non-standard MySQL port. @@ -28,10 +28,16 @@ $url['host'] = $url['host'] .':'. $url['port']; } - $connection = mysql_connect($url['host'], $url['user'], $url['pass'], TRUE) or die(mysql_error()); - mysql_select_db(substr($url['path'], 1)) or die('unable to select database'); + // For replication setups, we will assume that dieing is bad + $connection = mysql_pconnect($url['host'], $url['user'], $url['pass'], TRUE); + if(!is_resource($connection) && $critical) { + die(mysql_error()); + } + // We die on bad slaves. These should be taken care of + // maybe hook this somehow or something + mysql_select_db(substr($url['path'], 1)) or die(mysql_error()); - return $connection; + return ($connection)?$connection:false; } /** @@ -40,14 +46,38 @@ function _db_query($query, $debug = 0) { global $active_db; global $queries; + global $db_url; if (variable_get('dev_query', 0)) { list($usec, $sec) = explode(' ', microtime()); $timer = (float)$usec + (float)$sec; } + /** + * If the db_url isn't an array, then we can assume that this server is not + * ready for a replication setup. This outer if is purely for a speed as + * calling PHP's builtin functions are way faster than user functions. + * So, if its an array and has more than one element, we shall atleast check + * to see if it's possibly ready for a replication enviorment. However, + * if no [slave#] elements are found, then we assume normal operations. + */ + if(is_array($db_url) && sizeof($db_url) > 1 && _replication_ready() ) + { + if (_is_commit($query) === true){ + db_set_active('default'); + } + else + { + // no checks as _replication_ready() verifies + // that there is atleast one slave + db_set_active(db_find_slave()); + } + } + + $result = mysql_query($query, $active_db); + if (variable_get('dev_query', 0)) { list($usec, $sec) = explode(' ', microtime()); $stop = (float)$usec + (float)$sec; @@ -67,6 +97,7 @@ } } + /** * Fetch one result row from the previous query as an object. * @@ -239,4 +270,4 @@ * @} End of "ingroup database". */ -?> \ No newline at end of file +?> --- ./sites/default/settings.php~ 2005-08-15 16:41:07.000000000 +0300 +++ ./sites/default/settings.php 2005-08-15 16:42:47.000000000 +0300 @@ -77,8 +77,15 @@ * Database URL format: * $db_url = 'mysql://username:password@localhost/database'; * $db_url = 'pgsql://username:password@localhost/database'; + * + * Database URL format with MySQL replication: + * $db_url = array(); + * $db_url['default'] = "mysql://username:password@localhost/database"; + * $db_url['slave1'] = "mysql://username:password@host/database"; */ -$db_url = 'mysql://username:password@localhost/database'; +$db_url = array(); +$db_url['default'] = 'mysql://mysql:@localhost/drupal'; +#$db_url['slave1'] = "mysql://mysql:@mysql-slave/drupal"; $db_prefix = ''; /**