Commit 496ad3d1 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 374004: Enable transaction code and use it in some installation places

Patch By Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=mkanat
parent edd47c05
...@@ -412,14 +412,15 @@ sub request_cache { ...@@ -412,14 +412,15 @@ sub request_cache {
# Private methods # Private methods
# Per process cleanup # Per-process cleanup
sub _cleanup { sub _cleanup {
# When we support transactions, need to ->rollback here
my $main = request_cache()->{dbh_main}; my $main = request_cache()->{dbh_main};
my $shadow = request_cache()->{dbh_shadow}; my $shadow = request_cache()->{dbh_shadow};
$main->disconnect if $main; foreach my $dbh ($main, $shadow) {
$shadow->disconnect if $shadow && Bugzilla->params->{"shadowdb"}; next if !$dbh;
$dbh->bz_rollback_transaction() if $dbh->bz_in_transaction;
$dbh->disconnect;
}
undef $_request_cache; undef $_request_cache;
} }
......
...@@ -859,39 +859,51 @@ sub bz_table_list_real { ...@@ -859,39 +859,51 @@ sub bz_table_list_real {
# Transaction Methods # Transaction Methods
##################################################################### #####################################################################
sub bz_in_transaction {
return $_[0]->{private_bz_transaction_count} ? 1 : 0;
}
sub bz_start_transaction { sub bz_start_transaction {
my ($self) = @_; my ($self) = @_;
if ($self->{private_bz_in_transaction}) { if ($self->bz_in_transaction) {
ThrowCodeError("nested_transaction"); $self->{private_bz_transaction_count}++;
} else { } else {
# Turn AutoCommit off and start a new transaction # Turn AutoCommit off and start a new transaction
$self->begin_work(); $self->begin_work();
$self->{private_bz_in_transaction} = 1; # REPEATABLE READ means "We work on a snapshot of the DB that
# is created when we execute our first SQL statement." It's
# what we need in Bugzilla to be safe, for what we do.
# Different DBs have different defaults for their isolation
# level, so we just set it here manually.
$self->do('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
$self->{private_bz_transaction_count} = 1;
} }
} }
sub bz_commit_transaction { sub bz_commit_transaction {
my ($self) = @_; my ($self) = @_;
if (!$self->{private_bz_in_transaction}) { if ($self->{private_bz_transaction_count} > 1) {
ThrowCodeError("not_in_transaction"); $self->{private_bz_transaction_count}--;
} else { } elsif ($self->bz_in_transaction) {
$self->commit(); $self->commit();
$self->{private_bz_transaction_count} = 0;
$self->{private_bz_in_transaction} = 0; } else {
ThrowCodeError('not_in_transaction');
} }
} }
sub bz_rollback_transaction { sub bz_rollback_transaction {
my ($self) = @_; my ($self) = @_;
if (!$self->{private_bz_in_transaction}) { # Unlike start and commit, if we rollback at any point it happens
# instantly, even if we're in a nested transaction.
if (!$self->bz_in_transaction) {
ThrowCodeError("not_in_transaction"); ThrowCodeError("not_in_transaction");
} else { } else {
$self->rollback(); $self->rollback();
$self->{private_bz_transaction_count} = 0;
$self->{private_bz_in_transaction} = 0;
} }
} }
...@@ -928,9 +940,6 @@ sub db_new { ...@@ -928,9 +940,6 @@ sub db_new {
# above "die" condition. # above "die" condition.
$self->{RaiseError} = 1; $self->{RaiseError} = 1;
# class variables
$self->{private_bz_in_transaction} = 0;
bless ($self, $class); bless ($self, $class);
return $self; return $self;
...@@ -2212,20 +2221,33 @@ in the database. ...@@ -2212,20 +2221,33 @@ in the database.
=over =over
=item C<bz_in_transaction>
Returns C<1> if we are currently in the middle of an uncommitted transaction,
C<0> otherwise.
=item C<bz_start_transaction> =item C<bz_start_transaction>
Starts a transaction if supported by the database being used. Returns nothing Starts a transaction.
and takes no parameters.
It is OK to call C<bz_start_transaction> when you are already inside of
a transaction. However, you must call L</bz_commit_transaction> as many
times as you called C<bz_start_transaction>, in order for your transaction
to actually commit.
Bugzilla uses C<REPEATABLE READ> transactions.
Returns nothing and takes no parameters.
=item C<bz_commit_transaction> =item C<bz_commit_transaction>
Ends a transaction, commiting all changes, if supported by the database Ends a transaction, commiting all changes. Returns nothing and takes
being used. Returns nothing and takes no parameters. no parameters.
=item C<bz_rollback_transaction> =item C<bz_rollback_transaction>
Ends a transaction, rolling back all changes, if supported by the database Ends a transaction, rolling back all changes. Returns nothing and takes
being used. Returns nothing and takes no parameters. no parameters.
=back =back
......
...@@ -207,14 +207,23 @@ sub bz_lock_tables { ...@@ -207,14 +207,23 @@ sub bz_lock_tables {
ThrowCodeError("already_locked", { current => $self->{private_bz_tables_locked}, ThrowCodeError("already_locked", { current => $self->{private_bz_tables_locked},
new => $list }); new => $list });
} else { } else {
$self->bz_start_transaction();
$self->do('LOCK TABLE ' . $list); $self->do('LOCK TABLE ' . $list);
$self->{private_bz_tables_locked} = $list; $self->{private_bz_tables_locked} = $list;
} }
} }
sub bz_unlock_tables { sub bz_unlock_tables {
my ($self, $abort) = @_; my ($self, $abort) = @_;
if ($self->bz_in_transaction) {
if ($abort) {
$self->bz_rollback_transaction();
}
else {
$self->bz_commit_transaction();
}
}
# Check first if there was previous matching lock # Check first if there was previous matching lock
if (!$self->{private_bz_tables_locked}) { if (!$self->{private_bz_tables_locked}) {
...@@ -223,28 +232,10 @@ sub bz_unlock_tables { ...@@ -223,28 +232,10 @@ sub bz_unlock_tables {
ThrowCodeError("no_matching_lock"); ThrowCodeError("no_matching_lock");
} else { } else {
$self->do("UNLOCK TABLES"); $self->do("UNLOCK TABLES");
$self->{private_bz_tables_locked} = ""; $self->{private_bz_tables_locked} = "";
} }
} }
# As Bugzilla currently runs on MyISAM storage, which does not support
# transactions, these functions die when called.
# Maybe we should just ignore these calls for now, but as we are not
# using transactions in MySQL yet, this just hints the developers.
sub bz_start_transaction {
die("Attempt to start transaction on DB without transaction support");
}
sub bz_commit_transaction {
die("Attempt to commit transaction on DB without transaction support");
}
sub bz_rollback_transaction {
die("Attempt to rollback transaction on DB without transaction support");
}
sub _bz_get_initial_schema { sub _bz_get_initial_schema {
my ($self) = @_; my ($self) = @_;
return $self->_bz_build_schema_from_disk(); return $self->_bz_build_schema_from_disk();
......
...@@ -2205,6 +2205,9 @@ sub _migrate_email_prefs_to_new_table { ...@@ -2205,6 +2205,9 @@ sub _migrate_email_prefs_to_new_table {
my %requestprefs = ("FlagRequestee" => EVT_FLAG_REQUESTED, my %requestprefs = ("FlagRequestee" => EVT_FLAG_REQUESTED,
"FlagRequester" => EVT_REQUESTED_FLAG); "FlagRequester" => EVT_REQUESTED_FLAG);
# We run the below code in a transaction to speed things up.
$dbh->bz_start_transaction();
# Select all emailflags flag strings # Select all emailflags flag strings
my $sth = $dbh->prepare("SELECT userid, emailflags FROM profiles"); my $sth = $dbh->prepare("SELECT userid, emailflags FROM profiles");
$sth->execute(); $sth->execute();
...@@ -2271,6 +2274,7 @@ sub _migrate_email_prefs_to_new_table { ...@@ -2271,6 +2274,7 @@ sub _migrate_email_prefs_to_new_table {
# EVT_ATTACHMENT. # EVT_ATTACHMENT.
_clone_email_event(EVT_ATTACHMENT, EVT_ATTACHMENT_DATA); _clone_email_event(EVT_ATTACHMENT, EVT_ATTACHMENT_DATA);
$dbh->bz_commit_transaction();
$dbh->bz_drop_column("profiles", "emailflags"); $dbh->bz_drop_column("profiles", "emailflags");
} }
} }
......
...@@ -102,6 +102,7 @@ use Apache2::Const -compile => qw(OK); ...@@ -102,6 +102,7 @@ use Apache2::Const -compile => qw(OK);
sub handler { sub handler {
my $r = shift; my $r = shift;
Bugzilla::_cleanup();
# Sometimes mod_perl doesn't properly call DESTROY on all # Sometimes mod_perl doesn't properly call DESTROY on all
# the objects in pnotes() # the objects in pnotes()
foreach my $key (keys %{$r->pnotes}) { foreach my $key (keys %{$r->pnotes}) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment