Skip to content

Commit

Permalink
use ThreadPoolExecutor to run external repos requests in parallel
Browse files Browse the repository at this point in the history
On ProjectDetail pages with several repos the lookup for private/public
status was noticably slowing the page down.  This switches to
ThreadPoolExecutor to run through them in parallel (mostly).
  • Loading branch information
ghickman committed Jan 27, 2022
1 parent dce09c6 commit dc2543c
Showing 1 changed file with 17 additions and 5 deletions.
22 changes: 17 additions & 5 deletions jobserver/views/projects.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import concurrent
import operator

import requests
from django.contrib import messages
from django.contrib.auth.decorators import login_required
Expand All @@ -19,6 +22,9 @@
from ..models import Org, Project, ProjectInvitation, ProjectMembership, Snapshot, User


repo_thread_pool = concurrent.futures.ThreadPoolExecutor(thread_name_prefix="get_repo")


@method_decorator(login_required, name="dispatch")
class ProjectAcceptInvite(View):
def get(self, request, *args, **kwargs):
Expand Down Expand Up @@ -125,13 +131,15 @@ def get_context_data(self, **kwargs):

workspaces = self.object.workspaces.order_by("is_archived", "name")

repos = sorted(set(workspaces.values_list("repo", flat=True)))
repos = set(workspaces.values_list("repo", flat=True))

return super().get_context_data(**kwargs) | {
"can_create_workspaces": can_create_workspaces,
"can_manage_members": can_manage_members,
"outputs": self.get_outputs(workspaces),
"repos": list(self.get_repos(repos)),
"repos": list(
sorted(self.iter_repos(repos), key=operator.itemgetter("name"))
),
"workspaces": workspaces,
}

Expand Down Expand Up @@ -161,21 +169,25 @@ def get_outputs(self, workspaces):

return Snapshot.objects.filter(pk__in=snapshot_pks).order_by("-published_at")

def get_repos(self, repo_urls):
for url in repo_urls:
def iter_repos(self, repo_urls):
def get_repo(url):
f = furl(url)

try:
is_private = get_repo_is_private(*f.path.segments)
except requests.HTTPError:
is_private = None

yield {
return {
"name": f.path.segments[1],
"url": url,
"is_private": is_private,
}

futures = [repo_thread_pool.submit(get_repo, url=url) for url in repo_urls]
for future in concurrent.futures.as_completed(futures):
yield future.result()


class ProjectInvitationCreate(CreateView):
form_class = ProjectInvitationForm
Expand Down

0 comments on commit dc2543c

Please sign in to comment.