MT#64067 address deadlock detection for preferences

* improve Role/API/Preferences err_code callback to check
  deadlocks (via $self->check_deadlock) and set $process_extras->{retry_tx} = 1
  so that the outer caller can act upon it and restart the transaction.
* increase deadlock attempts from 2 to 5 as some deadlocks seem to take
  longer time to resolve.
* wait 1 second between deadlock retry attempts as otherwise if a
  deadlock takes more than 1 second and all the retries are done within
  less than a second it will cause an error.
* EntitiesItem: add support for retry_tx and call goto TX_START if
  $process_extras->{retry_tx} (when check_deadlock is processed
  externally, like in Utils::Preferences::update_item in the err_code
  callback).

Change-Id: Ib56383710041c21d72782047a852353296c22215
(cherry picked from commit eae620f204)
(cherry picked from commit 2d31d86c3d)
mr12.5.1
Kirill Solomko 1 month ago
parent 38324661c5
commit 60a81b50ee

@ -120,7 +120,9 @@ sub PATCH :Allow {
# last param is "no replace" to NOT delete existing prefs
# for proper PATCH behavior
$subscriber = $self->update_item($c, $subscriber, $old_resource, $resource, 0, "subscribers");
my $process_extras;
($subscriber, undef, $process_extras) = $self->update_item($c, $subscriber, $old_resource, $resource, undef, $process_extras);
goto TX_START if $process_extras->{retry_tx};
last unless $subscriber;
my $hal = $self->hal_from_item($c, $subscriber, "subscribers");
@ -181,7 +183,9 @@ sub PUT :Allow {
# last param is "replace" to delete all existing prefs
# for proper PUT behavior
$subscriber = $self->update_item($c, $subscriber, $old_resource, $resource, 1, "subscribers");
my $process_extras;
$subscriber = $self->update_item($c, $subscriber, $old_resource, $resource, undef, $process_extras);
goto TX_START if $process_extras->{retry_tx};
last unless $subscriber;
my $hal = $self->hal_from_item($c, $subscriber, "subscribers");

@ -2182,7 +2182,7 @@ sub check_wildcard_search {
sub check_deadlock {
my ($self, $c, $error) = @_;
my $max_attempts = 2;
my $max_attempts = 5;
return 0 unless $error;
@ -2202,6 +2202,7 @@ sub check_deadlock {
log => ($lockwait_retry and $lockwait_err or $deadlock_err),
);
$c->stash->{deadlock_retry_attempt} = $attempt+1;
sleep 1;
return 1;
}

@ -210,10 +210,28 @@ sub _item_rs {
}
sub update_item {
my ($self, $c, $item, $old_resource, $resource, $form, $process_extras) = @_;
my ($self, $c, $item, $old_resource, $resource) = @_;
$process_extras //= {};
return NGCP::Panel::Utils::Preferences::update_preferences(
if (ref $process_extras eq 'HASH') {
$process_extras->{retry_tx} = 0;
}
my $err_cb = sub {
my ($code, $msg, $err) = @_;
if ($err && $self->check_deadlock($c, $err)) {
if (ref $process_extras eq 'HASH') {
$process_extras->{retry_tx} = 1;
}
return;
}
unless ($c->has_errors) {
$self->error($c, $code, $msg, $err);
}
};
$item = NGCP::Panel::Utils::Preferences::update_preferences(
c => $c,
schema => $c->model('DB'),
item => $item,
@ -221,12 +239,11 @@ sub update_item {
resource => $resource,
type => $self->container_resource_type,
replace => uc($c->request->method) eq 'PUT',
err_code => sub {
my ($code, $msg) = @_;
$self->error($c, $code, $msg);
},
err_code => $err_cb,
);
return $item, $form, $process_extras;
}
1;

@ -313,7 +313,8 @@ sub patch {
goto TX_END;
}
($item, $form, $process_extras) = $self->update_item($c, $item, $old_resource, $resource, $form, $process_extras );
($item, $form, $process_extras) = $self->update_item($c, $item, $old_resource, $resource, $form, $process_extras);
goto TX_START if ref $process_extras eq 'HASH' && $process_extras->{retry_tx};
if ($c->has_errors) {
goto TX_END;
}
@ -410,7 +411,8 @@ sub put {
my ($data_processed_result);
if (!$non_json_data || !$data) {
($item, $form, $process_extras) = $self->update_item($c, $item, $old_resource, $resource, $form, $process_extras );
($item, $form, $process_extras) = $self->update_item($c, $item, $old_resource, $resource, $form, $process_extras);
goto TX_START if ref $process_extras eq 'HASH' && $process_extras->{retry_tx};
if ($c->has_errors) {
goto TX_END;
}

@ -591,7 +591,7 @@ sub update_preferences {
$full_rs->delete_all;
} catch($e) {
$c->log->error("failed to clear preferences for '$accessor': $e");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.", $e);
return;
};
} else {
@ -693,7 +693,7 @@ sub update_preferences {
}
} catch($e) {
$c->log->error("failed to clear preference for '$accessor': $e");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.", $e);
return;
};
}
@ -1015,7 +1015,7 @@ sub update_preferences {
};
if ($@) {
$c->log->error("Failed to transform pref value - $@");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error."); # TODO?
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.", $@); # TODO?
return;
}
if(JSON::is_bool($v)){
@ -1030,7 +1030,7 @@ sub update_preferences {
};
if ($@) {
$c->log->error("Failed to transform pref value - $@");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error."); # TODO?
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.", $@); # TODO?
return;
}
if(JSON::is_bool($resource->{$pref})){
@ -1044,7 +1044,7 @@ sub update_preferences {
};
if ($@) {
$c->log->error("Failed to transform pref value - $@");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error."); # TODO?
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.", $@); # TODO?
return;
}
if(JSON::is_bool($resource->{$pref})){
@ -1061,7 +1061,7 @@ sub update_preferences {
}
} catch($e) {
$c->log->error("failed to update preference for '$accessor': $e");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.", $e);
return;
}
}
@ -1081,7 +1081,7 @@ sub update_preferences {
);
} catch($e) {
$c->log->error("Failed to set peer registration: $e");
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error."); # TODO?
&$err_code(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error.", $e); # TODO?
return;
}
}

Loading…
Cancel
Save