Skip to content

Commit

Permalink
Describes Eventlet's Alternatives Into the Migration Guide (eventlet#971
Browse files Browse the repository at this point in the history
)

* shorten the lines
* Document alternatives and methodology
* adding a glossary
* why abandonning eventlet

This is the beginning of the implementation of the migration
guide proposed here:

https://review.opendev.org/c/openstack/governance/+/902585

The goal is to centralize all the materials we have to help
users to safely migrate off of Eventlet.
  • Loading branch information
4383 authored Jul 19, 2024
1 parent 230d2b9 commit 8bac9b2
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 26 deletions.
41 changes: 41 additions & 0 deletions doc/source/asyncio/asyncio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,48 @@ You may also want to take a look to the
Migrating from Eventlet to Asyncio
==================================

Why Migrating?
--------------

Eventlet is a broken and outdated technology.

Eventlet was created almost 20 years ago (See the :ref:`history` of Eventlet),
at a time where Python did not provided non-blocking features.

Time passed and Python now provide AsyncIO.

In parallel of the evolution of Python, the maintenance of Eventlet was
discontinued during several versions of Python, increasing the gap between
the monkey patching of Eventlet and the recent implementation of Python.

This gap is now not recoverable. For this reason, we decided to officially
abandon the maintenance of Eventlet in an incremental way.

In a last effort, we want to lead Eventlet to a well deserved rest.
Our goal is to provide you a guide to migrate off of Eventlet and then
to properly retire Eventlet.

For more details about the reasons who motivated this effort we invite the
readers to show the discussions related to this scheduled abandon:

https://review.opendev.org/c/openstack/governance/+/902585

Getting Started
---------------

Want to use Asyncio and Eventlet together or you simply want to migrate
off of Eventlet?

Follow the :ref:`official migration guide <migration-guide>`.

We encourage readers to first look at the :ref:`glossary_guide` to
learn about the various terms that may be encountered during the migration.

Alternatives & Tips
-------------------

You want to refactor your code to replace Eventlet usages? See the proposed
alternatives and tips:

- :ref:`awaitlet_alternative`
- :ref:`manage-your-deprecations`
47 changes: 47 additions & 0 deletions doc/source/asyncio/guide/awaitlet.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.. _awaitlet_alternative:

Awaitlet as an Alternative
==========================

Applications with several years of existence may have seen their code base
growing again and again, thus, migrating this kind of existing code
base toward AsyncIO, would be painful or even unrealistic. For most of these
applications, migrating to AsyncIO would may mean a complete rewriting of
these applications.

`Awaitlet <https://awaitlet.sqlalchemy.org/en/latest/>`_ is an alternative
which allow you to migrate this kind of existing code base without getting
the headaches associated to migrating such deliverables.

Awaitlet allows existing programs written to use threads and blocking APIs to
be ported to asyncio, by replacing frontend and backend code with asyncio
compatible approaches, but allowing intermediary code to remain completely
unchanged, with no addition of ``async`` or ``await`` keywords throughout the
entire codebase needed. Its primary use is to support code that is
cross-compatible with asyncio and non-asyncio runtime environments.

Awaitlet is a direct extract of `SQLAlchemy <https://www.sqlalchemy.org/>`_’s
own asyncio mediation layer, with no dependencies on SQLAlchemy. This code has
been in widespread production use in thousands of environments for several
years.

.. warning::

Using Awaitlet require to use the :mod:`Asyncio Hub
<eventlet.hubs.asyncio>`

:ref:`understanding_hubs`

Here is an example of Awaitlet usage::

import asyncio
import awaitlet

def asyncio_sleep():
return awaitlet.awaitlet(asyncio.sleep(5, result='hello'))

print(asyncio.run(awaitlet.async_def(asyncio_sleep)))

We invite the reader to read the `Awaitlet synopsis
<https://awaitlet.sqlalchemy.org/en/latest/synopsis.html>`_ to get a better
overview of the opportunities offered by this library.
36 changes: 36 additions & 0 deletions doc/source/asyncio/guide/deprecation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.. _manage-your-deprecations:

Manage Your Deprecations
========================

Libraries or applications may have specific features who are strongly related
to Eventlet, like the ``heartbeat_in_pthread`` feature in
the Opentack `oslo.messaging
<https://docs.openstack.org/oslo.messaging/latest/configuration/opts.html#oslo_messaging_rabbit.heartbeat_in_pthread>`_
deliverable.

Migrating off of Eventlet would make these features obsolete. As this kind of
feature expose configuration endpoints people would have to deprecate them to
allow your users to update their config files accordingly. However, the
deprecation process would take several months or even numerous versions before
hoping to see these features removed. Hence blocking the migration.

The proposed solution is to mock these features with empty entrypoints
who will only raise deprecation warnings to inform your users that they have
to update their config files. After 1 or 2 new versions these empty mocks
could be safely removed without impacting anybody.

In other words, these feature will remain in the code, but they will do
nothing. They will be empty feature allowing us to migrate properly.

