From 205211839b4ba9f7a17ead2c2d585a6b17f0a15a Mon Sep 17 00:00:00 2001 From: "justdave%bugzilla.org" Date: Tue, 17 Oct 2006 16:48:56 +0000 Subject: [PATCH] Bug 325916: Allow Bonsai to use a replicated slave database for queries r=bear --- webtools/bonsai/INSTALL | 13 ++++++++ webtools/bonsai/defparams.pl | 5 +++ webtools/bonsai/globals.pl | 65 ++++++++++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/webtools/bonsai/INSTALL b/webtools/bonsai/INSTALL index b2ece7b45d2f..add5b55619cb 100644 --- a/webtools/bonsai/INSTALL +++ b/webtools/bonsai/INSTALL @@ -435,3 +435,16 @@ typically write the following section in http.conf and restart the server: Order deny,allow Deny from All + +8. APPENDIX: MySQL Replication + +If you have a really high-traffic site and bonsai is getting queried a lot +(this typically happens if you have tinderbox set up with it, and there are +a lot of tinderbox trees - since every report from a tinderbox will query +bonsai), you can get a performance boost by having bonsai use the slave +database (which is typically configured to give queries priority over +updates - thus avoiding many lock contentions) for everything except write +operations. To do this, set the 'shadowdbiparam' parameter to point at +the slave database. The username and password used to access it must be +the same. Setting up replication in MySQL is beyond the scope of this +document. MySQL has plenty of docs on this subject on their website. diff --git a/webtools/bonsai/defparams.pl b/webtools/bonsai/defparams.pl index 4b5c3d5d54e9..adb3b2fc8478 100644 --- a/webtools/bonsai/defparams.pl +++ b/webtools/bonsai/defparams.pl @@ -151,6 +151,11 @@ DefParam("dbiparam", "t", "DBI:mysql:database=bonsai;"); +DefParam("shadowdbiparam", + "The first parameter to pass to the DBI->connect() method of a read-only replicated slave database to use for queries, to help with performance on high-traffic systems. If left blank, queries will be made against the primary database and this param will be ignored.
Example: DBI:mysql:host=slaveserver;database=bonsai", + "t", + ""); + DefParam("readonly", "Are the hook files readonly. (This value gets changed on the fly, so it is ok to leave the way it is.)", diff --git a/webtools/bonsai/globals.pl b/webtools/bonsai/globals.pl index 2b22fdeb9512..03f04916271f 100644 --- a/webtools/bonsai/globals.pl +++ b/webtools/bonsai/globals.pl @@ -108,23 +108,61 @@ sub Param { sub ConnectToDatabase { my ($dsn); - if (!defined $::db) { - $dsn = Param('dbiparam'); + if (!defined $::readdb) { + $dsn = Param('shadowdbiparam') || Param('dbiparam'); # DBI->trace(1, "/tmp/dbi.out"); - $::db = DBI->connect($dsn, Param('mysqluser'), Param('mysqlpassword')) + $::readdb = DBI->connect($dsn, Param('mysqluser'), Param('mysqlpassword')) || die "Can't connect to database server."; + $::db = $::readdb; + } +} + +sub ConnectToWriteDatabase { + my ($dsn); + + if (!defined $::writedb) { + $dsn = Param('dbiparam'); + if (!Param('shadowdbiparam') && defined $::readdb) { + # If we don't have a shadowdb defined, and we're already connected to + # the database, just use the existing connection + $::writedb = $::readdb; + } + else { + $::writedb = DBI->connect($dsn, Param('mysqluser'), Param('mysqlpassword')) + || die "Can't connect to shadow database server."; + $::db = $::writedb; + } } } sub DisconnectFromDatabase { - if (defined $::db) { - $::db->disconnect(); - undef $::db; + if (defined $::readdb) { + $::readdb->disconnect(); + undef $::db if ($::db == $::readdb); + undef $::readdb; } } +sub DisconnectFromWriteDatabase { + if (defined $::writedb) { + $::writedb->disconnect(); + undef $::db if ($::db == $::writedb); + undef $::writedb; + } +} + +sub SwitchToShadowDB { + ConnectToDatabase() if !defined($::readdb); + $::db = $::readdb; +} + +sub SwitchToWriteDB { + ConnectToWriteDatabase() if !defined($::writedb); + $::db = $::writedb; +} + sub SendSQL { my ($str, @bind_values) = (@_); my $status = 0; @@ -337,6 +375,8 @@ sub GetId { return ($lastidcache{$index}) if (exists $lastidcache{$index}); + SwitchToWriteDB(); + &SendSQL("SELECT id FROM $table WHERE $field = ?", $value); ($id) = &FetchSQLData(); @@ -346,6 +386,8 @@ sub GetId { ($id) = &FetchSQLData(); } + SwitchToShadowDB(); + return ($lastidcache{$index} = $id); } @@ -354,6 +396,8 @@ sub GetId_NoCache { my ($table, $field, $value) = @_; my ($id); + SwitchToWriteDB(); + &SendSQL("SELECT id FROM $table WHERE $field = ?", $value); ($id) = &FetchSQLData(); @@ -363,6 +407,8 @@ sub GetId_NoCache { ($id) = &FetchSQLData(); } + SwitchToShadowDB(); + return $id; } @@ -389,6 +435,8 @@ sub GetHashedId { my ($table, $field, $value) = @_; my (@bind_values, $id); + SwitchToWriteDB(); + @bind_values = (&MakeValueHash($value), $value); &SendSQL("SELECT id FROM $table WHERE hash = ? AND $field = ?", @bind_values); @@ -401,6 +449,8 @@ sub GetHashedId { ($id) = &FetchSQLData(); } + SwitchToShadowDB(); + return $id; } @@ -416,6 +466,8 @@ sub AddToDatabase { my ($chtype, $date, $name, $repository, $dir); my ($file, $version, $sticky, $branch, $addlines, $removelines); + SwitchToWriteDB(); + # Truncate/cleanup description $desc = substr($desc, 0, 60000) if (length($desc) > 60000); @@ -507,6 +559,7 @@ sub AddToDatabase { print "." if $verbose; } } + SwitchToShadowDB(); } sub assert {