|
1 | 1 | """Tasks for OAuth services."""
|
2 | 2 |
|
| 3 | +import datetime |
| 4 | + |
3 | 5 | import structlog
|
4 | 6 | from allauth.socialaccount.providers import registry as allauth_registry
|
5 | 7 | from django.contrib.auth.models import User
|
| 8 | +from django.db.models.functions import ExtractIsoWeekDay |
| 9 | +from django.utils import timezone |
6 | 10 |
|
7 | 11 | from readthedocs.core.permissions import AdminPermission
|
8 | 12 | from readthedocs.core.utils.tasks import PublicTask, user_id_matches_or_superuser
|
@@ -100,7 +104,55 @@ def sync_remote_repositories_organizations(organization_slugs=None):
|
100 | 104 | )
|
101 | 105 |
|
102 | 106 |
|
103 |
| -@app.task(queue='web') |
| 107 | +@app.task( |
| 108 | + queue="web", |
| 109 | + time_limit=60 * 60 * 3, # 3h |
| 110 | + soft_time_limit=(60 * 60 * 3) - 5 * 60, # 2h 55m |
| 111 | +) |
| 112 | +def sync_active_users_remote_repositories(): |
| 113 | + """ |
| 114 | + Sync ``RemoteRepository`` for active users. |
| 115 | +
|
| 116 | + We consider active users those that logged in at least once in the last 90 days. |
| 117 | +
|
| 118 | + This task is thought to be executed daily. It checks the weekday of the |
| 119 | + last login of the user with today's weekday. If they match, the re-sync is |
| 120 | + triggered. This logic guarantees us the re-sync to be done once a week per user. |
| 121 | +
|
| 122 | + Note this is a long running task syncronizhing all the users in the same Celery process, |
| 123 | + and it will require a pretty high ``time_limit`` and ``soft_time_limit``. |
| 124 | + """ |
| 125 | + today_weekday = timezone.now().isoweekday() |
| 126 | + three_months_ago = timezone.now() - datetime.timedelta(days=90) |
| 127 | + users = User.objects.annotate(weekday=ExtractIsoWeekDay("last_login")).filter( |
| 128 | + last_login__gt=three_months_ago, |
| 129 | + socialaccount__isnull=False, |
| 130 | + weekday=today_weekday, |
| 131 | + ) |
| 132 | + |
| 133 | + users_count = users.count() |
| 134 | + log.bind(total_users=users_count) |
| 135 | + log.info("Triggering re-sync of RemoteRepository for active users.") |
| 136 | + |
| 137 | + for i, user in enumerate(users): |
| 138 | + log.bind( |
| 139 | + user_username=user.username, |
| 140 | + progress=f"{i}/{users_count}", |
| 141 | + ) |
| 142 | + |
| 143 | + # Log an update every 50 users |
| 144 | + if i % 50 == 0: |
| 145 | + log.info("Progress on re-syncing RemoteRepository for active users.") |
| 146 | + |
| 147 | + try: |
| 148 | + # NOTE: sync all the users/repositories in the same Celery process. |
| 149 | + # Do not trigger a new task per user. |
| 150 | + sync_remote_repositories(user.pk) |
| 151 | + except Exception: |
| 152 | + log.exception("There was a problem re-syncing RemoteRepository.") |
| 153 | + |
| 154 | + |
| 155 | +@app.task(queue="web") |
104 | 156 | def attach_webhook(project_pk, user_pk, integration=None):
|
105 | 157 | """
|
106 | 158 | Add post-commit hook on project import.
|
|
0 commit comments