Merge lp:~davidstrauss/pressflow/database-failover into lp:pressflow

Proposed by David Strauss
Status: Rejected
Rejected by: David Strauss
Proposed branch: lp:~davidstrauss/pressflow/database-failover
Merge into: lp:pressflow
Diff against target: None lines
To merge this branch: bzr merge lp:~davidstrauss/pressflow/database-failover
Reviewer Review Type Date Requested Status
David Strauss Pending
Review via email: mp+11606@code.launchpad.net

Commit message

Built-in database failover, originally developed by David Rothstein and Acquia Hosting.

To post a comment you must log in.
50. By David Strauss

Add missing file

Unmerged revisions

50. By David Strauss

Add missing file

49. By David Strauss

Add changes from David Rothstein (Acquia Hosting).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'includes/database.inc'
2--- includes/database.inc 2009-07-01 22:36:49 +0000
3+++ includes/database.inc 2009-09-11 15:02:34 +0000
4@@ -130,6 +130,13 @@
5 install_goto('install.php');
6 }
7
8+ // If an array of possible databases (e.g., for a master-master setup) was
9+ // specified and this is the first time we're trying to connect to them,
10+ // choose the one we will connect to during this page request.
11+ if (!isset($db_conns[$name]) && isset($db_url[$name]) && is_array($db_url[$name])) {
12+ $db_conns[$name] = db_choose_active($db_url[$name], $name);
13+ }
14+
15 if (!isset($db_conns[$name])) {
16 // Initiate a new connection, using the named DB URL specified.
17 if (is_array($db_url)) {
18@@ -163,9 +170,20 @@
19 _db_error_page("The database type '". $db_type ."' is unsupported. Please use either 'mysql' or 'mysqli' for MySQL, or 'pgsql' for PostgreSQL databases.");
20 }
21
22- $db_conns[$name] = db_connect($connect_url);
23+ try {
24+ $db_conns[$name] = db_connect($connect_url);
25+ }
26+ catch (Exception $e) {
27+ _db_error_page($e->getMessage());
28+ }
29+
30 if (!empty($slave_connect_url)) {
31- $db_slave_conns[$name] = db_connect($slave_connect_url);
32+ try {
33+ $db_slave_conns[$name] = db_connect($slave_connect_url);
34+ }
35+ catch (Exception $e) {
36+ _db_error_page($e->getMessage());
37+ }
38 }
39 }
40
41@@ -184,6 +202,96 @@
42 }
43
44 /**
45+ * Choose a database server to connect to during the current page request.
46+ *
47+ * This function will loop through a list of provided databases and attempt to
48+ * connect to them in order, stopping when a successful connection is made.
49+ * However, it also checks a (pluggable) cache to see if a different database
50+ * server is preferred and moves that server to the front of the list so that
51+ * it can be attempted before any of the others. If the database server that
52+ * is eventually connected to is different from the one stored in the cache,
53+ * it writes the new, preferred sever to the cache before exiting.
54+ *
55+ * @param $db_urls
56+ * An ordered array of possible database servers to connect to. The keys
57+ * should be unique identifiers for each server (e.g., "db1", "db2"), and
58+ * the values should be database connection URLs. Once a database server
59+ * has been chosen, the array will be replaced with a string containing the
60+ * database connection URL that is actually being used.
61+ * @param $name
62+ * The name of the database that is being connected to. Usually this will be
63+ * 'default', but alternate values can be specified if a separate, external
64+ * set of database servers is being used for part of the page request (in
65+ * addition to the main Drupal database).
66+ * @return
67+ * The new database connection.
68+ */
69+function db_choose_active(&$db_urls, $name) {
70+ // Load the pluggable cache and check if it contains the name of a preferred
71+ // database server. If so, move that server to the front of the list.
72+ include_once variable_get('db_server_cache_inc', 'includes/db.server.cache.filesystem.inc');
73+ if (function_exists('db_server_cache_get') && ($cached_db_server_id = db_server_cache_get($name))) {
74+ if (isset($db_urls[$cached_db_server_id]) && $db_urls[$cached_db_server_id] != reset($db_urls)) {
75+ $db_urls = array($cached_db_server_id => $db_urls[$cached_db_server_id]) + $db_urls;
76+ }
77+ }
78+
79+ // Loop through each database server and try to connect to it. Stop once a
80+ // successful connection is made.
81+ $error_message = '';
82+ foreach ($db_urls as $id => $connect_url) {
83+ $db_type = substr($connect_url, 0, strpos($connect_url, '://'));
84+ $handler = "./includes/database.$db_type.inc";
85+ if (is_file($handler)) {
86+ include_once $handler;
87+ // Try to connect to each server three times, with an increasing delay
88+ // between each attempt.
89+ $connection_attempt = 1;
90+ $max_connection_attempts = 3;
91+ while ($connection_attempt <= $max_connection_attempts) {
92+ try {
93+ // Quit the entire loop as soon as a successful connection is made.
94+ $connection = db_connect($connect_url);
95+ $preferred_db_server_id = $id;
96+ break 2;
97+ }
98+ catch (Exception $e) {
99+ if ($connection_attempt == $max_connection_attempts) {
100+ $error_message .= ' Server '. $id .': '. $e->getMessage();
101+ }
102+ else {
103+ // Wait 0.5 seconds before the second attempt, and 1 second before
104+ // the third attempt.
105+ usleep($connection_attempt * 500000);
106+ }
107+ }
108+ $connection_attempt += 1;
109+ }
110+ }
111+ else {
112+ $error_message .= ' Server '. $id .": The database type '". $db_type ."' is unsupported. Please use either 'mysql' or 'mysqli' for MySQL, or 'pgsql' for PostgreSQL databases.";
113+ }
114+ }
115+
116+ // If no connection was made, show an error page and exit.
117+ if (empty($connection)) {
118+ _db_error_page($error_message);
119+ }
120+
121+ // If the preferred database server changed, write this to the cache.
122+ if (empty($cached_db_server_id) || $cached_db_server_id != $preferred_db_server_id) {
123+ if (function_exists('db_server_cache_set')) {
124+ db_server_cache_set($name, $preferred_db_server_id);
125+ }
126+ }
127+
128+ // Replace the list of database connection URLs with the one that is
129+ // actually being used, and return the new database connection.
130+ $db_urls = $db_urls[$preferred_db_server_id];
131+ return $connection;
132+}
133+
134+/**
135 * Helper function to show fatal database errors.
136 *
137 * Prints a themed maintenance page with the 'Site off-line' text,
138
139=== modified file 'includes/database.mysql.inc'
140--- includes/database.mysql.inc 2009-02-26 00:40:48 +0000
141+++ includes/database.mysql.inc 2009-09-11 15:02:34 +0000
142@@ -53,7 +53,10 @@
143
144 // Check if MySQL support is present in PHP
145 if (!function_exists('mysql_connect')) {
146+ throw new Exception('Unable to use the MySQL database because the MySQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
147+ /* Original version (replaced with exception-based method above):
148 _db_error_page('Unable to use the MySQL database because the MySQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
149+ */
150 }
151
152 // Decode url-encoded information in the db connection string
153@@ -76,8 +79,11 @@
154 // (matched) rows, not the number of affected rows.
155 $connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2);
156 if (!$connection || !mysql_select_db(substr($url['path'], 1))) {
157+ throw new Exception(mysql_error());
158+ /* Original version (replaced with exception-based method above):
159 // Show error screen otherwise
160 _db_error_page(mysql_error());
161+ */
162 }
163
164 // Force UTF-8.
165
166=== modified file 'includes/database.mysqli.inc'
167--- includes/database.mysqli.inc 2009-02-26 00:40:48 +0000
168+++ includes/database.mysqli.inc 2009-09-11 15:02:34 +0000
169@@ -57,7 +57,10 @@
170 function db_connect($url) {
171 // Check if MySQLi support is present in PHP
172 if (!function_exists('mysqli_init') && !extension_loaded('mysqli')) {
173+ throw new Exception('Unable to use the MySQLi database because the MySQLi extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
174+ /* Original version (replaced with exception-based method above):
175 _db_error_page('Unable to use the MySQLi database because the MySQLi extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
176+ */
177 }
178
179 $url = parse_url($url);
180@@ -76,7 +79,10 @@
181 @mysqli_real_connect($connection, $url['host'], $url['user'], $url['pass'], substr($url['path'], 1), $url['port'], NULL, MYSQLI_CLIENT_FOUND_ROWS);
182
183 if (mysqli_connect_errno() > 0) {
184+ throw new Exception(mysqli_connect_error());
185+ /* Original version (replaced with exception-based method above):
186 _db_error_page(mysqli_connect_error());
187+ */
188 }
189
190 // Force UTF-8.
191
192=== modified file 'includes/database.pgsql.inc'
193--- includes/database.pgsql.inc 2009-07-01 22:36:49 +0000
194+++ includes/database.pgsql.inc 2009-09-11 15:02:34 +0000
195@@ -47,7 +47,10 @@
196 function db_connect($url) {
197 // Check if PostgreSQL support is present in PHP
198 if (!function_exists('pg_connect')) {
199+ throw new Exception('Unable to use the PostgreSQL database because the PostgreSQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
200+ /* Original version (replaced with exception-based method above):
201 _db_error_page('Unable to use the PostgreSQL database because the PostgreSQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.');
202+ */
203 }
204
205 $url = parse_url($url);
206@@ -79,7 +82,12 @@
207 $connection = @pg_connect($conn_string);
208 if (!$connection) {
209 require_once './includes/unicode.inc';
210+ $error_message = decode_entities($php_errormsg);
211+ ini_set('track_errors', $track_errors_previous);
212+ throw new Exception($error_message);
213+ /* Original version (replaced with exception-based method above):
214 _db_error_page(decode_entities($php_errormsg));
215+ */
216 }
217
218 // Restore error tracking setting

Subscribers

People subscribed via source and target branches

to status/vote changes: