Skip to content

Commit

Permalink
Correctly import eventlet to prevent threads blocking each other
Browse files Browse the repository at this point in the history
Currently, slow running OpenStack API Requests (either stuck connecting
or still waiting for the actual response) from the periodic DataGatherer
task will block HTTPServer connections from being processed. Blocked
HTTPServer connections will also block both other connections and the
DataGatherer task.

Observed Symptoms:
- Slow or failed prometheus requests
- Statistics not being updated as often as you would expect
- HTTP 500 responses and BrokenPipeError tracebacks being logged due to
  later trying to respond to prometheus clients which timed out and
  disconnected the socket
- Hitting the forked process limit

This happens because in the current code, we are intending to use the
eventlet library for asynchronous non-blocking I/O, but we are not using
it correctly. All code within the main application and all imported
dependencies must import the special eventlet "green" versions of many
python libraries (e.g. socket, time, threading, SimpleHTTPServer, etc)
which yield to other green threads when they would have blocked waiting
for I/O or to sleep. Currently this does not always happen.

Fix this by importing eventlet and using eventlet.patcher.monkey_patch()
before importing any other modules. This will automatically intercept
all future imports (including those inside dependencies) and
automatically load the green versions of relevant libraries.

Documentation on correctly import eventlet can be found here:
https://eventlet.readthedocs.io/en/latest/patching.html

A detailed and comprehensive analysis of the issue and multiple previous
attempts to fix it can be found in Issue #130. If you intend to make
further related changes to the use of eventlet, threads or forked
processes please read the detailed history lesson available there.

Fixes: #130, #126, #124, #116, #115, #112
  • Loading branch information
lathiat committed Jun 10, 2024
1 parent 19816fa commit 0e57e4b
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 13 deletions.
23 changes: 10 additions & 13 deletions prometheus-openstack-exporter
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

import argparse
# We must import and monkey patch before importing any other modules:
# https://eventlet.readthedocs.io/en/latest/patching.html
import eventlet

eventlet.patcher.monkey_patch()

import argparse # noqa: I100
import ast
import json
import logging.handlers
Expand All @@ -34,15 +40,11 @@ from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
from os import environ as env
from os import path, rename
from socketserver import ForkingMixIn
from threading import Thread
from time import sleep, time

from cinderclient.v3 import client as cinder_client

from eventlet.green import asyncore # noqa: F401
from eventlet.green import socket # noqa: F401
from eventlet.green.threading import Thread
from eventlet.green.time import sleep, time

from netaddr import IPRange

from neutronclient.v2_0 import client as neutron_client
Expand Down Expand Up @@ -273,10 +275,8 @@ class DataGatherer(Thread):

log.debug("DataGatherer: Update keystone")
prodstack.update(self._get_keystone_info(keystone))
sleep(0)
log.debug("DataGatherer: Update neutron")
prodstack.update(self._get_neutron_info(neutron))
sleep(0)
log.debug("DataGatherer: Update nova")
prodstack.update(self._get_nova_info(nova, cinder, prodstack))

Expand Down Expand Up @@ -1143,7 +1143,4 @@ if __name__ == "__main__":
data_gatherer = DataGatherer()
data_gatherer.start()
server = HTTPServer(("", config.get("listen_port")), handler)
server.timeout = config.get("cache_refresh_interval", 900)
while True:
sleep(0)
server.handle_request()
server.serve_forever()
4 changes: 4 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ deps =
flake8-colors

[flake8]
extend-ignore =
# E402: E402 module level import not at top of file
# eventlet.monkey_patch() is required before importing other modules
E402
exclude =
.git,
__pycache__,
Expand Down

0 comments on commit 0e57e4b

Please sign in to comment.