Skip to content

Commit

Permalink
Improved cheapening abstractions.
Browse files Browse the repository at this point in the history
  • Loading branch information
idlesign committed Oct 16, 2017
1 parent 279eecd commit b34bb77
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 221 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ uwsgiconf changelog

Unreleased
----------
+ Improved cheapening abstractions.
+ Added empire.Broodlord preset


Expand Down
7 changes: 7 additions & 0 deletions docs/source/grp_workers.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Workers
=======


.. toctree::
:maxdepth: 3

grp_workers_cheapening


.. automodule:: uwsgiconf.options.workers
:members:

5 changes: 5 additions & 0 deletions docs/source/grp_workers_cheapening.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Cheapening
==========

.. automodule:: uwsgiconf.options.workers_cheapening
:members:
19 changes: 10 additions & 9 deletions tests/options/test_workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,29 +93,30 @@ def test_cheapening(assert_lines):

assert_lines([
'cheaper-algo = manual',
], Section().cheapening.set_basic_params(cheaper_algo=Section.cheapening.algorithms.MANUAL))
], Section().cheapening.set_basic_params(cheaper_algo=Section.cheapening.algorithms.manual()))

assert_lines([
'cheaper-algo = spare',
'cheaper-overload = 20',
], Section().cheapening.set_algo_spare_params(check_interval_overload=20))
], Section().cheapening(cheaper_algo=Section.cheapening.algorithms.spare(check_interval_overload=20)))

assert_lines([
'cheaper-algo = spare2',
'cheaper-idle = 10',
], Section().cheapening.set_algo_spare2_params(check_interval_idle=10))
], Section().cheapening(cheaper_algo=Section.cheapening.algorithms.spare2(check_interval_idle=10)))

assert_lines([
'cheaper-algo = backlog',
'cheaper-overload = 30',
], Section().cheapening.set_algo_backlog_params(check_num_overload=30))
], Section().cheapening(cheaper_algo=Section.cheapening.algorithms.backlog(check_num_overload=30)))

assert_lines([
'plugin = cheaper_busyness',
'cheaper-busyness-max = 25',
], Section().cheapening.set_algo_busyness_params(busy_max=25))

assert_lines([
'plugin = cheaper_busyness',
'cheaper-busyness-backlog-step = 3',
], Section().cheapening.set_algo_busyness_emergency_params(workers_step=3))
], Section().cheapening(
cheaper_algo=Section.cheapening.algorithms.busyness(busy_max=25).set_emergency_params(workers_step=3)
))

