From e852b156652474de1b5cb4b12b1a08d4954f69a6 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Wed, 3 Jun 2015 12:46:00 +0200 Subject: [PATCH] SL::DB::Helper::TransNumberGenerator: Tabellen und Zeilen locken MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Die Tabelle, aus der die Liste der bereits benutzten Belegnummern ausgelesen wird, muss exklusiv gelockt werden, um zu verhindern, dass danach zwischen dem Auslesen und der Vergabe der neuen Belegnummer eine andere DB-Verbindung dasselbe macht und dieselbe Nummer verwendet. Dieses Locking muss daher vor dem Auslesen der Daten geschehen. Weiterhin müssen die Zeilen in den Nummernkreistabellen (defaults bzw. business) gelockt werden. Hier reicht aber das Locking der entsprechenden Zeile. Beide Locks müssen analog zu SL::TransNumber gehandhabt werden, um einen potenziellen Deadlock zu vermeiden, sprich zuerst die Belegtabelle, danach die Zeile in der Nummernkreistabelle. --- SL/DB/Helper/TransNumberGenerator.pm | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/SL/DB/Helper/TransNumberGenerator.pm b/SL/DB/Helper/TransNumberGenerator.pm index 9684677c2..e42c41597 100644 --- a/SL/DB/Helper/TransNumberGenerator.pm +++ b/SL/DB/Helper/TransNumberGenerator.pm @@ -70,9 +70,21 @@ sub get_next_trans_number { } } + # Lock both the table where the new number is stored and the range + # table. The storage table has to be locked first in order to + # prevent deadlocks as the legacy code in SL/TransNumber.pm locks it + # first, too. + + # For the storage table we have to use a full lock in order to + # prevent insertion of new entries while this routine is still + # working. For the range table we only need a row-level lock, + # therefore we're re-loading the row. + $self->db->dbh->do("LOCK " . $self->meta->table) || die $self->db->dbh->errstr; + my %numbers_in_use = map { ( $_->$number_column => 1 ) } @{ $self->_get_manager_class->get_all(%conditions_for_in_use) }; - my $range_table = $business ? $business : SL::DB::Default->get; + my $range_table = ($business ? $business : SL::DB::Default->get)->load(for_update => 1); + my $start_number = $range_table->$number_range_column; $start_number = $range_table->articlenumber if ($number_range_column eq 'assemblynumber') && (length($start_number) < 1); my $sequence = SL::PrefixedNumber->new(number => $start_number // 0); -- 2.20.1