Limit the default value of [api]api_workers to 4

Each ironic-api process consumes non-negligible amount of RAM,
defaulting to CPU core count may result in many hundres of megabytes
occupied by ironic-api processes. Limit the default value to 4
and let people who actually need more than that pick their value.

Change-Id: I5aefa8c6c7aadc56aea151647e1c0a5af54ada4c
This commit is contained in:
Dmitry Tantsur 2020-11-03 16:33:14 +01:00
parent 731f0768c4
commit c9c492725e
4 changed files with 32 additions and 8 deletions

View File

@ -20,6 +20,9 @@ from ironic.common.i18n import _
from ironic.conf import CONF from ironic.conf import CONF
_MAX_DEFAULT_WORKERS = 4
class WSGIService(service.ServiceBase): class WSGIService(service.ServiceBase):
"""Provides ability to launch ironic API from wsgi app.""" """Provides ability to launch ironic API from wsgi app."""
@ -32,8 +35,12 @@ class WSGIService(service.ServiceBase):
""" """
self.name = name self.name = name
self.app = app.VersionSelectorApplication() self.app = app.VersionSelectorApplication()
self.workers = (CONF.api.api_workers self.workers = (
or processutils.get_worker_count()) CONF.api.api_workers
# NOTE(dtantsur): each worker takes a substantial amount of memory,
# so we don't want to end up with dozens of them.
or min(processutils.get_worker_count(), _MAX_DEFAULT_WORKERS)
)
if self.workers and self.workers < 1: if self.workers and self.workers < 1:
raise exception.ConfigInvalid( raise exception.ConfigInvalid(
_("api_workers value of %d is invalid, " _("api_workers value of %d is invalid, "

View File

@ -44,9 +44,9 @@ opts = [
), ),
cfg.IntOpt('api_workers', cfg.IntOpt('api_workers',
help=_('Number of workers for OpenStack Ironic API service. ' help=_('Number of workers for OpenStack Ironic API service. '
'The default is equal to the number of CPUs available ' 'The default is equal to the number of CPUs available, '
'if that can be determined, else a default worker ' 'but not more than 4. One worker is used if the CPU '
'count of 1 is returned.')), 'number cannot be detected.')),
cfg.BoolOpt('enable_ssl_api', cfg.BoolOpt('enable_ssl_api',
default=False, default=False,
help=_("Enable the integrated stand-alone API to service " help=_("Enable the integrated stand-alone API to service "

View File

@ -23,12 +23,12 @@ CONF = cfg.CONF
class TestWSGIService(base.TestCase): class TestWSGIService(base.TestCase):
@mock.patch.object(processutils, 'get_worker_count', lambda: 2)
@mock.patch.object(wsgi_service.wsgi, 'Server', autospec=True) @mock.patch.object(wsgi_service.wsgi, 'Server', autospec=True)
def test_workers_set_default(self, mock_server): def test_workers_set_default(self, mock_server):
service_name = "ironic_api" service_name = "ironic_api"
test_service = wsgi_service.WSGIService(service_name) test_service = wsgi_service.WSGIService(service_name)
self.assertEqual(processutils.get_worker_count(), self.assertEqual(2, test_service.workers)
test_service.workers)
mock_server.assert_called_once_with(CONF, service_name, mock_server.assert_called_once_with(CONF, service_name,
test_service.app, test_service.app,
host='0.0.0.0', host='0.0.0.0',
@ -41,11 +41,19 @@ class TestWSGIService(base.TestCase):
test_service = wsgi_service.WSGIService("ironic_api") test_service = wsgi_service.WSGIService("ironic_api")
self.assertEqual(8, test_service.workers) self.assertEqual(8, test_service.workers)
@mock.patch.object(processutils, 'get_worker_count', lambda: 3)
@mock.patch.object(wsgi_service.wsgi, 'Server', autospec=True) @mock.patch.object(wsgi_service.wsgi, 'Server', autospec=True)
def test_workers_set_zero_setting(self, mock_server): def test_workers_set_zero_setting(self, mock_server):
self.config(api_workers=0, group='api') self.config(api_workers=0, group='api')
test_service = wsgi_service.WSGIService("ironic_api") test_service = wsgi_service.WSGIService("ironic_api")
self.assertEqual(processutils.get_worker_count(), test_service.workers) self.assertEqual(3, test_service.workers)
@mock.patch.object(processutils, 'get_worker_count', lambda: 42)
@mock.patch.object(wsgi_service.wsgi, 'Server', autospec=True)
def test_workers_set_default_limit(self, mock_server):
self.config(api_workers=0, group='api')
test_service = wsgi_service.WSGIService("ironic_api")
self.assertEqual(4, test_service.workers)
@mock.patch.object(wsgi_service.wsgi, 'Server', autospec=True) @mock.patch.object(wsgi_service.wsgi, 'Server', autospec=True)
def test_workers_set_negative_setting(self, mock_server): def test_workers_set_negative_setting(self, mock_server):

View File

@ -0,0 +1,9 @@
---
upgrade:
- |
The default value of ``[api]api_workers`` is now limited to 4. Set it
explicitly if you need a higher value.
fixes:
- |
No longer launches too many API workers on systems with a lot of CPU
cores by default.