diff --git a/corehq/motech/repeaters/management/commands/set_repeater_workers.py b/corehq/motech/repeaters/management/commands/set_repeater_workers.py new file mode 100644 index 000000000000..c9cd2ece923f --- /dev/null +++ b/corehq/motech/repeaters/management/commands/set_repeater_workers.py @@ -0,0 +1,37 @@ +from django.conf import settings +from django.core.management.base import BaseCommand + +from corehq.motech.repeaters.models import Repeater + + +class Command(BaseCommand): + help = f""" + Sets Repeater.max_workers, which is the number of Celery workers + allocated to sending a batch of repeat records. Set to "0" to use + the default, which is {settings.DEFAULT_REPEATER_WORKERS}. Set to + "1" to send repeat records chronologically, one at a time. The + maximum value is {settings.MAX_REPEATER_WORKERS}. + """ + + def add_arguments(self, parser): + parser.add_argument('domain') + parser.add_argument('repeater_id') + parser.add_argument('max_workers', type=int) + + def handle(self, domain, repeater_id, max_workers, *args, **options): + if not 0 <= max_workers <= settings.MAX_REPEATER_WORKERS: + self.stderr.write( + 'max_workers must be between 0 and ' + f'{settings.MAX_REPEATER_WORKERS}.' + ) + # Use QuerySet.update() to avoid a race condition if the + # repeater is currently in use. + rows = ( + Repeater.objects + .filter(domain=domain, id=repeater_id) + .update(max_workers=max_workers) + ) + if not rows: + self.stderr.write( + f'Repeater {repeater_id} was not found in domain {domain}.' + ) diff --git a/settings.py b/settings.py index ac168f7c65ff..5d04b4e695c9 100755 --- a/settings.py +++ b/settings.py @@ -632,7 +632,7 @@ # repeater can use to send repeat records at the same time. This is a # guardrail to prevent one repeater from hogging repeat_record_queue # workers and to ensure that repeaters are iterated fairly. -MAX_REPEATER_WORKERS = 144 +MAX_REPEATER_WORKERS = 79 # websockets config WEBSOCKET_URL = '/ws/'