Implemented timestamp mark on messages to trigger message expiration logic in case something stays on the bus for longer than a minute

This commit is contained in:
Fabio Manganiello 2018-10-08 10:35:56 +00:00
parent c10e882dd0
commit 948f3dc37d
6 changed files with 36 additions and 8 deletions

View File

@ -12,7 +12,6 @@ import traceback
from threading import Thread
from .bus import Bus
from .bus.redis import RedisBus
from .config import Config
from .context import register_backends

View File

@ -1,5 +1,6 @@
import logging
import threading
import time
from queue import Queue
@ -12,6 +13,9 @@ logger = logging.getLogger(__name__)
class Bus(object):
""" Main local bus where the daemon will listen for new messages """
_MSG_EXPIRY_TIMEOUT = 60.0 # Consider a message on the bus as expired
# after one minute without being picked up
def __init__(self, on_message=None):
self.bus = Queue()
self.on_message = on_message
@ -45,6 +49,11 @@ class Bus(object):
stop=False
while not stop:
msg = self.get()
if msg.timestamp and time.time() - msg.timestamp > self._MSG_EXPIRY_TIMEOUT:
logger.info('{} seconds old message on the bus expired, ignoring it: {}'
.format(int(time.time()-msg.timestamp), msg))
continue
self.on_message(msg)
if isinstance(msg, StopEvent) and msg.targets_me():

View File

@ -1,6 +1,7 @@
import logging
import inspect
import json
import time
logger = logging.getLogger(__name__)
@ -17,7 +18,7 @@ class Message(object):
return json.dumps({
attr: getattr(self, attr)
for attr in self.__dir__()
if not attr.startswith('_')
if (attr != '_timestamp' or not attr.startswith('_'))
and not inspect.ismethod(getattr(self, attr))
}).replace('\n', ' ')
@ -48,6 +49,10 @@ class Message(object):
logger.warning('Invalid JSON message: {}'.format(msg))
assert isinstance(msg, dict)
if not '_timestamp' in msg:
msg['_timestamp'] = time.time()
return msg
@classmethod

View File

@ -3,6 +3,7 @@ import json
import random
import re
import threading
import time
from datetime import date
@ -13,7 +14,7 @@ from platypush.utils import get_event_class_by_type
class Event(Message):
""" Event message class """
def __init__(self, target=None, origin=None, id=None, **kwargs):
def __init__(self, target=None, origin=None, id=None, timestamp=None, **kwargs):
"""
Params:
target -- Target node [String]
@ -28,6 +29,7 @@ class Event(Message):
self.type = '{}.{}'.format(self.__class__.__module__,
self.__class__.__name__)
self.args = kwargs
self.timestamp = timestamp or time.time()
@classmethod
def build(cls, msg):
@ -42,6 +44,7 @@ class Event(Message):
args['id'] = msg['id'] if 'id' in msg else cls._generate_id()
args['target'] = msg['target'] if 'target' in msg else Config.get('device_id')
args['origin'] = msg['origin'] if 'origin' in msg else Config.get('device_id')
args['timestamp'] = msg['_timestamp'] if '_timestamp' in msg else time.time()
return event_class(**args)
@ -168,6 +171,7 @@ class Event(Message):
'target' : self.target,
'origin' : self.origin if hasattr(self, 'origin') else None,
'id' : self.id if hasattr(self, 'id') else None,
'_timestamp' : self.timestamp,
'args' : {
'type' : self.type,
**args

View File

@ -4,6 +4,7 @@ import json
import logging
import random
import re
import time
import traceback
from threading import Thread
@ -21,16 +22,17 @@ class Request(Message):
""" Request message class """
def __init__(self, target, action, origin=None, id=None, backend=None,
args=None, token=None):
args=None, token=None, timestamp=None):
"""
Params:
target -- Target node [String]
action -- Action to be executed (e.g. music.mpd.play) [String]
origin -- Origin node [String]
target -- Target node [Str]
action -- Action to be executed (e.g. music.mpd.play) [Str]
origin -- Origin node [Str]
id -- Message ID, or None to get it auto-generated
backend -- Backend connected to the request, where the response will be delivered
args -- Additional arguments for the action [Dict]
token -- Authorization token, if required on the server [Str]
timestamp -- Message creation timestamp [Float]
"""
self.id = id if id else self._generate_id()
@ -40,6 +42,7 @@ class Request(Message):
self.args = args if args else {}
self.backend = backend
self.token = token
self.timestamp = timestamp or time.time()
@classmethod
def build(cls, msg):
@ -51,6 +54,7 @@ class Request(Message):
}
args['id'] = msg['id'] if 'id' in msg else cls._generate_id()
args['timestamp'] = msg['_timestamp'] if '_timestamp' in msg else time.time()
if 'origin' in msg: args['origin'] = msg['origin']
if 'token' in msg: args['token'] = msg['token']
return cls(**args)
@ -240,6 +244,7 @@ class Request(Message):
'origin' : self.origin if hasattr(self, 'origin') else None,
'id' : self.id if hasattr(self, 'id') else None,
'token' : self.token if hasattr(self, 'token') else None,
'_timestamp' : self.timestamp,
})

View File

@ -1,11 +1,13 @@
import json
import time
from platypush.message import Message
class Response(Message):
""" Response message class """
def __init__(self, target=None, origin=None, id=None, output=None, errors=[]):
def __init__(self, target=None, origin=None, id=None, output=None, errors=[],
timestamp=None):
"""
Params:
target -- Target [String]
@ -13,12 +15,14 @@ class Response(Message):
output -- Output [String]
errors -- Errors [List of strings or exceptions]
id -- Message ID this response refers to
timestamp -- Message timestamp [Float]
"""
self.target = target
self.output = self._parse_msg(output)
self.errors = self._parse_msg(errors)
self.origin = origin
self.timestamp = timestamp or time.time()
self.id = id
def is_error(self):
@ -45,6 +49,7 @@ class Response(Message):
'errors' : msg['response']['errors'],
}
args['timestamp'] = msg['_timestamp'] if '_timestamp' in msg else time.time()
if 'id' in msg: args['id'] = msg['id']
if 'origin' in msg: args['origin'] = msg['origin']
return cls(**args)
@ -61,6 +66,7 @@ class Response(Message):
'type' : 'response',
'target' : self.target if hasattr(self, 'target') else None,
'origin' : self.origin if hasattr(self, 'origin') else None,
'_timestamp' : self.timestamp,
'response' : {
'output' : self.output,
'errors' : self.errors,