[Rt-devel] [PATCH] Fix for SetOwner race condition
Todd Chapman
todd at chaka.net
Wed Feb 7 10:44:08 EST 2007
We are a pretty high volume RT user. Our customer service
people need to chug through so many tickets that I even
had to customize RT so that when a ticket is resolved the
next ticket in the queue is automatically taken and
displayed.
Several times in one week 2 people we able to Take the
same ticket. What is up with that?
Armed with 2 debugger sessions I stepped through the
code for SetOwner and was able to figure out that
the decision to allow a user to take a ticket was
happening outside of the critical section of code. This
means that the decision to allow a user to take a ticket
may be invalidated between the time the decision was
made and the time the ticket was actually taken.
By moving around the locking and a few other tricks
I was able to eliminate the race condition.
We have been using this new code for about 2 weeks with
no problems.
Patch is attached.
-Todd
-------------- next part --------------
==== Patch <set_owner_race_condition> level 1
Source: e4b3ae24-6623-0410-94c5-e08a22d88352:/local/bp/3.6-SetOwner_fix:7136
Target: e417ac7c-1bcc-0310-8ffa-8f5827389a85:/rt/branches/3.6-RELEASE:6920
(svn://svn.bestpractical.com)
Log:
r7135 at fe-rtr00a: todd-chapman | 2007-02-07 10:20:36 -0500
Submitting patch for SetOwner race condition.
r7136 at fe-rtr00a: todd-chapman | 2007-02-07 10:36:00 -0500
Fixed race condition in SetOwner.
=== lib/RT/Ticket_Overlay.pm
==================================================================
--- lib/RT/Ticket_Overlay.pm (revision 6920)
+++ lib/RT/Ticket_Overlay.pm (patch set_owner_race_condition level 1)
@@ -2979,14 +2979,28 @@
my $NewOwner = shift;
my $Type = shift || "Give";
+ $RT::Handle->BeginTransaction();
+
+ $self->_SetLastUpdated(); # lock the ticket
+ $self->Load( $self->id ); # in case $self changed while waiting for lock
+
my $OldOwnerObj = $self->OwnerObj;
+ my $NewOwnerObj = RT::User->new( $self->CurrentUser );
+ $NewOwnerObj->Load( $NewOwner );
+ unless ( $NewOwnerObj->Id ) {
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc("That user does not exist") );
+ }
+
+
# must have ModifyTicket rights
# or TakeTicket/StealTicket and $NewOwner is self
# see if it's a take
if ( $OldOwnerObj->Id == $RT::Nobody->Id ) {
unless ( $self->CurrentUserHasRight('ModifyTicket')
|| $self->CurrentUserHasRight('TakeTicket') ) {
+ $RT::Handle->Rollback();
return ( 0, $self->loc("Permission Denied") );
}
}
@@ -2997,27 +3011,24 @@
unless ( $self->CurrentUserHasRight('ModifyTicket')
|| $self->CurrentUserHasRight('StealTicket') ) {
+ $RT::Handle->Rollback();
return ( 0, $self->loc("Permission Denied") );
}
}
else {
unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
+ $RT::Handle->Rollback();
return ( 0, $self->loc("Permission Denied") );
}
}
- my $NewOwnerObj = RT::User->new( $self->CurrentUser );
- $NewOwnerObj->Load( $NewOwner );
- unless ( $NewOwnerObj->Id ) {
- return ( 0, $self->loc("That user does not exist") );
- }
-
- # If we're not stealing and the ticket has an owner and it's not
+ # If we're not stealing and the ticket has an owner and it's not
# the current user
if ( $Type ne 'Steal' and $Type ne 'Force'
and $OldOwnerObj->Id != $RT::Nobody->Id
and $OldOwnerObj->Id != $self->CurrentUser->Id )
{
+ $RT::Handle->Rollback();
return ( 0, $self->loc("You can only take tickets that are unowned") )
if $NewOwnerObj->id == $self->CurrentUser->id;
return (
@@ -3028,17 +3039,17 @@
#If we've specified a new owner and that user can't modify the ticket
elsif ( !$NewOwnerObj->HasRight( Right => 'OwnTicket', Object => $self ) ) {
+ $RT::Handle->Rollback();
return ( 0, $self->loc("That user may not own tickets in that queue") );
}
# If the ticket has an owner and it's the new owner, we don't need
# To do anything
elsif ( $NewOwnerObj->Id == $OldOwnerObj->Id ) {
+ $RT::Handle->Rollback();
return ( 0, $self->loc("That user already owns that ticket") );
}
- $RT::Handle->BeginTransaction();
-
# Delete the owner in the owner group, then add a new one
# TODO: is this safe? it's not how we really want the API to work
# for most things, but it's fast.
@@ -3073,8 +3084,6 @@
return ( 0, $self->loc("Could not change owner. ") . $msg );
}
- $RT::Handle->Commit();
-
($val, $msg) = $self->_NewTransaction(
Type => $Type,
Field => 'Owner',
@@ -3086,9 +3095,14 @@
if ( $val ) {
$msg = $self->loc( "Owner changed from [_1] to [_2]",
$OldOwnerObj->Name, $NewOwnerObj->Name );
+ }
+ else {
+ $RT::Handle->Rollback();
+ return ( 0, $msg );
+ }
- # TODO: make sure the trans committed properly
- }
+ $RT::Handle->Commit();
+
return ( $val, $msg );
}
==== BEGIN SVK PATCH BLOCK ====
Version: svk 1.08 (linux)
eJytVktr5EYQ1i3LEHLJKbfG1mIbVjNS6zEPx8Psbmw2xMTLeDaHhDC0Wq1R2xpJtHr8wDL4weSQ
X2AILAkhJIeQew7J78gtfyXVPQ/bJGZ3IWKQW11V31dVXV3lHTHY7DlVt2tXpmNX+1981um8JJIm
jx2vMoOKRVzmwvSrlB2x1HSrNB+ZXpWRMQNpmU8EVQtJxIhJteD0kMlu1wG41gxuW0MsYDVqSGSe
lWZbww+lYMx0Kq/nVz1X/Yam065KBhINOxTsiJc8z4C46bgBaIC6A+Z5wbKhyHMJkqDtBT2sbO2K
pnnJhgocdgAW1LEJAWn9iAtGwaNTFQ0PNdTcXFP7c/NbPdBY4rj/gRMonEZ/oGneBIWXUN4igpin
zMTeHKUx0Dkc7h0xkZLTejHWtG/CdWe4WkqKIj0dSnYiI5ZKorlcXPmUNiMaunEchq5HCG2Stu1E
Drb9OGzh2ISVqoHPDeOb79Xvy2nDmL5eMS4ud4zL96c/rV58bVzh6etPboz9q78c4+rD6Q8f3xiX
q9Mfn822/jQue9OfnRvjujP9JQGRmP760UVsfPvV9Lcj4yqa/j6+KIy/D6Z/fGD2B53OC5JFKbO6
z9iIZwNBspJQCUe9vrFZqyF4zJKlsdUd7jO5S0r5qoiIZBGI0SpKc3qIZMLQrOru6u/mJFpffPAI
aX2eIUpKNttGNCHZiEXoOIH0o2PCJc9GKM6Fxp2xj0+RuZdGe8cZE3vhAdpaQC52Nu9F0c/TNCT0
UHmP5o9gciIytI7sJwtjwF9fGSREoknJBIpyVqIsl4id8FKubKC59XntIfB33f80RsdsTTDNUkpG
UhUq6N3JHkpICVsoV5FpGZdr2q//y4t3SAlJBSPRqXKmBB9he+bkneTMMqTeLIUzPVuivxv9uBzd
5rv2L/vn+XjMpbKG63Az+i5/7/rRxfUjWBpCsEy+Am+trnpDMaxvPFmSqKfPR4nUq60uWjGF+ly5
o7Gh16rSgXoVnZ/rP2dnZ6ichOgFKWcAtdpWAtnAyx3wc0AOoWgIKoggYzi5BB1zmejjJFIKHk4k
yNe0+po+zbWXgmeUFyRdA9qFhCuQWW6tkkI3gpKAtjEhKdLuoljkY6Qy8vT5NlKGtzDaONNClQCU
hwfQjVQS+zrDJXIQj7VPxcJIV5na0ej1peYki1istKHe6hAwnQDQvSycLS+kvr1bqEx4LOHket7d
pq976bzfUa/tuxSHmDXjIHAdN3T9tue3cRBQtxVj3fqxjSskYK74vZhZQgrbJh2EZB5FFrSIYgwh
VggaY9OysWU3kWN3sN1xA2TZvm3X0P4khBLRzaNQI063EGhXukUgQShDNM9gFkFbq9c0VfD2VG7Q
se0F1Q4/gQO6D6m62oKtXlNjRE1OfYuHSnO41Ox2cWViPJvKAz1a4eQyDnOmJOnjoIIZAxEkMBdh
4sLHZMIjEwdVA24nSRth0XDrgbUgg1SfzCey6cJ/Cl7oEoY9Kwiwa9meY1ttj/oWs1sE46jVcn3c
3cDVgzxe1RCyEcIMoAkrNVV/e3f76f62nn3YnrE4TZhb1HJCSi3bBZZWHBN4+S3cdFtt0vLNoPVW
3nQejqujovoHLmPZuw==
==== END SVK PATCH BLOCK ====
More information about the Rt-devel
mailing list