From 5bcde84e1407e169cc8ae00def2aaaa86cbcd3e9 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 15 May 2014 16:22:28 -0400 Subject: [PATCH] Fix cpu churn when web sessions open and idle It turns out that eventlet.green.threading.Event() doesn't behave very efficiently in this context for whatever reason. Use eventlet.event.Event() instead. It was not used before due to lack of timeout and clear, but that is overcome by disposing of it rather than reusing and using with eventlet.Timeout() to add timeout to wait that doesn't have built in timeout. --- TODO | 30 ++++++++++++++++++++- confluent_server/confluent/consoleserver.py | 16 ++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index 13d09bdb..a77e25c3 100644 --- a/TODO +++ b/TODO @@ -33,4 +33,32 @@ KeyError: '' -read exclusive and full exclusive console access modes -when an http session exists, confluent cpu usage on my vm idles at 2 %. profile exactly what is spinning and try to make it nicer -- an ipmi console was once stuck in 'connecting' state, no idea how \ No newline at end of file +- an ipmi console was once stuck in 'connecting' state, no idea how, this traceback might have occurred: +Traceback (most recent call last): + File "/usr/lib/python2.6/site-packages/eventlet/hubs/hub.py", line 346, in fire_timers + timer() + File "/usr/lib/python2.6/site-packages/eventlet/hubs/timer.py", line 56, in __call__ + cb(*args, **kw) + File "/usr/lib/python2.6/site-packages/eventlet/semaphore.py", line 121, in _do_acquire + waiter.switch() + File "/usr/lib/python2.6/site-packages/eventlet/greenthread.py", line 194, in main + result = function(*args, **kwargs) + File "/usr/lib/python2.6/site-packages/confluent/consoleserver.py", line 177, in _connect_backend + self._console.connect(self.get_console_output) + File "/usr/lib/python2.6/site-packages/confluent/plugins/hardwaremanagement/ipmi.py", line 143, in connect + iohandler=self.handle_data) + File "/usr/lib/python2.6/site-packages/pyghmi/ipmi/console.py", line 65, in __init__ + session.Session.wait_for_rsp(0) + File "/usr/lib/python2.6/site-packages/pyghmi/ipmi/private/session.py", line 914, in wait_for_rsp + cls._route_ipmiresponse(sockaddr, data) + File "/usr/lib/python2.6/site-packages/pyghmi/ipmi/private/session.py", line 991, in _route_ipmiresponse + sockaddr=sockaddr) + File "/usr/lib/python2.6/site-packages/pyghmi/ipmi/private/session.py", line 1035, in _handle_ipmi_packet + self._handle_ipmi2_packet(data) + File "/usr/lib/python2.6/site-packages/pyghmi/ipmi/private/session.py", line 1063, in _handle_ipmi2_packet + self.k1, rawdata[4:-12], hashlib.sha1).digest()[:12] + File "/usr/lib64/python2.6/hmac.py", line 133, in new + return HMAC(key, msg, digestmod) + File "/usr/lib64/python2.6/hmac.py", line 68, in __init__ + if len(key) > blocksize: +TypeError: object of type 'NoneType' has no len() diff --git a/confluent_server/confluent/consoleserver.py b/confluent_server/confluent/consoleserver.py index 358986f8..05dbf208 100644 --- a/confluent_server/confluent/consoleserver.py +++ b/confluent_server/confluent/consoleserver.py @@ -27,7 +27,7 @@ import confluent.interface.console as conapi import confluent.log as log import confluent.core as plugin import eventlet -import eventlet.green.threading as threading +import eventlet.event import random import traceback @@ -416,7 +416,7 @@ class ConsoleSession(object): self.username = username connect_node(node, configmanager) _handled_consoles[consk].attachuser(username) - self._evt = threading.Event() + self._evt = None self.node = node self.conshdl = _handled_consoles[consk] self.write = _handled_consoles[consk].write @@ -448,7 +448,8 @@ class ConsoleSession(object): for data, we must maintain data in a buffer until retrieved """ self.databuffer.append(data) - self._evt.set() + if self._evt: + self._evt.send() def get_next_output(self, timeout=45): """Poll for next available output on this console. @@ -457,10 +458,13 @@ class ConsoleSession(object): at least one case where we don't have that luxury """ self.reaper.cancel() + if self._evt: + raise Exception('get_next_output is not re-entrant') if not self.databuffer: - self._evt.wait(timeout) - if self._evt is not None: - self._evt.clear() + self._evt = eventlet.event.Event() + with eventlet.Timeout(timeout, False): + self._evt.wait() + self._evt = None if not self.databuffer: self.reaper = eventlet.spawn_after(15, self.destroy) return ""