Merge lp:~edwin-grubbs/launchpad/bug-615654-fillTeamParticipation-timeout into lp:launchpad

Proposed by Edwin Grubbs
Status: Merged
Approved by: Māris Fogels
Approved revision: no longer in the source branch.
Merged at revision: 11374
Proposed branch: lp:~edwin-grubbs/launchpad/bug-615654-fillTeamParticipation-timeout
Merge into: lp:launchpad
Diff against target: 102 lines (+44/-15)
1 file modified
lib/lp/registry/model/teammembership.py (+44/-15)
To merge this branch: bzr merge lp:~edwin-grubbs/launchpad/bug-615654-fillTeamParticipation-timeout
Reviewer Review Type Date Requested Status
Māris Fogels (community) Approve
Review via email: mp+32895@code.launchpad.net

Description of the change

Summary
-------

This branch optimizes the _fillTeamParticipation() method to help
prevent timeouts when approving lots of teams as members. A followup
branch will queue the sending of emails, which should provide a more
complete solution to timeouts.

Tests
-----

./bin/test -vv -t member

Demo and Q/A
------------

* Open http://staging.launchpad.net/~ubuntu-br/+editproposedmembers
  * Try approving 25 teams.
  * There shouldn't be a timeout.

To post a comment you must log in.
Revision history for this message
Māris Fogels (mars) wrote :

Hi Edwin,

This change looks good. r=mars.

I wish there was some better way to load test changes like this. I imagine one could do something creative by pointing the API, LaunchpadObjectFactory, curl, and a stepper algorithm at staging >:)

Maris

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/model/teammembership.py'
--- lib/lp/registry/model/teammembership.py 2010-07-16 14:58:17 +0000
+++ lib/lp/registry/model/teammembership.py 2010-08-17 16:33:03 +0000
@@ -11,7 +11,6 @@
11 ]11 ]
1212
13from datetime import datetime, timedelta13from datetime import datetime, timedelta
14import itertools
15import pytz14import pytz
1615
17from storm.locals import Store16from storm.locals import Store
@@ -167,8 +166,10 @@
167 simple_sendmail(from_addr, address, subject, msg)166 simple_sendmail(from_addr, address, subject, msg)
168167
169 def canChangeStatusSilently(self, user):168 def canChangeStatusSilently(self, user):
170 """Ensure that the user is in the Launchpad Administrators group before169 """Ensure that the user is in the Launchpad Administrators group.
171 silently making changes to their membership status."""170
171 Then the user can silently make changes to their membership status.
172 """
172 return user.inTeam(getUtility(ILaunchpadCelebrities).admin)173 return user.inTeam(getUtility(ILaunchpadCelebrities).admin)
173174
174 def canChangeExpirationDate(self, person):175 def canChangeExpirationDate(self, person):
@@ -288,8 +289,8 @@
288289
289 if silent and not self.canChangeStatusSilently(user):290 if silent and not self.canChangeStatusSilently(user):
290 raise UserCannotChangeMembershipSilently(291 raise UserCannotChangeMembershipSilently(
291 "Only Launchpad administrators may change membership statuses "292 "Only Launchpad administrators may change membership "
292 "silently.")293 "statuses silently.")
293294
294 approved = TeamMembershipStatus.APPROVED295 approved = TeamMembershipStatus.APPROVED
295 admin = TeamMembershipStatus.ADMIN296 admin = TeamMembershipStatus.ADMIN
@@ -536,7 +537,7 @@
536 conditions = [537 conditions = [
537 TeamMembership.dateexpires <= when,538 TeamMembership.dateexpires <= when,
538 TeamMembership.status.is_in(539 TeamMembership.status.is_in(
539 [TeamMembershipStatus.ADMIN, TeamMembershipStatus.APPROVED])540 [TeamMembershipStatus.ADMIN, TeamMembershipStatus.APPROVED]),
540 ]541 ]
541 if exclude_autorenewals:542 if exclude_autorenewals:
542 # Avoid circular import.543 # Avoid circular import.
@@ -718,20 +719,48 @@
718 return Store.of(person).find(Person, "id IN (%s)" % query)719 return Store.of(person).find(Person, "id IN (%s)" % query)
719720
720721
721def _fillTeamParticipation(member, team):722def _fillTeamParticipation(member, accepting_team):
722 """Add relevant entries in TeamParticipation for given member and team.723 """Add relevant entries in TeamParticipation for given member and team.
723724
724 Add a tuple "member, team" in TeamParticipation for the given team and all725 Add a tuple "member, team" in TeamParticipation for the given team and all
725 of its superteams. More information on how to use the TeamParticipation726 of its superteams. More information on how to use the TeamParticipation
726 table can be found in the TeamParticipationUsage spec.727 table can be found in the TeamParticipationUsage spec.
727 """728 """
728 members = [member]
729 if member.isTeam():729 if member.isTeam():
730 # The given member is, in fact, a team, and in this case we must730 # The submembers will be all the members of the team that is
731 # add all of its members to the given team and to its superteams.731 # being added as a member. The superteams will be all the teams
732 members.extend(member.allmembers)732 # that the accepting_team belongs to, so all the members will
733 # also be joining the superteams indirectly. It is important to
734 # remember that teams are members of themselves, so the member
735 # team will also be one of the submembers, and the
736 # accepting_team will also be one of the superteams.
737 query = """
738 INSERT INTO TeamParticipation (person, team)
739 SELECT submember.person, superteam.team
740 FROM TeamParticipation submember
741 JOIN TeamParticipation superteam ON TRUE
742 WHERE submember.team = %(member)d
743 AND superteam.person = %(accepting_team)d
744 AND NOT EXISTS (
745 SELECT 1
746 FROM TeamParticipation
747 WHERE person = submember.person
748 AND team = superteam.team
749 )
750 """ % dict(member=member.id, accepting_team=accepting_team.id)
751 else:
752 query = """
753 INSERT INTO TeamParticipation (person, team)
754 SELECT %(member)d, superteam.team
755 FROM TeamParticipation superteam
756 WHERE superteam.person = %(accepting_team)d
757 AND NOT EXISTS (
758 SELECT 1
759 FROM TeamParticipation
760 WHERE person = %(member)d
761 AND team = superteam.team
762 )
763 """ % dict(member=member.id, accepting_team=accepting_team.id)
733764
734 for m in members:765 store = Store.of(member)
735 for t in itertools.chain(team.super_teams, [team]):766 store.execute(query)
736 if not m.hasParticipationEntryFor(t):
737 TeamParticipation(person=m, team=t)