Example with the ``heartbeat_in_pthread`` feature, by using Asyncio
we wouldn't have to run heartbeats in a separated threads. This feature,
the RabbitMQ heartbeat, would be run in a coroutine. A coroutine who is
ran in the main native thread. The config option will remain available but
it will only show a deprecation warning like the following one::

__main__:1: DeprecationWarning: Using heartbeat_in_pthread is
deprecated and will be removed in {SERIES}. Enabling that feature
have no functional effects due to recent changes applied in the
networking model used by oslo.messaging. Please plan an update of your
configuration.
158 changes: 158 additions & 0 deletions doc/source/asyncio/guide/glossary.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
.. _glossary_guide:

Glossary
========

This glossary provides a brief description of some of the terms used within
Eventlet in general, and more specifically in the migration context.
The goal of this glossary is to ensure that everybody has the same
understanding of the used terms.

For more information about anything the migration, see the
:ref:`migration-guide`.

.. _glossary-concurrency:

Concurrency
-----------

**Concurrency** is when two or more tasks can start, run, and complete in
overlapping time **periods**. It doesn't necessarily mean they'll ever both be
running **at the same instant**. For example, _multitasking_ on a single-core
machine.

.. _glossary-cooperative-multitasking:

Cooperative Multitasking
------------------------

Whenever a **thread** begins sleeping or awaiting network I/O, there is a
chance for another thread to take the **GIL** and execute Python code.
This is **cooperative multitasking**.

.. _glossary-coro:

Coro
----

Using the name **coro** is a common convention in the Python API
documentation. It refers to a coroutine; i.e., strictly speaking, the result
of calling an async def function, and not the function itself.

.. _glossary-coroutine:

Coroutine
---------

**Coroutines** are programs components that allow execution to be suspended
and resumed, generalizing. They have been described as "functions whose
execution you can pause".

.. _glossary-future:

Future
------

A **future** represents a future completion state of some activity and is
managed by the loop. A Future is a special low-level awaitable object that
represents an eventual result of an asynchronous operation.

.. _glossary-greenlet:

Greenlet
--------

A **greenlet** is a lightweight **coroutine** for in-process sequential
concurrent programming (see **concurrency**). You can usually think of
greenlets as cooperatively scheduled **threads**. The major differences are
that since they’re cooperatively scheduled, you are in control of when they
execute, and since they are **coroutines**, many greenlets can exist in a
single native **thread**.

Greenlets are cooperative (see **cooperative multitasking**) and sequential.
This means that when one greenlet is running, no other greenlet can be
running; the programmer is fully in control of when execution switches between
greenlets. In other words ones, when using greenlets, should not expect
**preemptive** behavior.

Greenlet is also the name of a `library
<https://greenlet.readthedocs.io/en/latest/>`_ that provide the greenlet
mechanism. Eventlet is based on the greenlet library.

.. _glossary-green-thread:

Green Thread
------------

A **green thread** is a **threads** that is scheduled by a runtime library
or virtual machine (VM) instead of natively by the underlying operating system
(OS). Green threads emulate multithreaded environments without relying on any
native OS abilities, and they are managed in user space) instead of kernel
space, enabling them to work in environments that do not have native thread
support.

.. _glossary-gil:

Global Interpreter Lock (GIL)
-----------------------------

A **global interpreter lock (GIL**) is a lock used internally to CPython to
ensure that only one **thread** runs in the Python VM at a time. In general,
Python offers to switch among threads only between bytecode instructions (see
**preemptive multitasking** and **cooperative multitasking**).

.. _glossary-parallelism:

Parallelism
-----------

**Parallelism** is when tasks _literally_ run at the same time, e.g., on a
multicore processor. A condition that arises when at least two threads are
executing simultaneously.

.. _glossary-preemptive:

Preemptive/Preemption
---------------------

**Preemption** is the act of temporarily interrupting an executing **task**,
with the intention of resuming it at a later time. This interrupt is done by
an external scheduler with no assistance or cooperation from the task.

.. _glossary-preemptive-multitasking:

Preemptive multitasking
-----------------------

**Preemptive multitasking** involves the use of an interrupt mechanism which
suspends the currently executing process and invokes a scheduler to determine
which process should execute next. Therefore, all processes will get some
amount of CPU time at any given time.

CPython also has _preemptive multitasking_: If a thread runs
uninterrupted for 1000 bytecode instructions in Python 2, or runs 15
milliseconds in Python 3, then it gives up the GIL and another thread may run.

.. _glossary-task:

Task
----

A **task** is a scheduled and independently managed **coroutine**. Tasks are
awaitable objects used to schedule coroutines concurrently.

.. _glossary-thread:

Thread
------

**Threads** are a way for a program to divide (termed "split") itself into two
or more simultaneously (or pseudo-simultaneously) running tasks. Threads and
processes differ from one operating system to another but, in general, a
thread is contained inside a process and different threads in the same process
share same resources while different processes in the same multitasking
operating system do not.

When do threads switch in Python? The switch depends on the context. The
threads may be interrupted (see **preemptive multitasking**) or behave
cooperatively (see **cooperative multitasking**).
Loading

0 comments on commit 8bac9b2

Please sign in to comment.