Refactored backends to be more robust by wrapping the core logic into a try-except logic with sleep and retry

This commit is contained in:
Fabio Manganiello 2018-10-25 19:46:13 +02:00
parent 0a7722d858
commit 6ce348365f
25 changed files with 265 additions and 259 deletions

View File

@ -7,6 +7,7 @@ import importlib
import logging
import sys
import threading
import time
from threading import Thread
@ -34,6 +35,7 @@ class Backend(Thread):
"""
_default_response_timeout = 5
_backend_reload_timeout = 10
def __init__(self, bus=None, **kwargs):
"""
@ -211,9 +213,38 @@ class Backend(Thread):
redis.send_message(msg, queue_name=queue_name)
def exec(self):
""" Backend thread logic. To be implemented in the derived classes """
raise RuntimeError('Backend class {} does not implement the exec() method'.
format(self.__class__.__name__))
def run(self):
""" Starts the backend thread. To be implemented in the derived classes """
""" Thread runner. It wraps the exec method in a retry block """
super().run()
self.thread_id = threading.get_ident()
error = None
while not self.should_stop():
try:
self.exec()
except Exception as e:
error = e
if not self.should_stop():
if error:
self.logger.error(('Backend {} terminated with an exception, ' +
'reloading in {} seconds').format(
self.__class__.__name__,
self._backend_reload_timeout))
self.logger.exception(error)
else:
self.logger.warning(('Backend {} unexpectedly terminated, ' +
'reloading in {} seconds').format(
self.__class__.__name__,
self._backend_reload_timeout))
time.sleep(self._backend_reload_timeout)
def on_stop(self):
""" Callback invoked when the process stops """

View File

@ -110,8 +110,7 @@ class AssistantGoogleBackend(Backend):
self.assistant.stop_conversation()
def run(self):
super().run()
def exec(self):
with Assistant(self.credentials, self.device_model_id) as assistant:
self.assistant = assistant

View File

@ -176,9 +176,8 @@ class AssistantGooglePushtotalkBackend(Backend):
""" Speech recognized handler """
self.bus.post(SpeechRecognizedEvent(phrase=speech))
def run(self):
def exec(self):
""" Backend executor """
super().run()
with SampleAssistant(self.lang, self.device_model_id, self.device_id,
self.conversation_stream,

View File

@ -72,8 +72,7 @@ class AssistantSnowboyBackend(Backend):
self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
return callback
def run(self):
super().run()
def exec(self):
self.detector.start(self.hotword_detected())

View File

@ -112,8 +112,7 @@ class ButtonFlicBackend(Backend):
return _f
def run(self):
super().run()
def exec(self):
self.client.handle_events()

View File

@ -160,8 +160,7 @@ class CameraPiBackend(Backend):
self.logger.warning('Failed to stop recording')
self.logger.exception(e)
def run(self):
super().run()
def exec(self):
if not self.redis:
self.redis = get_backend('redis')

View File

@ -377,8 +377,7 @@ class HttpBackend(Backend):
loop.run_forever()
def run(self):
super().run()
def exec(self):
os.putenv('FLASK_APP', 'platypush')
os.putenv('FLASK_ENV', 'production')
self.logger.info('Initialized HTTP backend on port {}'.format(self.port))

View File

@ -72,8 +72,7 @@ class HttpPollBackend(Backend):
self.requests.append(request)
def run(self):
super().run()
def exec(self):
while not self.should_stop():
for request in self.requests:

View File

@ -48,8 +48,7 @@ class InotifyBackend(Backend):
self.inotify_watch = None
def run(self):
super().run()
def exec(self):
self.inotify_watch = inotify.adapters.Inotify()
for path in self.watch_paths:

View File

@ -29,8 +29,7 @@ class JoystickBackend(Backend):
self.device = device
def run(self):
super().run()
def exec(self):
self.logger.info('Initialized joystick backend on device {}'.format(self.device))
while not self.should_stop():

View File

@ -81,8 +81,7 @@ class KafkaBackend(Backend):
self.logger.warning('Exception occurred while closing Kafka connection')
self.logger.exception(e)
def run(self):
super().run()
def exec(self):
self.consumer = KafkaConsumer(self.topic, bootstrap_servers=self.server)
self.logger.info('Initialized kafka backend - server: {}, topic: {}'

View File

@ -61,8 +61,7 @@ class LocalBackend(Backend):
return Message.build(msg) if len(msg) else None
def run(self):
super().run()
def exec(self):
self.logger.info('Initialized local backend on {} and {}'.
format(self.request_fifo, self.response_fifo))

View File

@ -102,8 +102,7 @@ class MidiBackend(Backend):
return callback
def run(self):
super().run()
def exec(self):
self.midi.open_port(self.port_number)
self.logger.info('Initialized MIDI backend, listening for events on device {}'.

View File

@ -45,7 +45,7 @@ class MqttBackend(Backend):
publisher.single(self.topic, str(msg), hostname=self.host, port=self.port)
def run(self):
def exec(self):
def on_connect(client, userdata, flags, rc):
client.subscribe(self.topic)
@ -57,7 +57,6 @@ class MqttBackend(Backend):
self.logger.info('Received message on the MQTT backend: {}'.format(msg))
self.on_message(msg)
super().run()
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

View File

@ -39,8 +39,7 @@ class MusicMpdBackend(Backend):
self.poll_seconds = poll_seconds
def run(self):
super().run()
def exec(self):
last_status = {}
last_state = None

View File

@ -175,8 +175,7 @@ class PushbulletBackend(Backend):
def on_stop(self):
self.ws.close()
def run(self):
super().run()
def exec(self):
self._init_socket()
self.logger.info('Initialized Pushbullet backend - device_id: {}'

View File

@ -60,8 +60,7 @@ class RedisBackend(Backend):
return msg
def run(self):
super().run()
def exec(self):
self.logger.info('Initialized Redis backend on queue {} with arguments {}'.
format(self.queue, self.redis_args))

View File

@ -50,8 +50,7 @@ class ScardBackend(Backend):
self.cardtype = AnyCardType()
def run(self):
super().run()
def exec(self):
self.logger.info('Initialized smart card reader backend - ATR filter: {}'.
format(self.ATRs))

View File

@ -40,8 +40,7 @@ class SensorBackend(Backend):
""" To be implemented in the derived classes """
raise NotImplementedError('To be implemented in a derived class')
def run(self):
super().run()
def exec(self):
self.logger.info('Initialized {} sensor backend'.format(self.__class__.__name__))
while not self.should_stop():

View File

@ -32,9 +32,7 @@ class SensorIrZeroborgBackend(Backend):
self.logger.info('Initialized Zeroborg infrared sensor backend')
def run(self):
super().run()
def exec(self):
while True:
try:
self.zb.GetIrMessage()

View File

@ -61,9 +61,7 @@ class SensorLeapBackend(Backend):
self.position_tolerance = position_tolerance
def run(self):
super().run()
def exec(self):
listener = LeapListener(position_ranges=self.position_ranges,
position_tolerance=self.position_tolerance)

View File

@ -90,9 +90,7 @@ class TcpBackend(Backend):
threading.Thread(target=_f_wrapper).run()
def run(self):
super().run()
def exec(self):
serv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serv_sock.bind((self.bind_address, self.port))

View File

@ -27,8 +27,7 @@ class WeatherForecastBackend(Backend):
def send_message(self, msg):
pass
def run(self):
super().run()
def exec(self):
weather = get_plugin('weather.forecast')
self.logger.info('Initialized weather forecast backend')