oslo.service: Propose a plan to Remove Eventlet
Find alternatives to Eventlet features and propose a plan with milestones to remove Eventlet Change-Id: I9e16ca8d4c229ff9427e52359548357d2bad9ebc
This commit is contained in:
parent
c47804b7fc
commit
c458c22c33
@ -8,7 +8,7 @@
|
|||||||
============================
|
============================
|
||||||
|
|
||||||
Epoxy
|
Epoxy
|
||||||
=========
|
=====
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:glob:
|
:glob:
|
||||||
@ -26,7 +26,7 @@ Dalmatian
|
|||||||
specs/dalmatian/*
|
specs/dalmatian/*
|
||||||
|
|
||||||
Zed
|
Zed
|
||||||
========
|
===
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:glob:
|
:glob:
|
||||||
|
809
specs/epoxy/remove-eventlet-from-oslo-service.rst
Normal file
809
specs/epoxy/remove-eventlet-from-oslo-service.rst
Normal file
@ -0,0 +1,809 @@
|
|||||||
|
..
|
||||||
|
This template should be in ReSTructured text. For help with syntax,
|
||||||
|
see http://sphinx-doc.org/rest.html
|
||||||
|
|
||||||
|
To test out your formatting, build the docs using tox, or see:
|
||||||
|
http://rst.ninjs.org
|
||||||
|
|
||||||
|
The filename in the git repository should match the launchpad URL,
|
||||||
|
for example a URL of
|
||||||
|
https://blueprints.launchpad.net/oslo?searchtext=awesome-thing should be
|
||||||
|
named awesome-thing.rst.
|
||||||
|
|
||||||
|
For specs targeted at a single project, please prefix the first line
|
||||||
|
of your commit message with the name of the project. For example,
|
||||||
|
if you're submitting a new feature for oslo.config, your git commit
|
||||||
|
message should start something like: "config: My new feature".
|
||||||
|
|
||||||
|
Wrap text at 79 columns.
|
||||||
|
|
||||||
|
Do not delete any of the sections in this template. If you have
|
||||||
|
nothing to say for a whole section, just write: None
|
||||||
|
|
||||||
|
If you would like to provide a diagram with your spec, ascii diagrams are
|
||||||
|
required. http://asciiflow.com/ is a very nice tool to assist with making
|
||||||
|
ascii diagrams. The reason for this is that the tool used to review specs is
|
||||||
|
based purely on plain text. Plain text will allow review to proceed without
|
||||||
|
having to look at additional files which can not be viewed in gerrit. It
|
||||||
|
will also allow inline feedback on the diagram itself.
|
||||||
|
|
||||||
|
==================================
|
||||||
|
Remove Eventlet From oslo.service
|
||||||
|
==================================
|
||||||
|
|
||||||
|
https://blueprints.launchpad.net/oslo?searchtext=remove-eventlet-from-oslo-service
|
||||||
|
|
||||||
|
Oslo.service provides a framework for defining long-running services.
|
||||||
|
To achieve that goal, oslo.service rely on Eventlet and its coroutines.
|
||||||
|
|
||||||
|
Eventlet is the heart of oslo.service.
|
||||||
|
|
||||||
|
However, the removal of Eventlet from Openstack is now a `fact
|
||||||
|
<https://review.opendev.org/c/openstack/governance/+/902585>`_.
|
||||||
|
|
||||||
|
Removing Eventlet from oslo.service is now mandatory.
|
||||||
|
|
||||||
|
This specification aims to design the removal of eventlet from oslo.service.
|
||||||
|
|
||||||
|
The goal of this document is:
|
||||||
|
1. to identify alternatives to the Eventlet features currently used by oslo.service;
|
||||||
|
2. to define how to put in place those alternatives;
|
||||||
|
3. to define the various milestone needed to properly remove Eventlet;
|
||||||
|
4. to minimize the impact for oslo.service's users.
|
||||||
|
|
||||||
|
Problem description
|
||||||
|
===================
|
||||||
|
|
||||||
|
The removal of Eventlet is now a `fact
|
||||||
|
<https://review.opendev.org/c/openstack/governance/+/902585>`_.
|
||||||
|
The official Eventlet project will be retired in a near future and the
|
||||||
|
Openstack T.C officially accepted the community goal proposed to retire
|
||||||
|
Eventlet from the Openstack runtime.
|
||||||
|
|
||||||
|
The main features provided by oslo.service are:
|
||||||
|
|
||||||
|
- ``loopingcall``: a module to run a method in a loop;
|
||||||
|
- ``periodic task``: periodic tasks that can be run in a separate process;
|
||||||
|
- ``service``: a service manager that can handle workers;
|
||||||
|
- ``wsgi``: an utility to create and launch wsgi server;
|
||||||
|
- ``systemd``: an helper module for systemd service readiness notification;
|
||||||
|
- ``threadgroup``: an helper to create a group of greenthreads and timers.
|
||||||
|
|
||||||
|
The problem is that oslo.service heavily rely on Eventlet.
|
||||||
|
All the oslo.service features listed below are based on Eventlet:
|
||||||
|
|
||||||
|
- ``loopingcall``;
|
||||||
|
- ``periodic task``;
|
||||||
|
- ``service``;
|
||||||
|
- ``wsgi``;
|
||||||
|
- ``threadgroup``.
|
||||||
|
|
||||||
|
In parallel of that, oslo.service also provide side features like:
|
||||||
|
|
||||||
|
- ``eventlet_backdoor``;
|
||||||
|
- ``fixture``;
|
||||||
|
- ``sslutils``.
|
||||||
|
|
||||||
|
These side feature are strongly linked to behavioral mechanisms of Eventlet
|
||||||
|
and are not strictly speaking features of oslo.service.
|
||||||
|
|
||||||
|
So if we want to remove Eventlet from oslo.service we have to identify
|
||||||
|
what to do with the modules listed below:
|
||||||
|
|
||||||
|
- ``loopingcall``;
|
||||||
|
- ``periodic task``;
|
||||||
|
- ``service``;
|
||||||
|
- ``wsgi``;
|
||||||
|
- ``threadgroup``;
|
||||||
|
- ``eventlet_backdoor``;
|
||||||
|
- ``fixture``;
|
||||||
|
- ``sslutils``.
|
||||||
|
|
||||||
|
We must identify if all these module could be rewritten by using alternatives
|
||||||
|
and if the usage of these alternatives would impact the existing API of
|
||||||
|
oslo.service (additional parameter, default value, etc).
|
||||||
|
|
||||||
|
We must decide:
|
||||||
|
- how to transition from the current version of oslo.service
|
||||||
|
to the future version of oslo.service, a version free from Eventlet.
|
||||||
|
- if the future version of oslo.service will again provide all of
|
||||||
|
these features or if we should remove some of them in the process.
|
||||||
|
|
||||||
|
If we decide to remove existing feature, then we must decide how transition
|
||||||
|
our customers.
|
||||||
|
|
||||||
|
In all the case, at some point will would have to release major version of
|
||||||
|
oslo.service where the backward compatibility will be definitely broken.
|
||||||
|
|
||||||
|
That document aim to answer all these questions.
|
||||||
|
|
||||||
|
Constraints
|
||||||
|
-----------
|
||||||
|
|
||||||
|
For the sake of consistency we have to define a couple of constrains. The
|
||||||
|
goal of these constraints is to keep the migration as transparent as possible
|
||||||
|
for customers.
|
||||||
|
|
||||||
|
* we cannot abruptly remove Eventlet, so, for all the previously described
|
||||||
|
oslo.service features, both implementations will have to cohabit, the
|
||||||
|
Eventlet version, and the new one;
|
||||||
|
|
||||||
|
* at the oslo.service's customer level, the transition must be as smooth as
|
||||||
|
possible. Meaning that customer should not have to rewrite all their imports
|
||||||
|
to continue using the Eventlet based implementation, or the new version.
|
||||||
|
Some oslo.service modules may be abandoned, only those imports would have
|
||||||
|
to be removed if imported at the customers level;
|
||||||
|
|
||||||
|
* customers must decide when to switch from an implementation to an other;
|
||||||
|
|
||||||
|
* the removal of the Eventlet implementation should not impact the customer
|
||||||
|
in a random way. If a feature is explicitly removed from oslo.service, then
|
||||||
|
an alternative must be documented in a migration guide specific to
|
||||||
|
oslo.service. Customers must be informed by deprecation warning of the
|
||||||
|
removal of the features;
|
||||||
|
|
||||||
|
* non-actively maintained deliverables must not be broken by the removal
|
||||||
|
of the Eventlet implementation;
|
||||||
|
|
||||||
|
* non-actively maintained deliverables must not block indefinitely the
|
||||||
|
removal of the Eventlet implementation. If such case is identified, then
|
||||||
|
the Pop Up team dedicated to the migration must be informed of this problem.
|
||||||
|
|
||||||
|
Proposed change
|
||||||
|
===============
|
||||||
|
|
||||||
|
Features triagging
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Again, here are the main modules provided by oslo.service:
|
||||||
|
|
||||||
|
- ``loopingcall``;
|
||||||
|
- ``periodic task``;
|
||||||
|
- ``service``;
|
||||||
|
- ``wsgi``;
|
||||||
|
- ``threadgroup``;
|
||||||
|
- ``systemd``;
|
||||||
|
- ``eventlet_backdoor``;
|
||||||
|
- ``fixture``;
|
||||||
|
- ``sslutils``.
|
||||||
|
|
||||||
|
Each module is more or less a feature.
|
||||||
|
|
||||||
|
As said previously, some modules are specific to Eventlet:
|
||||||
|
|
||||||
|
- ``eventlet_backdoor``: backdoor made to attach an Eventlet based process;
|
||||||
|
- ``fixture``: a fixture for mocking the ``wait()``;
|
||||||
|
- ``sslutils``: specific Eventlet wrapper for ssl.
|
||||||
|
|
||||||
|
For this reason, the new implementation won't re-implement these modules.
|
||||||
|
These modules will be simply removed from the new implementation.
|
||||||
|
|
||||||
|
The ``wsgi`` module is based on a the `Eventlet wsgi server
|
||||||
|
<https://eventlet.readthedocs.io/en/latest/examples.html#wsgi-server>`_ for
|
||||||
|
this reason, we also propose to remove that module from the new
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
We want to encourage consistency across projects. It is crucial to avoid
|
||||||
|
having multiple ways to start services across different projects. A unified
|
||||||
|
approach will simplify maintenance and enhance user experience.
|
||||||
|
We don't want projects looking/running different.
|
||||||
|
For this reason we advocate for the adoption of one or two of the following
|
||||||
|
packages which could be credible alternatives to the Eventlet WSGI module
|
||||||
|
exposed by oslo.service:
|
||||||
|
|
||||||
|
* `uWSGI <https://uwsgi-docs.readthedocs.io/en/latest/>`: synchronous WSGI
|
||||||
|
server well tested and OpenStack context. Threads based;
|
||||||
|
* `uvicorn <https://pypi.org/project/uvicorn/>`: an ASGI web server
|
||||||
|
implementation for Python;
|
||||||
|
* `asgiref <https://pypi.org/project/asgiref/>`: allow to wrap or decorate
|
||||||
|
async or sync functions to call them from the other style (so you can call
|
||||||
|
async functions from a synchronous thread, or vice-versa).
|
||||||
|
|
||||||
|
This way we will have an unified approach for all our deliverables. This
|
||||||
|
approach is compatible with both world (sync, and async).
|
||||||
|
|
||||||
|
Both libraries are well and actively maintained by many developers.
|
||||||
|
|
||||||
|
At the application layer, we advocate for the usage of
|
||||||
|
`FastAPI <https://pypi.org/project/fastapi/>` which is
|
||||||
|
also compatible with both worlds (sync and async), and which is actively
|
||||||
|
maintained by hundred of people. FastAPI is coming a mainstream library
|
||||||
|
heavily used in the AI realm, so we think that it is a credible alternative
|
||||||
|
a long life ahead of him. The considerations about the application layer are
|
||||||
|
a bit out of the current topic though and or are given here with the sake of
|
||||||
|
giving tracks for discussions.
|
||||||
|
|
||||||
|
The following modules will remains and would have to be transitioned:
|
||||||
|
|
||||||
|
- ``loopingcall``;
|
||||||
|
- ``periodic task``;
|
||||||
|
- ``service``;
|
||||||
|
- ``threadgroup``;
|
||||||
|
- ``systemd``;
|
||||||
|
|
||||||
|
The implementation of the ``systemd`` module seems to be a CPython vanilla
|
||||||
|
implementation, so it may remains untouched.
|
||||||
|
|
||||||
|
How to proceed?
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Oslo.service cannot be transitioned in one time. We propose to introduce the
|
||||||
|
notion of backend into oslo.service to allow the usage of both implementation
|
||||||
|
in parallel.
|
||||||
|
|
||||||
|
Backend will allow to implement the new version of oslo.service while keeping
|
||||||
|
the existing version handy.
|
||||||
|
|
||||||
|
The backend will simplify the life of users during the transition.
|
||||||
|
|
||||||
|
We propose the following milestones to juggle between implementations
|
||||||
|
and with the notion of backend:
|
||||||
|
|
||||||
|
- (SLURP) 2025.1: move the current implementation into an ``eventlet`` backend
|
||||||
|
(the default backend in the config);
|
||||||
|
- (SLURP) 2025.1: implement the ``threading`` backend;
|
||||||
|
- (NON-SLURP) 2025.2: deprecate the ``eventlet`` backend and make
|
||||||
|
``threading`` the default;
|
||||||
|
- (NON-SLURP) 2026.2: remove the ``eventlet`` implementation and move the
|
||||||
|
``threading`` implementation at the root level, and remove the backend
|
||||||
|
notion.
|
||||||
|
|
||||||
|
Actually oslo.service is a flatten module. All its sub-modules are
|
||||||
|
at the root level. Meaning that users imports the features they needs from
|
||||||
|
the root level of the oslo.service module, example::
|
||||||
|
|
||||||
|
from oslo_service import wsgi
|
||||||
|
from oslo_service import service
|
||||||
|
from oslo_service import loopingcall
|
||||||
|
...
|
||||||
|
|
||||||
|
If we do not introduce the backend notion, all the Openstack services using
|
||||||
|
oslo.service will have to rewrite all their imports at least twice. The first
|
||||||
|
time when they will be eager to use the new implementation::
|
||||||
|
|
||||||
|
from oslo_service.threading import service
|
||||||
|
|
||||||
|
and the second one when the old implementation will be removed, and, hence,
|
||||||
|
when the new implementation will be moved at the root level::
|
||||||
|
|
||||||
|
from oslo_service import service
|
||||||
|
|
||||||
|
This is not an acceptable scenario because it will lead to many useless back
|
||||||
|
and forth at the import level, without any additional added value for the
|
||||||
|
users.
|
||||||
|
|
||||||
|
Usaging backends will hide the complexity of this swapping into oslo.service.
|
||||||
|
Users won't suffer from changing their imports again and again.
|
||||||
|
|
||||||
|
Actually, oslo.service looks like to::
|
||||||
|
|
||||||
|
oslo_service
|
||||||
|
├── eventlet_backdoor.py
|
||||||
|
├── fixture.py
|
||||||
|
├── _i18n.py
|
||||||
|
├── __init__.py
|
||||||
|
├── locale
|
||||||
|
│ └── .. (ignored)
|
||||||
|
├── loopingcall.py
|
||||||
|
├── _options.py
|
||||||
|
├── periodic_task.py
|
||||||
|
├── service.py
|
||||||
|
├── sslutils.py
|
||||||
|
├── systemd.py
|
||||||
|
├── tests
|
||||||
|
│ └── .. (ignored)
|
||||||
|
├── threadgroup.py
|
||||||
|
├── version.py
|
||||||
|
└── wsgi.py
|
||||||
|
|
||||||
|
Once the backend will be added the structure of oslo.service will looks like
|
||||||
|
to::
|
||||||
|
|
||||||
|
oslo_service
|
||||||
|
├── backends
|
||||||
|
│ ├── eventlet
|
||||||
|
│ │ ├── eventlet_backdoor.py
|
||||||
|
│ │ ├── fixture.py
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── loopingcall.py
|
||||||
|
│ │ ├── periodic_task.py
|
||||||
|
│ │ ├── service.py
|
||||||
|
│ │ ├── sslutils.py
|
||||||
|
│ │ ├── threadgroup.py
|
||||||
|
│ │ └── wsgi.py
|
||||||
|
│ └── threading
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── loopingcall.py
|
||||||
|
│ ├── periodic_task.py
|
||||||
|
│ ├── service.py
|
||||||
|
│ └── threadgroup.py
|
||||||
|
├── eventlet_backdoor.py
|
||||||
|
├── fixture.py
|
||||||
|
├── _i18n.py
|
||||||
|
├── __init__.py
|
||||||
|
├── locale
|
||||||
|
│ └── .. (ignored)
|
||||||
|
├── loopingcall.py
|
||||||
|
├── _options.py
|
||||||
|
├── periodic_task.py
|
||||||
|
├── service.py
|
||||||
|
├── sslutils.py
|
||||||
|
├── systemd.py
|
||||||
|
├── tests
|
||||||
|
│ └── .. (ignored)
|
||||||
|
├── threadgroup.py
|
||||||
|
├── version.py
|
||||||
|
└── wsgi.py
|
||||||
|
|
||||||
|
|
||||||
|
Each root sub-module will simply import the right backend conditionally,
|
||||||
|
example with the service sub-module::
|
||||||
|
|
||||||
|
if _options.backend == "threading":
|
||||||
|
from oslo_service.threading import service
|
||||||
|
else
|
||||||
|
from oslo_service.eventlet import service
|
||||||
|
|
||||||
|
If a sub-module do not exists in the new implementation, then the root level
|
||||||
|
sub-module will use debtcollector `to emit a deprecation warning
|
||||||
|
<https://docs.openstack.org/debtcollector/latest/user/usage.html#deprecating-anything-else>`_
|
||||||
|
and give instruction to users, example with the wsgi sub-module::
|
||||||
|
|
||||||
|
debtcollector.deprecate(
|
||||||
|
"""
|
||||||
|
The WSGI module is no longer supported\n
|
||||||
|
You see this deprecation warning because you are importing\n
|
||||||
|
the oslo.service wsgi module. This module is deprecated and will\n
|
||||||
|
be soon removed. Please consider using uwsgi and consider following
|
||||||
|
the migration path described here:
|
||||||
|
https://docs.openstack.org/oslo.service/latest/migration/wsgi.html
|
||||||
|
",
|
||||||
|
version="1.0"
|
||||||
|
)
|
||||||
|
if _options.backend == "eventlet":
|
||||||
|
from oslo_service.eventlet import service
|
||||||
|
else
|
||||||
|
raise ImportError("WSGI module not found in the threading backend...")
|
||||||
|
|
||||||
|
Concerning the modules conserved in the ``threading`` implementation, they
|
||||||
|
will be rewritten by using new underlying libraries, like cotyledon, futurist,
|
||||||
|
and threading/concurrent from the stdlib. See the dependency and API section
|
||||||
|
for more details about the usage of these new libraries.
|
||||||
|
|
||||||
|
If a sub-module is not impacted by the Eventlet removal and so not
|
||||||
|
re-implemented, then it could remains at the root level. That's by example
|
||||||
|
the case of the ``systemd`` sub-module, which in our previous tree example
|
||||||
|
remains at the root level.
|
||||||
|
|
||||||
|
If a customers do not move its oslo.service backend to the ``threading``
|
||||||
|
backend in the allotted time, then the T.C should be warned. Then the T.C will
|
||||||
|
surely inform the Pop Up team created to manage the whole Eventlet removal.
|
||||||
|
In this case, the Pop Up team may decide to migrate this deliverable or could
|
||||||
|
propose to retire it if nobody actively maintain it.
|
||||||
|
|
||||||
|
Alternatives
|
||||||
|
------------
|
||||||
|
|
||||||
|
It would be also possible to entirely deprecate oslo.service and to point
|
||||||
|
the available alternatives into the deprecation warnings, therefore,
|
||||||
|
leaving the charge of the refactor to the customers.
|
||||||
|
|
||||||
|
The problem of this approach is that it would surely spring various approaches
|
||||||
|
and so a diversity of solution.
|
||||||
|
|
||||||
|
The motivation behind the creation of the Oslo projects was to uniformize the
|
||||||
|
solutions and to reduce the technical debt.
|
||||||
|
|
||||||
|
If we delegate the refactor to oslo.service customers it will lead to an
|
||||||
|
increase of this technical debt.
|
||||||
|
|
||||||
|
Impact on Existing APIs
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The temporary backends
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The existing API will be modified to introduce the temporary backends.
|
||||||
|
Backends will remains private module not directly importable
|
||||||
|
by customers. One or the other backend will be imported by the classic
|
||||||
|
import and by choosing one backend or the other in the config.
|
||||||
|
|
||||||
|
The public API will remains almost the same until the Eventlet backend is not
|
||||||
|
removed.
|
||||||
|
|
||||||
|
Once the Eventlet backend will be removed, then the public API related
|
||||||
|
to Eventlet will be also removed, see the next section.
|
||||||
|
|
||||||
|
Removed sub-modules
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Once the migration will be terminated, the backend notion will be removed
|
||||||
|
and the new implementation will be moved at the root level, meaning
|
||||||
|
that once the migration will be done, oslo.service will looks like to::
|
||||||
|
|
||||||
|
oslo_service
|
||||||
|
├── _i18n.py
|
||||||
|
├── __init__.py
|
||||||
|
├── locale
|
||||||
|
│ └── .. (voluntary ignored)
|
||||||
|
├── loopingcall.py
|
||||||
|
├── _options.py
|
||||||
|
├── periodic_task.py
|
||||||
|
├── service.py
|
||||||
|
├── systemd.py
|
||||||
|
├── tests
|
||||||
|
│ └── .. (voluntary ignored)
|
||||||
|
├── threadgroup.py
|
||||||
|
└── version.py
|
||||||
|
|
||||||
|
The following modules won't exists anymore, and so won't be anymore
|
||||||
|
importable:
|
||||||
|
|
||||||
|
- wsgi
|
||||||
|
- eventlet_backdoor
|
||||||
|
- fixture
|
||||||
|
- sslutils
|
||||||
|
|
||||||
|
During the time the ``backends`` notion will be present, users will face
|
||||||
|
import errors until the ``wsgi``, ``eventlet_backdoor``, ``fixture``,
|
||||||
|
``sslutils`` modules are removed from the user codebase. Indeed, it is
|
||||||
|
useless to implement a ``NotImplemented`` interface as in all the case
|
||||||
|
users will have to remove them.
|
||||||
|
|
||||||
|
And the transient ``backends`` sub-module and its content, will be also
|
||||||
|
removed.
|
||||||
|
|
||||||
|
Periodic task
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``periodic_task`` sub-module will become a proxy for
|
||||||
|
the futurist library.
|
||||||
|
|
||||||
|
The oslo.service ``periodic_task`` sub-module provide the following
|
||||||
|
abstractions to manage periodical tasks:
|
||||||
|
|
||||||
|
- `oslo_service.periodic_task.periodic_task
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/periodic_task.html#oslo_service.periodic_task.periodic_task>`_
|
||||||
|
which represent a periodical task;
|
||||||
|
- `oslo_service.periodic_task.PeriodicTasks
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/periodic_task.html#oslo_service.periodic_task.PeriodicTasks>`_
|
||||||
|
which is a manager for periodical tasks (one to many). Could be seen as a
|
||||||
|
worker where we attach callable to run periodically.
|
||||||
|
|
||||||
|
Futurist define the following methods and class to manage periodic tasks:
|
||||||
|
|
||||||
|
- `futurist.periodics.PeriodicWorker
|
||||||
|
<https://docs.openstack.org/futurist/latest/reference/index.html#futurist.periodics.PeriodicWorker>`_
|
||||||
|
which allow to call a collection of callable periodically. This is a worker
|
||||||
|
where we attach callable to run periodically;
|
||||||
|
- `futurist.periodics.periodic
|
||||||
|
<https://docs.openstack.org/futurist/latest/reference/index.html#futurist.periodics.periodic>`_
|
||||||
|
which allow to tag a method/function as wanting/able to execute periodically;
|
||||||
|
- `futurist.periodics.Watcher
|
||||||
|
<https://docs.openstack.org/futurist/latest/reference/index.html#futurist.periodics.Watcher>`_
|
||||||
|
which is a read-only object representing a periodic callback’s activities.
|
||||||
|
|
||||||
|
So the following bindings are proposed to use oslo.service as a proxy of
|
||||||
|
futurist:
|
||||||
|
|
||||||
|
- ``oslo_service.periodic_task.periodic_task`` will be bound with
|
||||||
|
``futurist.periodics.periodic``;
|
||||||
|
- ``oslo_service.periodic_task.PeriodicTasks`` will be bound with
|
||||||
|
``futurist.periodics.PeriodicWorker``;
|
||||||
|
|
||||||
|
As our goal is to keep the existing oslo.service API as intact as possible,
|
||||||
|
we propose to ignore the ``futurist.periodics.Watcher`` class.
|
||||||
|
|
||||||
|
The ``futurist.periodics.periodic`` implement the ``enabled`` notion. That
|
||||||
|
option do not exists in oslo.service. Indeed in oslo.service a periodic task
|
||||||
|
is disabled if the ``spacing`` parameter is set to a negative number. In this
|
||||||
|
case, if this number is negative on oslo.service, then, we will have to set
|
||||||
|
the ``enabled`` parameter of futurist to ``False``.
|
||||||
|
|
||||||
|
Futurist periodic tasks `accept an executor parameter
|
||||||
|
<https://docs.openstack.org/futurist/latest/reference/index.html#periodics>`_.
|
||||||
|
Futurist `define different kind of executors
|
||||||
|
<https://docs.openstack.org/futurist/latest/user/features.html#async>`_.
|
||||||
|
|
||||||
|
We should provide a way to choose the kind of executor that futurist will
|
||||||
|
use, for this reason, we will have to implement an abstraction to this notion
|
||||||
|
to offer to users a way to select one.
|
||||||
|
|
||||||
|
Futurist provide an executor based on Eventlet. As our goal is to
|
||||||
|
remove Eventlet we won't provide access to this executor at the oslo.service
|
||||||
|
level.
|
||||||
|
|
||||||
|
Oslo.service will only allow to use/select the following futurist executors:
|
||||||
|
|
||||||
|
- futurist.ProcessPoolExecutor
|
||||||
|
- futurist.SynchronousExecutor
|
||||||
|
- futurist.ThreadPoolExecutor
|
||||||
|
|
||||||
|
Service
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
To implement the new version of the oslo.service' service sub-module we
|
||||||
|
propose to use Cotyledon.
|
||||||
|
|
||||||
|
The Cotyledon module provide the following public API:
|
||||||
|
|
||||||
|
- `cotyledon.Service
|
||||||
|
<https://cotyledon.readthedocs.io/en/latest/api.html#cotyledon.Service>`_:
|
||||||
|
base class for a service;
|
||||||
|
- `cotyledon.ServiceManager
|
||||||
|
<https://cotyledon.readthedocs.io/en/latest/api.html#cotyledon.ServiceManager>`_:
|
||||||
|
manage lifetimes of services.
|
||||||
|
|
||||||
|
Where service sub-module of oslo.service provide the following public API:
|
||||||
|
|
||||||
|
- `oslo_service.service.Launcher
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/service.html#oslo_service.service.Launcher>`_:
|
||||||
|
launch one or more services and wait for them to complete;
|
||||||
|
- `oslo_service.service.ProcessLauncher
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/service.html#oslo_service.service.ProcessLauncher>`_:
|
||||||
|
launch a service with a given number of workers;
|
||||||
|
- `oslo_service.service.Service
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/service.html#oslo_service.service.Service>`_:
|
||||||
|
service object for binaries running on hosts;
|
||||||
|
- `oslo_service.service.ServiceBase
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/service.html#oslo_service.service.ServiceBase>`_:
|
||||||
|
base class for all services;
|
||||||
|
- `oslo_service.service.ServiceLauncher
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/service.html#oslo_service.service.ServiceLauncher>`_:
|
||||||
|
runs one or more service in a parent process;
|
||||||
|
- `oslo_service.service.launch
|
||||||
|
<https://docs.openstack.org/oslo.service/latest/reference/service.html#oslo_service.service.launch>`_:
|
||||||
|
launch a service with a given number of workers.
|
||||||
|
|
||||||
|
We propose the following bindings:
|
||||||
|
|
||||||
|
- ``oslo_service.service.Launcher`` will delegate to ``cotyledon.ServiceManager``;
|
||||||
|
- ``oslo_service.service.ServiceLauncher`` will delegate to ``cotyledon.ServiceManager``;
|
||||||
|
- ``oslo_service.service.Service`` will delegate to ``cotyledon.Service``;
|
||||||
|
|
||||||
|
And the ``oslo_service.service.launch`` and
|
||||||
|
``oslo_service.service.ProcessLauncher`` will remains more or less with
|
||||||
|
the same logic that is currently implemented, less the monkey patching of
|
||||||
|
Eventlet.
|
||||||
|
|
||||||
|
Unlike oslo.service cotyledon allow only one service workers manager run at a
|
||||||
|
time. Oslo.service allow to run more than one Service launcher at a time.
|
||||||
|
This difference should be documented.
|
||||||
|
|
||||||
|
Loopingcall & threadgroup
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``loopingcall`` and ``threadgroup`` modules are based on greenthreads,
|
||||||
|
so we have to re-implement them. We propose to use the CPython ``threading``
|
||||||
|
module to refactor them.
|
||||||
|
|
||||||
|
``loopingcall`` seems to simply needs threads to run methods in loop.
|
||||||
|
|
||||||
|
``threadgroup`` use eventlet green pool to manage group of threads. Again
|
||||||
|
the stdlib ``threading`` module offer ways to attach threads to a defined
|
||||||
|
group. Possibly it would also require the usage of `ThreadPoolExecutor
|
||||||
|
<https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor>`_
|
||||||
|
to allow asynchronous behavior.
|
||||||
|
|
||||||
|
These 2 sub-modules should not be really impacted by API changes. Only the
|
||||||
|
internal mechanisms will change and the public API will surely remains the
|
||||||
|
same.
|
||||||
|
|
||||||
|
Security impact
|
||||||
|
---------------
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
Performance Impact
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Removing Eventlet would mean moving, in some circumstances, to a native
|
||||||
|
threading model. Eventlet is based on cooperative coroutines provided by
|
||||||
|
greenlet, when cotyledon, or even futurist, uses threads, who are preemptive.
|
||||||
|
|
||||||
|
Threads tend to be more expensive and slower than coroutines because they
|
||||||
|
involve context switching. OS will continue to share CPU operations with
|
||||||
|
all the threads even if they are not ready to works (network IO, etc).
|
||||||
|
|
||||||
|
Indeed, depending on the number of workers allocated to services or periodic
|
||||||
|
tasks, in a context with a lot of threading concurrency, threads can degrade
|
||||||
|
the flow rate of the machine. This is linked to context-switching which is
|
||||||
|
resource-intensive.
|
||||||
|
|
||||||
|
Threads are preemptive so compared to cooperative coroutines, they are more
|
||||||
|
prone to lead to race condition.
|
||||||
|
|
||||||
|
Configuration Impact
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
This topic will impact the configuration in numerous ways.
|
||||||
|
The first impact will be related to the addition of a new config option
|
||||||
|
to allow switching the implementation. Switching the backend to use.
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
oslo_service_backend = eventlet
|
||||||
|
|
||||||
|
This new option will named ``oslo_service_backend`` and it will be a
|
||||||
|
``cfg.StrOpt``.
|
||||||
|
|
||||||
|
It will propose the following choices as valid settings::
|
||||||
|
|
||||||
|
choices=['eventlet', 'threading']
|
||||||
|
|
||||||
|
And it will defaulted to ``eventlet``, and users
|
||||||
|
will move this value to ``threading`` when they will have cleaned-up the usage
|
||||||
|
of oslo.service deprecated sub-modules from their code base.
|
||||||
|
|
||||||
|
This option will be removed once the deprecation period will be over.
|
||||||
|
|
||||||
|
As said previously, using the backend notion, and so this option will allow
|
||||||
|
internal transients states within oslo.service, allowing us to swap the
|
||||||
|
internal implementations.
|
||||||
|
|
||||||
|
The existing configuration related to the wsgi module and to the sslutils
|
||||||
|
of oslo.service will be removed once the swapping will be done, as these
|
||||||
|
sub-modules will be retired.
|
||||||
|
|
||||||
|
In a first time these config sections (wsgi, sslutils) will be fully deprecated
|
||||||
|
to warn the user that they have to stop using it.
|
||||||
|
|
||||||
|
We should also deprecate the ``backdoor_socket`` and the ``backdoor_port``
|
||||||
|
from the default config section, as the eventlet_backdoor module will be
|
||||||
|
removed, as so, these options will be also removed once the swapping will
|
||||||
|
be done.
|
||||||
|
|
||||||
|
Developer Impact
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Removing Eventlet from oslo.service would allow side works, like:
|
||||||
|
- removing the mutex tricks oslo.log;
|
||||||
|
- removing the greenlet/eventlet executor from futurist.
|
||||||
|
|
||||||
|
Testing Impact
|
||||||
|
--------------
|
||||||
|
|
||||||
|
As the current tests also relies on Eventlet and on monkey patching, all
|
||||||
|
the new implementation should also introduce its own tests.
|
||||||
|
|
||||||
|
The existing tests should remains, and the ``tests`` directory structure
|
||||||
|
should reflect the new module tree with both backends.
|
||||||
|
|
||||||
|
The tests of the removal and the of the deprecation will be at the charge
|
||||||
|
of the ``eventlet`` backend. We do not want to pollute the new ``threading``
|
||||||
|
implementation with obsolete artifacts.
|
||||||
|
|
||||||
|
Oslo.service do not implement functional tests, so this refactor won't add
|
||||||
|
ones.
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
==============
|
||||||
|
|
||||||
|
Assignee(s)
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Primary assignee:
|
||||||
|
Hervé Beraud (hberaud)
|
||||||
|
|
||||||
|
Milestones
|
||||||
|
----------
|
||||||
|
|
||||||
|
Target Milestone for completion:
|
||||||
|
|
||||||
|
- (SLURP) 2025.1: move the current implementation into an ``eventlet`` backend
|
||||||
|
(the default backend in the config);
|
||||||
|
- (SLURP) 2025.1: implement the ``threading`` backend;
|
||||||
|
- (NON-SLURP) 2025.2: deprecate the eventlet backend and make ``threading`` the
|
||||||
|
default;
|
||||||
|
- (NON-SLURP) 2026.2: remove the ``eventlet`` implementation and move the
|
||||||
|
``threading`` implementation at the root level, and remove the backend
|
||||||
|
notion.
|
||||||
|
|
||||||
|
Work Items
|
||||||
|
----------
|
||||||
|
|
||||||
|
#. create an ``eventlet`` sub-module to host the existing implementation
|
||||||
|
and plug the root level sub-modules to this new module
|
||||||
|
#. create a ``threading`` sub-module to host the new implementation
|
||||||
|
and add a new backend config defaulting to the ``eventlet`` sub-module
|
||||||
|
#. deprecate the ``eventlet`` sub-module
|
||||||
|
#. default the backend config to the ``threading`` implementation
|
||||||
|
#. remove the ``eventlet`` sub-module and remove the backend config option
|
||||||
|
#. move the ``threading`` implementation at the root level
|
||||||
|
|
||||||
|
Documentation Impact
|
||||||
|
====================
|
||||||
|
|
||||||
|
As the notion of backend will be added, to different documentation
|
||||||
|
will cohabit for both implementations of the same sub-module.
|
||||||
|
|
||||||
|
The documentation structure will reflect the oslo.service module::
|
||||||
|
|
||||||
|
doc/source/backends/
|
||||||
|
|
||||||
|
The new option allowing to swap the implementation will be documented.
|
||||||
|
|
||||||
|
Each new implementation will have to specify its specificity at the
|
||||||
|
docstring level.
|
||||||
|
|
||||||
|
The documentation should also provide a migration guide to give guidance
|
||||||
|
about the removed sub-module.
|
||||||
|
|
||||||
|
Each time a specific deprecation warning is emitted from a sub-module,
|
||||||
|
the deprecation message should give a link that refer to the right
|
||||||
|
section of this migration guide.
|
||||||
|
|
||||||
|
This migration guide will be hosted into the following path::
|
||||||
|
|
||||||
|
doc/source/migration
|
||||||
|
|
||||||
|
This specific migration guide should at least document the
|
||||||
|
removals of ``oslo_service.wsgi`` and give tracks to follow (WSGI/ASGI (uwsgi,
|
||||||
|
uvicorn, etc), application layer (flask, etc), HTTP...). This part of the
|
||||||
|
documentation will follow the standards defined by `the HTTP SGI working group
|
||||||
|
<https://wiki.openstack.org/wiki/Eventlet-removal#The_HTTP_SGI_Working_Group>`_
|
||||||
|
|
||||||
|
The other removed sub-modules which are specific to Eventlet (
|
||||||
|
eventlet_backdoor, fixture, etc...) won't have to be documented.
|
||||||
|
|
||||||
|
Once the Eventlet backend will be removed, this migration will be also
|
||||||
|
removed from the documentation.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
============
|
||||||
|
|
||||||
|
We propose to create two `extra environment markers
|
||||||
|
<https://docs.openstack.org/pbr/latest/user/using.html#environment-markers>`_.
|
||||||
|
One related the ``eventlet`` backend and one for the ``threading`` backend.
|
||||||
|
They would avoid installing useless packages and help to reduce the size
|
||||||
|
of packaging and disk size. By example, if the user decide to use the
|
||||||
|
``threading`` package, we do not need building a container that doesn't
|
||||||
|
require ``eventlet`` to be in it. Indeed, in many place the existence of
|
||||||
|
eventlet triggers a behavior change, avoid installing eventlet will reduce
|
||||||
|
the chance of facing this kind of situation.
|
||||||
|
|
||||||
|
The extra environment markers will looks like too:
|
||||||
|
|
||||||
|
.. code-block:: cfg
|
||||||
|
|
||||||
|
[extras]
|
||||||
|
eventlet =
|
||||||
|
eventlet>=0.36.1 # MIT
|
||||||
|
threading =
|
||||||
|
futurist>=3.0.0 # Apache-2.0
|
||||||
|
cotyledon>=1.7.3 # Apache-2.0
|
||||||
|
|
||||||
|
See the section below for further details and context about ``futurist`` and
|
||||||
|
``cotyledon``.
|
||||||
|
|
||||||
|
Periodic task
|
||||||
|
-------------
|
||||||
|
|
||||||
|
For the ``periodic task`` module, we propose to use `Futurist
|
||||||
|
<https://docs.openstack.org/futurist/latest/>`_ to replace Eventlet.
|
||||||
|
Indeed, Futurist is a library that provide periodic tasks management.
|
||||||
|
|
||||||
|
The ``oslo_service.periodic_task`` module will began a proxy of futurist.
|
||||||
|
|
||||||
|
Service
|
||||||
|
-------
|
||||||
|
|
||||||
|
`Cotyledon <https://github.com/sileht/cotyledon>`_ was created years ago
|
||||||
|
to offer an alternative to the Service module of oslo.service. An alternative
|
||||||
|
free from Eventlet. We propose to use cotyledon as underlying library for
|
||||||
|
the new implementation of the Service module of oslo.service.
|
||||||
|
|
||||||
|
In other words, the ``oslo_service.service`` module will began a proxy of
|
||||||
|
cotyledon.
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
* https://review.opendev.org/c/openstack/governance/+/902585
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This work is licensed under a Creative Commons Attribution 3.0
|
||||||
|
Unported License.
|
||||||
|
http://creativecommons.org/licenses/by/3.0/legalcode
|
Loading…
x
Reference in New Issue
Block a user