assert_lines([
'cheaper-rss-limit-soft = 1024',
Expand Down
213 changes: 1 addition & 212 deletions uwsgiconf/options/workers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from functools import partial

from .workers_cheapening import *
from ..base import OptionsGroup
from ..utils import listify

Expand Down Expand Up @@ -409,213 +408,3 @@ def set_zerg_client_params(self, server_sockets, use_fallback_socket=None):
self._section.networking.register_socket(self._section.networking.sockets.default(socket))

return self._section


class Cheapening(OptionsGroup):
"""uWSGI provides the ability to dynamically scale
the number of running workers (adaptive process spawning) via pluggable algorithms.
.. note:: This uses master process.
"""

class algorithms(object):
"""Algorithms available to use with ``cheaper_algorithm``."""

SPARE = 'spare'
"""The **default** algorithm.
If all workers are busy for ``cheaper_overload`` seconds then uWSGI will spawn new workers.
When the load is gone it will begin stopping processes one at a time.
* http://uwsgi.readthedocs.io/en/latest/Cheaper.html#spare-cheaper-algorithm
"""

SPARE2 = 'spare2'
"""This algorithm is similar to ``spare``, but suitable for large scale
by increase workers faster and decrease workers slower.
* http://uwsgi.readthedocs.io/en/latest/Cheaper.html#spare2-cheaper-algorithm
"""

BACKLOG = 'backlog'
"""If the socket's listen queue has more than ``cheaper_overload`` requests
waiting to be processed, uWSGI will spawn new workers.
If the backlog is lower it will begin killing processes one at a time.
* http://uwsgi.readthedocs.io/en/latest/Cheaper.html#backlog-cheaper-algorithm
.. note: Only available on Linux and only on TCP sockets (not UNIX domain sockets).
"""

BUSYNESS = 'busyness'
"""Algorithm adds or removes workers based on average utilization
for a given time period. It's goal is to keep more workers than
the minimum needed available at any given time, so the app will always
have capacity for new requests.
* http://uwsgi.readthedocs.io/en/latest/Cheaper.html#busyness-cheaper-algorithm
.. note:: Requires ``cheaper_busyness`` plugin.
"""

MANUAL = 'manual'
"""Algorithm allows to adjust number of workers using Master FIFO commands.
* http://uwsgi.readthedocs.io/en/latest/MasterFIFO.html#available-commands
"""

def set_basic_params(
self, spawn_on_request=None,
cheaper_algo=None, workers_min=None, workers_startup=None, workers_step=None):
"""
:param bool spawn_on_request: Spawn workers only after the first request.
:param cheaper_algo: The algorithm to be used used for adaptive process spawning. Default: ``spare``.
See ``.algorithms``.
:param int workers_min: Minimal workers count. Enables cheaper mode (adaptive process spawning).
.. note:: Must be lower than max workers count.
:param int workers_startup: The number of workers to be started when starting the application.
After the app is started the algorithm can stop or start workers if needed.
:param int workers_step: Number of additional processes to spawn at a time if they are needed,
"""
self._set('cheap', spawn_on_request, cast=bool)
self._set('cheaper-algo', cheaper_algo)
self._set('cheaper', workers_min)
self._set('cheaper-initial', workers_startup)
self._set('cheaper-step', workers_step)

return self._section

def set_algo_spare_params(self, check_interval_overload=None):
"""Sets ``spare`` cheaper algorithm params.
* http://uwsgi.readthedocs.io/en/latest/Cheaper.html#spare-cheaper-algorithm
:param int check_interval_overload: Interval (sec) to for worker overload.
"""
self._set('cheaper-overload', check_interval_overload)

return self._section

def set_algo_spare2_params(self, check_interval_idle=None):
"""Sets ``spare2`` cheaper algorithm params.
* http://uwsgi.readthedocs.io/en/latest/Cheaper.html#spare2-cheaper-algorithm
:param int check_interval_idle: Decrease workers after specified idle. Default: 10.
"""
self._set('cheaper-idle', check_interval_idle)

return self._section

def set_algo_backlog_params(self, check_num_overload=None):
"""Sets ``backlog`` cheaper algorithm params.
* http://uwsgi.readthedocs.io/en/latest/Cheaper.html#backlog-cheaper-algorithm
:param int check_num_overload: Number of backlog items in queue.
"""
self._set('cheaper-overload', check_num_overload)

return self._section

def set_algo_busyness_params(
self, check_interval_busy=None,
busy_max=None, busy_min=None,
idle_cycles_max=None, idle_cycles_penalty=None,
verbose=None):
"""Sets busyness algorithm related params.
:param int check_interval_busy: Interval (sec) to check worker busyness.
:param int busy_max: Maximum busyness (percents). Every time the calculated busyness
is higher than this value, uWSGI will spawn new workers. Default: 50.
:param int busy_min: Minimum busyness (percents). If busyness is below this value,
the app is considered in an "idle cycle" and uWSGI will start counting them.
Once we reach needed number of idle cycles uWSGI will kill one worker. Default: 25.
:param int idle_cycles_max: This option tells uWSGI how many idle cycles are allowed
before stopping a worker.
:param int idle_cycles_penalty: Number of idle cycles to add to ``idle_cycles_max``
in case worker spawned too early. Default is 1.
:param bool verbose: Enables debug logs for this algo.
"""
set_ = partial(self._set, plugin='cheaper_busyness')

set_('cheaper-overload', check_interval_busy)
set_('cheaper-busyness-max', busy_max)
set_('cheaper-busyness-min', busy_min)
set_('cheaper-busyness-multiplier', idle_cycles_max)
set_('cheaper-busyness-penalty', idle_cycles_penalty)
set_('cheaper-busyness-verbose', verbose, cast=bool)

return self._section

def set_algo_busyness_emergency_params(
self, workers_step=None, idle_cycles_max=None, backlog_size=None, backlog_nonzero_delay=None):
"""Sets busyness algorithm emergency workers related params.
Emergency workers could be spawned depending upon uWSGI backlog state.
.. note:: These options are Linux only.
:param int workers_step: Number of emergency workers to spawn. Default: 1.
:param int idle_cycles_max: Idle cycles to reach before stopping an emergency worker. Default: 3.
:param int backlog_size: Backlog max queue size to spawn an emergency worker. Default: 33.
:param int backlog_nonzero_delay: If the request listen queue is > 0 for more than given amount of seconds
new emergency workers will be spawned. Default: 60.
"""
set_ = partial(self._set, plugin='cheaper_busyness')

set_('cheaper-busyness-backlog-step', workers_step)
set_('cheaper-busyness-backlog-multiplier', idle_cycles_max)
set_('cheaper-busyness-backlog-alert', backlog_size)
set_('cheaper-busyness-backlog-nonzero', backlog_nonzero_delay)

return self._section

def set_memory_limits(self, rss_soft=None, rss_hard=None):
"""Sets worker memory limits for cheapening.
:param int rss_soft: Don't spawn new workers if total resident memory usage
of all workers is higher than this limit in bytes.
:param int rss_hard: Try to stop workers if total workers resident memory usage
is higher that thi limit in bytes.
"""
self._set('cheaper-rss-limit-soft', rss_soft)
self._set('cheaper-rss-limit-hard', rss_hard)

return self._section

def print_alorithms(self):
"""Print out enabled cheaper algorithms."""

self._set('cheaper-algo-list', True, cast=bool)

return self._section

0 comments on commit b34bb77

Please sign in to comment.