Skip to content

Commit

Permalink
Usage of tenacity, refactored logic and some decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
willianantunes committed May 29, 2019
1 parent 09d48fa commit cd450f2
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 29 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ gql = "*"
requests = "*"
python-dateutil = "*"
stomp-py = "*"
tenacity = "*"

[requires]
python_version = "3.7"
Expand Down
48 changes: 35 additions & 13 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 28 additions & 16 deletions django_graphql_playground/apps/pubsub/services/producer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
import ssl
import uuid
from contextlib import contextmanager
from typing import Callable
from typing import Dict

import stomp
import tenacity
from django.core.serializers.json import DjangoJSONEncoder
from stomp.connect import StompConnection11

from django_graphql_playground.support.decorators import slow_down

logger = logging.getLogger(__name__)


Expand All @@ -23,6 +27,7 @@ def __init__(self, connection: StompConnection11, connection_configuration: Dict
def is_open(self):
return self._connection.is_connected()

@slow_down
def start(self):
self._connection.start()
self._connection.connect(**self._connection_configuration)
Expand All @@ -32,25 +37,32 @@ def close(self):
self._connection.disconnect()
logger.info("Disconnected")

def send(self, body, headers=None):
if hasattr(self, "_tmp_transaction_id"):
self._connection.send(
self._destination_name,
body=json.dumps(body, cls=DjangoJSONEncoder),
content_type=self._default_content_type,
headers=headers,
transaction=self._tmp_transaction_id,
)
else:
def send(self, body, headers=None, attempt=10):
send_params = {
"destination": self._destination_name,
"body": json.dumps(body, cls=DjangoJSONEncoder),
"headers": headers,
"content_type": self._default_content_type,
"transaction": getattr(self, "_tmp_transaction_id", None),
}
send_params = {k: v for k, v in send_params.items() if v is not None}

def _internal_send_logic():
if not self.is_open():
logger.info("It is not open. Starting...")
self.start()
self._connection.send(
self._destination_name,
body=json.dumps(body, cls=DjangoJSONEncoder),
content_type=self._default_content_type,
headers=headers,
)
self._connection.send(**send_params)

self._retry_send(_internal_send_logic, attempt=attempt)

def _retry_send(self, function: Callable, attempt=10, *args, **kwargs):
retry_configuration = tenacity.Retrying(
stop=tenacity.stop_after_attempt(attempt),
wait=tenacity.wait_fixed(3) + tenacity.wait_random(0, 2),
after=tenacity.after_log(logger, logger.level) if logger else None,
reraise=True,
)
return retry_configuration(function, *args, **kwargs)


def build_publisher(destination_name, **connection_params) -> _Publisher:
Expand Down
46 changes: 46 additions & 0 deletions django_graphql_playground/support/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import functools
import logging
import time
from functools import wraps

logger = logging.getLogger(__name__)


def measure_it(func):
@wraps(func)
def timed(*args, **kw):
start = time.perf_counter()
try:
return func(*args, **kw)
finally:
elapsed = time.perf_counter() - start
logger.info(f"{func.__name__} from {func.__module__} with args {args} took {elapsed:0.2f} seconds")

return timed


def auto_str(cls):
def __str__(self):
return f"{type(self).__name__}" f"({', '.join('%s=%s' % item for item in vars(self).items())})"

cls.__str__ = __str__
return cls


def slow_down(_func=None, *, before=1, after=1):
"""Sleep given amount of seconds before calling the function"""

def decorator_slow_down(func):
@functools.wraps(func)
def wrapper_slow_down(*args, **kwargs):
time.sleep(before)
value = func(*args, **kwargs)
time.sleep(after)
return value

return wrapper_slow_down

if _func is None:
return decorator_slow_down
else:
return decorator_slow_down(_func)

0 comments on commit cd450f2

Please sign in to comment.