[Rt-devel] RT 3.4 / Postgres / Search UI performance

Jesse Vincent jesse at bestpractical.com
Thu Jan 13 23:38:14 EST 2005


I've spent the evening doing some stress testing / performance work on
RT 3.4's search page under postgres. I have a proposed patch, but I
really need folks to beat on it to see if it improves things
significantly and whether it seems to actually do the right thing.

It's attached to this message. If folks see a speed boost without 
errors, it'll make it into 3.4.0. But this means taht I need sites
running postgres (and other databases) to test it out. It's likey that
the issue in question only manifests with a large number of queues. 


=== html/Elements/SelectOwner
==================================================================
--- html/Elements/SelectOwner  (revision 2678)
+++ html/Elements/SelectOwner  (local)
@@ -67,25 +67,19 @@
 	@objects = keys %{$cfqueues};
 }
 else {
-        my $Queues = RT::Queues->new($session{CurrentUser});
-	$Queues->UnLimit();
-	while (my $Queue = $Queues->Next()) {
-	      push( @objects, $Queue );
-        }
+    # Let's check rights on an empty queue object. that will do a search for any queue.
+    my $queue = RT::Queue->new($session{'CurrentUser'});
+	      push( @objects, $queue );
 }
 
 my %user_uniq_hash;
 foreach my $object (@objects) {
 	my $Users = RT::Users->new($session{CurrentUser});
-        $Users->WhoHaveRight(Right => 'OwnTicket',
-                     Object => $object,
-                     IncludeSystemRights => 1,
-                     IncludeSuperusers => 0);
-        while (my $User = $Users->Next()) {
+        $Users->WhoHaveRight(Right => 'OwnTicket', Object => $object, IncludeSystemRights => 1, IncludeSuperusers => 0); while (my $User = $Users->Next()) {
 	      $user_uniq_hash{$User->Id()} = $User;
         }
 }
- at users = sort { uc($a->Name) cmp uc($b->Name) } values %user_uniq_hash;
+ at users = sort { uc($a->Name) cmp uc($b->Name) } values %user_uniq_hash; 
 </%INIT>
 
 <%ARGS>
=== lib/RT/Users_Overlay.pm
==================================================================
--- lib/RT/Users_Overlay.pm  (revision 2678)
+++ lib/RT/Users_Overlay.pm  (local)
@@ -239,6 +239,7 @@
 or as members of groups
 
 
+If passed a queue object, with no id, it will find users who have that right for _any_ queue
 
 
 
@@ -246,38 +247,121 @@
 
 sub WhoHaveRight {
     my $self = shift;
-    my %args = ( Right                  => undef,
-                 Object                 => undef,
-                 IncludeSystemRights    => undef,
-                 IncludeSuperusers      => undef,
-                 IncludeSubgroupMembers => 1,
-                 @_ );
+    my %args = (
+        Right                  => undef,
+        Object                 => undef,
+        IncludeSystemRights    => undef,
+        IncludeSuperusers      => undef,
+        IncludeSubgroupMembers => 1,
+        @_
+    );
 
-    if (defined $args{'ObjectType'} || defined $args{'ObjectId'}) {
-        $RT::Logger->crit("$self WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
-        return(undef);
+    if ( defined $args{'ObjectType'} || defined $args{'ObjectId'} ) {
+        $RT::Logger->crit( "$self WhoHaveRight called with the Obsolete ObjectId/ObjectType API");
+        return (undef);
     }
-        my @privgroups;
-        my $Groups = RT::Groups->new($RT::SystemUser);
-        $Groups->WithRight(Right=> $args{'Right'},
-                     Object => $args{'Object'},
-                     IncludeSystemRights => $args{'IncludeSystemRights'},
-                     IncludeSuperusers => $args{'IncludeSuperusers'});
-        while (my $Group = $Groups->Next()) {
-                push @privgroups, $Group->Id();
-        }
+    
 
+    # Find only members of groups that have the right.
 
-    if (@privgroups) {
-        $self->WhoBelongToGroups(Groups => \@privgroups,
-                                 IncludeSubgroupMembers => $args{'IncludeSubgroupMembers'});
+    my $acl       = $self->NewAlias('ACL');
+    my $groups    = $self->NewAlias('Groups');
+    my $userprinc = $self->{'princalias'};
+
+# The cachedgroupmembers table is used for unrolling group memberships to allow fast lookups
+# if we bind to CachedGroupMembers, we'll find all members of groups recursively.
+# if we don't we'll find only 'direct' members of the group in question
+    my $cgm;
+
+    if ( $args{'IncludeSubgroupMembers'} ) {
+        $cgm = $self->NewAlias('CachedGroupMembers');
     }
     else {
-	# We don't have any group that matches -- make it impossible.
-	$self->Limit( FIELD => 'Id', VALUE => 'IS', OPERATOR => 'NULL' );
+        $cgm = $self->NewAlias('GroupMembers');
     }
+
+#Tie the users we're returning ($userprinc) to the groups that have rights granted to them ($groupprinc)
+    $self->Join(
+        ALIAS1 => $cgm,
+        FIELD1 => 'MemberId',
+        ALIAS2 => $userprinc,
+        FIELD2 => 'id'
+    );
+
+    $self->Join(
+        ALIAS1 => $groups,
+        FIELD1 => 'id',
+        ALIAS2 => $cgm,
+        FIELD2 => 'GroupId'
+    );
+
+# {{{ Find only rows where the right granted is the one we're looking up or _possibly_ superuser
+    $self->Limit(
+        ALIAS    => $acl,
+        FIELD    => 'RightName',
+        OPERATOR => ( $args{Right} ? '=' : 'IS NOT' ),
+        VALUE => $args{Right} || 'NULL',
+        ENTRYAGGREGATOR => 'OR'
+    );
+
+    if ( $args{'IncludeSuperusers'} and $args{'Right'} ) {
+        $self->Limit(
+            ALIAS           => $acl,
+            FIELD           => 'RightName',
+            OPERATOR        => '=',
+            VALUE           => 'SuperUser',
+            ENTRYAGGREGATOR => 'OR'
+        );
+    }
+
+    # }}}
+
+    my ( $or_check_ticket_roles, $or_check_roles );
+    my $which_object = "$acl.ObjectType = 'RT::System'";
+
+    if ( defined $args{'Object'} ) {
+        if ( ref( $args{'Object'} ) eq 'RT::Ticket' ) {
+            $or_check_ticket_roles = " OR ( $groups.Domain = 'RT::Ticket-Role' AND $groups.Instance = " . $args{'Object'}->Id . ") ";
+
+# If we're looking at ticket rights, we also want to look at the associated queue rights.
+# this is a little bit hacky, but basically, now that we've done the ticket roles magic,
+# we load the queue object and ask all the rest of our questions about the queue.
+            $args{'Object'} = $args{'Object'}->QueueObj;
+        }
+
+        # TODO XXX This really wants some refactoring
+        if ( ref( $args{'Object'} ) eq 'RT::Queue' ) {
+            $or_check_roles = " OR ( ( ($groups.Domain = 'RT::Queue-Role' ";
+            $or_check_roles .= "AND $groups.Instance = " . $args{'Object'}->id if ( $args{'Object'}->id );
+            $or_check_roles .= ") $or_check_ticket_roles ) " . " AND $groups.Type = $acl.PrincipalType) ";
+        }
+        if ( $args{'IncludeSystemRights'} ) {
+            $which_object .= ' OR ';
+        }
+        else {
+            $which_object = '';
+        }
+        $which_object .= " ($acl.ObjectType = '" . ref( $args{'Object'} ) . "'";
+        if ( $args{'Object'}->id ) {
+            $which_object .= " AND $acl.ObjectId = " . $args{'Object'}->id;
+        }
+
+        $which_object .=  ") ";
+    }
+    $self->_AddSubClause( "WhichObject", "($which_object)" );
+    $self->_AddSubClause(
+        "WhichGroup",
+            qq{ ( (    $acl.PrincipalId = $groups.id AND $acl.PrincipalType = 'Group' 
+                AND (   $groups.Domain = 'SystemInternal' OR $groups.Domain = 'UserDefined' OR $groups.Domain = 'ACLEquivalence')) 
+                $or_check_roles) }
+    );
+    # only include regular RT users
+    $self->LimitToEnabled;
+
+    # no system user
+    $self->Limit( ALIAS => $userprinc, FIELD => 'id', OPERATOR => '!=', VALUE => $RT::SystemUser->id);
+
 }
-
 # }}}
 
 # {{{ WhoBelongToGroups 
@@ -310,20 +394,14 @@
         $cgm = $self->NewAlias('GroupMembers');
     }
 
-    # {{{ Tie the users we're returning ($userprinc) to the groups that have rights granted to them ($groupprinc)
+    #Tie the users we're returning ($userprinc) to the groups that have rights granted to them ($groupprinc)
     $self->Join( ALIAS1 => $cgm, FIELD1 => 'MemberId',
                  ALIAS2 => $userprinc, FIELD2 => 'id' );
-    # }}} 
 
- #   my $and_check_groups = "($cgm.GroupId = NULL";
     foreach my $groupid (@{$args{'Groups'}}) {
         $self->Limit(ALIAS => $cgm, FIELD => 'GroupId', VALUE => $groupid, QUOTEVALUE => 0, ENTRYAGGREGATOR=> 'OR')
 
-        #$and_check_groups .= " OR $cgm.GroupId = $groupid";
     }
-    #$and_check_groups .= ")";
-
-    #$self->_AddSubClause("WhichGroup", $and_check_groups);
 }
 # }}}
 
-- 


More information about the Rt-devel mailing list