Skip to content

Commit 807e29c

Browse files
OAuth: resync RemoteRepository weekly for active users (#9410)
* OAuth: resync `RemoteRepository` weekly for active users Trigger a daily task to compare user's last login isoweekday with today's isoweekday for all active users. If they matches, we resync the `RemoteRepository` for this user. This logic is the same as "resync `RemoteRepository` once a week per each user". We consider active users those that have logged in at least once in the last 90 days. Related: #8229 Related: #9409 * Crontab schedule Co-authored-by: Eric Holscher <[email protected]> * OAuth: re-sync `RemoteRepository` in 1 Celery process only Weekly resync will use only one Celery process to avoid backing up our queue. Co-authored-by: Eric Holscher <[email protected]>
1 parent 2eda61c commit 807e29c

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

readthedocs/oauth/tasks.py

+53-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
"""Tasks for OAuth services."""
22

3+
import datetime
4+
35
import structlog
46
from allauth.socialaccount.providers import registry as allauth_registry
57
from django.contrib.auth.models import User
8+
from django.db.models.functions import ExtractIsoWeekDay
9+
from django.utils import timezone
610

711
from readthedocs.core.permissions import AdminPermission
812
from readthedocs.core.utils.tasks import PublicTask, user_id_matches_or_superuser
@@ -100,7 +104,55 @@ def sync_remote_repositories_organizations(organization_slugs=None):
100104
)
101105

102106

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")
104156
def attach_webhook(project_pk, user_pk, integration=None):
105157
"""
106158
Add post-commit hook on project import.

readthedocs/settings/base.py

+5
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,11 @@ def TEMPLATES(self):
447447
'schedule': crontab(minute=0, hour=1),
448448
'options': {'queue': 'web'},
449449
},
450+
'every-day-resync-remote-repositories': {
451+
'task': 'readthedocs.oauth.tasks.sync_active_users_remote_repositories',
452+
'schedule': crontab(minute=30, hour=2),
453+
'options': {'queue': 'web'},
454+
}
450455
}
451456

452457
MULTIPLE_BUILD_SERVERS = [CELERY_DEFAULT_QUEUE]

0 commit comments

Comments
 (0)