File: backend.rst

package info (click to toggle)
python-oslo.service 4.3.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 820 kB
  • sloc: python: 4,646; makefile: 20; sh: 2
file content (239 lines) | stat: -rw-r--r-- 7,843 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
==============
Backend System
==============

Introduction
============

The backend system in ``oslo.service`` provides a modular architecture
that allows you to choose between different concurrency models
without modifying your service logic.

Currently, two backends are implemented and supported:

- **Eventlet backend**: The traditional concurrency backend based on green
  threads and cooperative multitasking (`greenlet` library).
- **Threading backend**: A new backend using native Python threads, Cotyledon,
  and Futurist, designed to be compatible with modern Python versions,
  simplify debugging, and improve maintainability.

Why a Backend System?
=====================

This system was introduced to enable a
**gradual transition away from the Eventlet backend**, which has known
limitations for scaling, cooperative multitasking complexity, and debugging in
some Python environments. By using the backend system, you can test, adopt, or
migrate to the Threading backend while keeping the same public API and service
structure.

In the future, the Eventlet backend may be deprecated once the Threading
backend covers all use cases. Some related features tied to Eventlet,
like the embedded WSGI server and the backdoor shell, are also planned for
removal.

For more context, see:

- `Epoxy spec for Eventlet removal <https://specs.openstack.org/openstack/oslo-specs/specs/epoxy/remove-eventlet-from-oslo-service.html>`_
- `Eventlet removal guide <https://removal.eventlet.org/>`_
- `OpenStack Eventlet removal goal <https://governance.openstack.org/tc/goals/selected/remove-eventlet.html>`_

How It Works
============

The backend system exposes the same **public components**
(``ServiceLauncher``, ``ProcessLauncher``, ``ThreadGroup``,
``LoopingCall``, etc.) through a dynamic selection mechanism.

At runtime, you select the backend you want to use by calling the
``init_backend()`` function and providing the desired ``BackendType``:

.. code-block:: python

   from oslo_service.backend import init_backend, BackendType

   # Use the Threading backend
   init_backend(BackendType.THREADING)

If you do not explicitly initialize a backend, the system will
use the **Eventlet** backend by default.

Once the backend is initialized, it **cannot be changed**.
Trying to re-initialize it will raise a ``BackendAlreadySelected`` exception.

If you see this exception, ensure that your backend is initialized only once
early during your application startup (for example in your main() entry point).
Avoid calling ``init_backend()`` in shared libraries or multiple times.
If you need to reset the backend in unit tests, use the ``_reset_backend()``
helper to clear the backend cache:

.. code-block:: python

   from oslo_service.backend import _reset_backend

   _reset_backend()

How to Select a Backend
========================

There are two supported ways to select which backend to use:

1. **Explicit initialization**

   Call ``init_backend()`` early in your application entry point:

   .. code-block:: python

      from oslo_service.backend import init_backend, BackendType

      init_backend(BackendType.THREADING)

   This will load the Threading backend and cache its components.

   For practical usage, see how Nova does it:
   https://review.opendev.org/c/openstack/nova/+/948311

2. **Dynamic hook**

   You may register a Python hook to dynamically decide the backend
   if ``init_backend()`` is not called:

   .. code-block:: python

      from oslo_service.backend \
          import register_backend_default_hook, BackendType

      def my_backend_decider():
          # Add custom logic here if needed
          return BackendType.THREADING

      register_backend_default_hook(my_backend_decider)

   If no explicit backend is set, the system will call your hook
   to decide which backend to load.

.. note::
   There is **no automatic oslo.config option** for ``service_backend``.
   If you want to make the backend configurable through your application's
   config file, you need to define and parse that option yourself, then
   call ``init_backend()`` with the selected value.

Accessing Backend Components
============================

Once the backend is initialized, you can access its components
via the ``get_component()`` helper function.

Example: getting a ``ProcessLauncher`` instance:

.. code-block:: python

   from oslo_service.backend import get_component

   ProcessLauncher = get_component("ProcessLauncher")

   launcher = ProcessLauncher(conf)
   launcher.launch_service(my_service)
   launcher.wait()

Available components include:

- ``ServiceLauncher``
- ``ProcessLauncher``
- ``ThreadGroup``
- ``LoopingCall`` variants
- ``SignalHandler``
- And other related service utilities

If you try to access a component that does not exist
in the current backend, a ``KeyError`` will be raised.

Example Usage
=============

Here is a minimal example that shows how to initialize the Threading backend
and launch a service:

.. code-block:: python

   from oslo_service.backend import (
       init_backend,
       get_component,
       BackendType
   )

   def main():
       # Select the Threading backend
       init_backend(BackendType.THREADING)

       # Get the ProcessLauncher component
       ProcessLauncher = get_component("ProcessLauncher")

       # Initialize your service and launch it
       launcher = ProcessLauncher(conf)
       launcher.launch_service(my_service)
       launcher.wait()

   if __name__ == "__main__":
       main()

Understanding the Implications of Migrating from Eventlet to Threading
======================================================================

The following table is not about choosing a backend, but about understanding
the implications of migrating away from Eventlet to the Threading backend:

.. list-table::
   :widths: 20 40 40
   :header-rows: 1

   * - Compared Aspect
     - Eventlet Backend
     - Threading Backend
   * - Concurrency model
     - Cooperative multitasking using green threads (`greenlet`)
     - Native Python threads (preemptive)
   * - Process management
     - Uses built-in eventlet processes
     - Uses Cotyledon for multi-process support
   * - Looping calls
     - Uses eventlet green thread pools
     - Uses Futurist thread pools
   * - Embedded WSGI server
     - Provided (with eventlet)
     - Planned for removal; use an external WSGI server instead
   * - Backdoor shell
     - Provided (eventlet only)
     - Planned for removal
   * - API compatibility
     - Same public API
     - Same public API
   * - Debugging
     - More complex due to cooperative multitasking and green threads.
       See `eventlet_backdoor.py <https://opendev.org/openstack/oslo.service/src/branch/master/oslo_service/eventlet_backdoor.py>`_
     - May produce clearer native thread stack traces and benefits
       from recent CPython improvements (e.g. PEP 768).
   * - Python compatibility
     - Known issues with CPython native threads and RLocks.
       May break with new Python versions.
     - Fully compatible with modern CPython threading.

Test Setup in Devstack and CI Gates
====================================

If you want to test the Threading backend in Devstack or your CI gates,
refer to:

- https://review.opendev.org/c/openstack/devstack/+/948436
- https://gibizer.github.io/posts/Eventlet-Removal-Scheduler-First-Run/
- https://review.opendev.org/c/openstack/devstack/+/948408

References
==========

- `Eventlet removal guide <https://removal.eventlet.org/>`_
- `Epoxy spec for oslo.service <https://specs.openstack.org/openstack/oslo-specs/specs/epoxy/remove-eventlet-from-oslo-service.html>`_
- `Eventlet removal goal <https://governance.openstack.org/tc/goals/selected/remove-eventlet.html>`_

For any questions, please refer to the OpenStack mailing list
or the oslo.service maintainers.