Skip to content

Commit

Permalink
Handle foreign key violation with savepoint
Browse files Browse the repository at this point in the history
Apparently wrapping a potential database error in an eval doesn't prevent
DBIx::Class from aborting the transaction.

But with a SAVEPOINT we can work around that.
svp_rollback will set back the transaction to the sate from svp_begin.

For being able to use savepoints we have to be in a transaction, and we don't
have that ensured in all our unit tests, so this is adding another txn_do
which will do nothing if it's nested.

Issue: https://progress.opensuse.org/issues/124487
  • Loading branch information
perlpunk committed Oct 28, 2024
1 parent e85916c commit 7b34cde
Showing 1 changed file with 23 additions and 14 deletions.
37 changes: 23 additions & 14 deletions lib/OpenQA/Shared/Plugin/Gru.pm
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,29 @@ sub _find_existing_minion_job ($self, $task, $args, $job_ids) {

sub _add_jobs_to_gru_task ($self, $gru_id, $job_ids) {
my $schema = OpenQA::Schema->singleton;
for my $id (@$job_ids) {
# Add job to existing gru task with the same args
my $gru_dep = eval { $schema->resultset('GruDependencies')->create({job_id => $id, gru_task_id => $gru_id}); };
unless ($gru_dep) {
my $error = $@;
die $error
unless $error
=~ m/insert or update on table "gru_dependencies" violates foreign key constraint "gru_dependencies_fk_gru_task_id"/i;
# if the GruTask was already deleted meanwhile, we can skip
# the rest of the jobs, since the wanted task was done
log_debug("GruTask $gru_id already gone, skip assigning jobs (message: $error)");
last;
}
}
# Wrap in txn_do so we can use savepoints in the method. Necessary for cases
# where we are not in a transaction. Otherwise it's a noop
$schema->txn_do(
sub {
$schema->svp_begin('try_gru_dependencies');
for my $id (@$job_ids) {
# Add job to existing gru task with the same args
my $gru_dep
= eval { $schema->resultset('GruDependencies')->create({job_id => $id, gru_task_id => $gru_id}); };
unless ($gru_dep) {
my $error = $@;
$schema->svp_rollback('try_gru_dependencies');
die $error
unless $error
=~ m/insert or update on table "gru_dependencies" violates foreign key constraint "gru_dependencies_fk_gru_task_id"/i;
# if the GruTask was already deleted meanwhile, we can skip
# the rest of the jobs, since the wanted task was done
log_debug("GruTask $gru_id already gone, skip assigning jobs (message: $error)");
last;
}
}
$schema->svp_release('try_gru_dependencies');
});
}

sub enqueue ($self, $task, $args = [], $options = {}, $jobs = []) {
Expand Down

0 comments on commit 7b34cde

Please sign in to comment.