Skip to content

Commit a6c2481

Browse files
committed
Use Docker time limit for max lock age
When building a project, if it tooks more than `REPO_LOCK_SECONDS` and while building after that time another build is triggered for the same Version and the same builder takes the task the lock will be considered "old" and remove and taken by the new build. This will end up in a collision when accessing the files and it could raise an exception like `IOError: [Errno 26] Text file busy`. Also, it could fail with another unexpected reasons. This PR increases the `max_lock_age` to the same value assigned for the project to end the build in order: * custom container time limit or, * `settings.DOCKER_LIMITS['time']` or, * `settings.REPO_LOCK_SECONDS` or, * 30 seconds Related to #1609
1 parent c01e8eb commit a6c2481

File tree

2 files changed

+25
-13
lines changed

2 files changed

+25
-13
lines changed

readthedocs/projects/models.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -651,8 +651,28 @@ def vcs_repo(self, version=LATEST, environment=None):
651651
repo = backend(self, version, environment)
652652
return repo
653653

654-
def repo_nonblockinglock(self, version, max_lock_age=5):
655-
return NonBlockingLock(project=self, version=version, max_lock_age=max_lock_age)
654+
def repo_nonblockinglock(self, version, max_lock_age=None):
655+
"""
656+
Return a ``NonBlockingLock`` or raise an exception.
657+
658+
:param version: project's version that want to get the lock for.
659+
:param max_lock_age: time (in seconds) to consider the lock's age is old
660+
and grab it anyway. It default to the ``container_time_limit`` of
661+
the project or the default ``DOCKER_LIMITS['time']`` or
662+
``REPO_LOCK_SECONDS`` or 30
663+
"""
664+
if max_lock_age is None:
665+
max_lock_age = (
666+
self.container_time_limit or
667+
getattr(settings, 'DOCKER_LIMITS', {}).get('time') or
668+
getattr(settings, 'REPO_LOCK_SECONDS', 30)
669+
)
670+
671+
return NonBlockingLock(
672+
project=self,
673+
version=version,
674+
max_lock_age=max_lock_age,
675+
)
656676

657677
def repo_lock(self, version, timeout=5, polling_interval=5):
658678
return Lock(self, version, timeout, polling_interval)

readthedocs/projects/tasks.py

+3-11
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,7 @@ def sync_repo(self):
113113
),
114114
)
115115

116-
with self.project.repo_nonblockinglock(
117-
version=self.version,
118-
max_lock_age=getattr(settings, 'REPO_LOCK_SECONDS', 30)):
119-
116+
with self.project.repo_nonblockinglock(version=self.version):
120117
# Get the actual code on disk
121118
try:
122119
before_vcs.send(sender=self.version)
@@ -649,10 +646,7 @@ def setup_python_environment(self):
649646
"""
650647
self.build_env.update_build(state=BUILD_STATE_INSTALLING)
651648

652-
with self.project.repo_nonblockinglock(
653-
version=self.version,
654-
max_lock_age=getattr(settings, 'REPO_LOCK_SECONDS', 30)):
655-
649+
with self.project.repo_nonblockinglock(version=self.version):
656650
# Check if the python version/build image in the current venv is the
657651
# same to be used in this build and if it differs, wipe the venv to
658652
# avoid conflicts.
@@ -682,9 +676,7 @@ def build_docs(self):
682676
before_build.send(sender=self.version)
683677

684678
outcomes = defaultdict(lambda: False)
685-
with self.project.repo_nonblockinglock(
686-
version=self.version,
687-
max_lock_age=getattr(settings, 'REPO_LOCK_SECONDS', 30)):
679+
with self.project.repo_nonblockinglock(version=self.version):
688680
outcomes['html'] = self.build_docs_html()
689681
outcomes['search'] = self.build_docs_search()
690682
outcomes['localmedia'] = self.build_docs_localmedia()

0 commit comments

Comments
 (0)