@@ -589,6 +589,33 @@ def TEMPLATES(self):
589589 USE_I18N = True
590590 USE_L10N = True
591591
592+ BUILD_TIME_LIMIT = 900 # seconds
593+
594+ @property
595+ def BUILD_MEMORY_LIMIT (self ):
596+ """
597+ Set build memory limit dynamically, if in production, based on system memory.
598+
599+ We do this to avoid having separate build images. This assumes 1 build
600+ process per server, which will be allowed to consume all available
601+ memory.
602+ """
603+ # Our normal default
604+ default_memory_limit = "7g"
605+
606+ # Only run on our servers
607+ if self .RTD_IS_PRODUCTION :
608+ total_memory , memory_limit = self ._get_build_memory_limit ()
609+
610+ memory_limit = memory_limit or default_memory_limit
611+ log .info (
612+ "Using dynamic build limits." ,
613+ hostname = socket .gethostname (),
614+ memory = memory_limit ,
615+ )
616+ return memory_limit
617+
618+
592619 # Celery
593620 CELERY_APP_NAME = "readthedocs"
594621 CELERY_ALWAYS_EAGER = True
@@ -605,7 +632,7 @@ def TEMPLATES(self):
605632 # https://github.com/readthedocs/readthedocs.org/issues/12317#issuecomment-3070950434
606633 # https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/redis.html#visibility-timeout
607634 BROKER_TRANSPORT_OPTIONS = {
608- 'visibility_timeout' : 18000 , # 5 hours
635+ 'visibility_timeout' : BUILD_TIME_LIMIT * 1.15 , # 15% more than the build time limit
609636 }
610637
611638 CELERY_DEFAULT_QUEUE = "celery"
@@ -721,7 +748,13 @@ def TEMPLATES(self):
721748 # since we can't read their config file image choice before cloning
722749 RTD_DOCKER_CLONE_IMAGE = RTD_DOCKER_BUILD_SETTINGS ["os" ]["ubuntu-22.04" ]
723750
724- def _get_docker_memory_limit (self ):
751+ def _get_build_memory_limit (self ):
752+ """
753+ Return the buld memory limit based on available system memory.
754+
755+ We subtract ~1000Mb for overhead of processes and base system, and set
756+ the build time as proportional to the memory limit.
757+ """
725758 try :
726759 total_memory = int (
727760 subprocess .check_output (
@@ -735,47 +768,6 @@ def _get_docker_memory_limit(self):
735768 # int and raise a ValueError
736769 log .exception ("Failed to get memory size, using defaults Docker limits." )
737770
738- # Coefficient used to determine build time limit, as a percentage of total
739- # memory. Historical values here were 0.225 to 0.3.
740- DOCKER_TIME_LIMIT_COEFF = 0.25
741-
742- @property
743- def DOCKER_LIMITS (self ):
744- """
745- Set docker limits dynamically, if in production, based on system memory.
746-
747- We do this to avoid having separate build images. This assumes 1 build
748- process per server, which will be allowed to consume all available
749- memory.
750-
751- We subtract 750MiB for overhead of processes and base system, and set
752- the build time as proportional to the memory limit.
753- """
754- # Our normal default
755- limits = {
756- "memory" : "2g" ,
757- "time" : 900 ,
758- }
759-
760- # Only run on our servers
761- if self .RTD_IS_PRODUCTION :
762- total_memory , memory_limit = self ._get_docker_memory_limit ()
763- if memory_limit :
764- limits = {
765- "memory" : f"{ memory_limit } m" ,
766- "time" : max (
767- limits ["time" ],
768- round (total_memory * self .DOCKER_TIME_LIMIT_COEFF , - 2 ),
769- ),
770- }
771- log .info (
772- "Using dynamic docker limits." ,
773- hostname = socket .gethostname (),
774- memory = limits ["memory" ],
775- time = limits ["time" ],
776- )
777- return limits
778-
779771 # Allauth
780772 ACCOUNT_ADAPTER = "readthedocs.core.adapters.AccountAdapter"
781773 SOCIALACCOUNT_ADAPTER = 'readthedocs.core.adapters.SocialAccountAdapter'
0 commit comments