
Introduces a new threading backend for oslo.service that provides a complete alternative to the eventlet-based backend. This backend includes: • A ThreadGroup and Thread implementation using the Python threading module. • A reimplementation of all looping call utilities ( FixedIntervalLoopingCall, DynamicLoopingCall, etc.) using threading.Event and futurist.ThreadPoolExecutor. • A service management layer based on cotyledon, including Service,ServiceWrapper, Launcher, and ProcessLauncher. • A new ThreadingBackend class that exposes all components needed for integration into oslo.service. This change provides a robust and eventlet-free alternative backend while maintaining API compatibility. Change-Id: Ia7518401ccdcc28afbffd4ffe5c3b772f3617001
65 lines
2.8 KiB
YAML
65 lines
2.8 KiB
YAML
features:
|
|
- |
|
|
Added a new `threading` backend to `oslo.service`, using standard Python
|
|
threads instead of `eventlet`. This includes a full implementation of:
|
|
|
|
- `Service`, `Launcher`, `ServiceLauncher`, and `ProcessLauncher` using
|
|
`cotyledon`
|
|
- `LoopingCall` variants (`FixedIntervalLoopingCall`,
|
|
`DynamicLoopingCall`, etc.) using `futurist.ThreadPoolExecutor`
|
|
- A new `ThreadGroup` and `Thread` abstraction to mimic `eventlet`-like
|
|
behavior
|
|
- A native `SignalHandler` implementation using standard Python signals
|
|
|
|
This backend provides a standard-thread-compatible alternative that avoids
|
|
monkey-patching, making it suitable for environments where `eventlet` is
|
|
problematic or undesirable.
|
|
|
|
Additionally:
|
|
|
|
- `ProcessLauncher` now supports a `no_fork=True` mode, allowing services
|
|
to run in the main process without forking. This is useful when `fork()`
|
|
is unsafe — for example, in threaded environments or with Python 3.12+
|
|
where the default multiprocessing start method has changed to `spawn`.
|
|
|
|
- A new `register_backend_default_hook()` API has been added. It allows
|
|
users to define a fallback backend type in case `init_backend()` was not
|
|
called early enough. This is helpful in environments where import order
|
|
or initialization timing cannot be guaranteed.
|
|
|
|
Example:
|
|
|
|
```python
|
|
from oslo_service import backend
|
|
backend.register_backend_default_hook(lambda: backend.BackendType.THREADING)
|
|
```
|
|
|
|
This hook will only be used if `init_backend()` has not already been called.
|
|
|
|
upgrade:
|
|
- |
|
|
While Python 3.14 defaults to ``spawn`` as the multiprocessing start
|
|
method, `oslo.service` continues to rely on ``fork`` as the only supported
|
|
method for creating worker processes. Many parts of OpenStack depend on
|
|
objects that cannot be safely pickled (e.g. argparse parsers, thread locks,
|
|
lambdas in config defaults), which makes ``spawn`` currently impractical.
|
|
|
|
In the long term, process scaling should be handled externally (e.g. via
|
|
Kubernetes or systemd), rather than by the service itself.
|
|
|
|
issues:
|
|
- |
|
|
When using the `threading` backend with multiple workers
|
|
(i.e. `ProcessLauncher` with `fork()`), **starting threads before the fork
|
|
occurs can lead to corrupted state** due to how `os.fork()` behaves in
|
|
multi-threaded processes. This is a known limitation in Python.
|
|
|
|
See: https://gibizer.github.io/posts/Eventlet-Removal-The-First-Threading-Bug/#threads--osfork--confused-threadpools
|
|
|
|
To avoid this issue, you can:
|
|
|
|
- Ensure that no threads are started before `oslo.service` forks the workers.
|
|
- Use `ProcessLauncher(no_fork=True)` to disable forking entirely.
|
|
- Explicitly manage thread lifecycle — for example, stop all threads before
|
|
forking, as currently done in Nova.
|