Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No logging by default. #90

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions rc_django/cache_implementation/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.conf import settings

enable_logging = getattr(settings, 'CACHE_ENABLE_LOGGING', None)


def log_err(logger, msg):
if enable_logging:
logger.error(msg)
105 changes: 52 additions & 53 deletions rc_django/cache_implementation/memcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Contains memcached cache implementations
"""

import bmemcached
from bmemcached import Client
from bmemcached.exceptions import MemcachedException
import logging
import pickle
Expand All @@ -11,6 +11,7 @@
from django.conf import settings
from django.utils import timezone
from restclients_core.models import MockHTTP
from rc_django.cache_implementation.logger import log_err


logger = logging.getLogger(__name__)
Expand All @@ -21,12 +22,19 @@ class MemcachedCache(object):
def __init__(self):
self._set_client()

def deleteCache(self, service, url):
key = self._get_key(service, url)
try:
return self.client.delete(key)
except MemcachedException as ex:
log_err(logger, "MemCached Delete(key: {}) => {}".format(key, ex))

def getCache(self, service, url, headers):
key = self._get_key(service, url)
try:
data = self.client.get(key)
except MemcachedException as ex:
logger.error("Get (key: {}) ==> {}".format(key, str(ex)))
log_err(logger, "MemCached Get(key: {}) => {}".format(key, ex))
return

if not data:
Expand All @@ -40,6 +48,22 @@ def getCache(self, service, url, headers):

return {"response": response}

def processResponse(self, service, url, response):
header_data = {}
for header in response.headers:
header_data[header] = response.getheader(header)

key = self._get_key(service, url)
cdata, time_to_store = self._make_cache_data(
service, url, response.data, header_data,
response.status, timezone.now())
try:
self.client.set(key, cdata, time=time_to_store)
logger.debug("MemCached Set(key: {}) for {:d} seconds".format(
key, time_to_store))
except MemcachedException as ex:
log_err(logger, "MemCached Set(key: {}) ==> {}".format(key, ex))

def updateCache(self, service, url, new_data, new_data_dt):
"""
:param new_data: a string representation of the data
Expand All @@ -48,40 +72,34 @@ def updateCache(self, service, url, new_data, new_data_dt):
:raise MemcachedException: if update failed
"""
key = self._get_key(service, url)

# clear existing data
try:
value = self.client.get(key)
if value:
data = pickle.loads(value, encoding="utf8")
if "time_stamp" in data:
cached_data_dt = parse(data["time_stamp"])
if new_data_dt > cached_data_dt:
self.client.delete(key)
# may raise MemcachedException
logger.info(
"IN cache (key: {}), older DELETE".format(key))
else:
logger.info(
"IN cache (key: {}), newer KEEP".format(key))
return
else:
logger.info("NOT IN cache (key: {})".format(key))

except MemcachedException as ex:
logger.error(
"Clear existing data (key: {}) ==> {}".format(key, str(ex)))
return

# store new value in cache
cdata, time_to_store = self._make_cache_data(
service, url, new_data, {}, 200, new_data_dt)

self.client.set(key, cdata, time=time_to_store)
# may raise MemcachedException
logger.info(
"MemCached SET (key {}) for {:d} seconds".format(
key, time_to_store))
value = self.client.get(key)
if value is None:
# not in cache
try:
self.client.set(key, cdata, time=time_to_store)
logger.debug("MemCached Set(key {}) for {:d} seconds".format(
key, time_to_store))
return
except MemcachedException as ex:
log_err(logger, "MemCached Set(key: {}) => {}".format(key, ex))
raise

data = pickle.loads(value, encoding="utf8")
if "time_stamp" in data:
cached_data_dt = parse(data["time_stamp"])
if new_data_dt <= cached_data_dt:
logger.debug("IN cache (key: {}), KEEP".format(key))
return
# replace existing value in cache
try:
self.client.replace(key, cdata, time=time_to_store)
logger.debug("IN cache (key: {}), REPLACE".format(key))
except MemcachedException as ex:
log_err(logger, "MemCached replace(key: {}) => {}".format(key, ex))
raise

def _make_cache_data(self, service, url, data_to_cache,
header_data, status, time_stamp):
Expand All @@ -93,25 +111,6 @@ def _make_cache_data(self, service, url, data_to_cache,
time_to_store = self.get_cache_expiration_time(service, url)
return pickle.dumps(data), time_to_store

def processResponse(self, service, url, response):
header_data = {}
for header in response.headers:
header_data[header] = response.getheader(header)

key = self._get_key(service, url)
cdata, time_to_store = self._make_cache_data(service, url,
response.data,
header_data,
response.status,
timezone.now())
try:
self.client.set(key, cdata, time=time_to_store)
logger.info("MemCached set with key '{}', {:d} seconds".format(
key, time_to_store))
except bmemcached.exceptions.MemcachedException as ex:
logger.error("set (key: {}) ==> {}".format(key, str(ex)))
return

def get_cache_expiration_time(self, service, url):
# Over-ride this to define your own.
return 60 * 60 * 4
Expand All @@ -132,5 +131,5 @@ def _set_client(self):
username = getattr(settings, "RESTCLIENTS_MEMCACHED_USER", None)
password = getattr(settings, "RESTCLIENTS_MEMCACHED_PASS", None)

self.client = bmemcached.Client(servers, username, password)
self.client = Client(servers, username, password)
MemcachedCache._memcached_cache[thread_id] = self.client
50 changes: 49 additions & 1 deletion rc_django/tests/cache_implementation/test_memcached.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from django.test import TestCase
from django.utils import timezone
from restclients_core.models import MockHTTP
from rc_django.cache_implementation.memcache import MemcachedCache
from rc_django.cache_implementation.memcache import (
MemcachedCache, Client, MemcachedException)


@skipIf(not getattr(settings, 'RESTCLIENTS_MEMCACHED_SERVERS', None),
Expand Down Expand Up @@ -32,6 +33,17 @@ def test_cacheGet(self):
self.assertEquals(response.status, 200)
self.assertEquals(response.data, '{"data": "Body Content"}')

# delete existing entry
self.assertTrue(cache.deleteCache('mem', '/same'))
# second delete
self.assertTrue(cache.deleteCache('mem', '/same') is False)

cache.client = MockClient1()
# test get err
cache.getCache('mem', '/same', {})
# test delete err
self.assertFalse(cache.deleteCache('mem', '/same'))

def test_updateCache(self):
cache = MemcachedCache()
# cache no data
Expand All @@ -54,6 +66,15 @@ def test_updateCache(self):
response = hit["response"]
self.assertEquals(response.data, '{"data": "Content2"}')

# test replace err
cache.client = MockClient2()
cache.updateCache('mem', '/same', '{}', timezone.now())

# test set err
cache = MemcachedCache()
cache.client = MockClient2()
cache.updateCache('mem', '/same', '{}', timezone.now())

def test_processResponse(self):
mock_resp = MockHTTP()
mock_resp.status = 200
Expand All @@ -69,6 +90,15 @@ def test_processResponse(self):
self.assertEquals(response.status, 200)
self.assertEquals(response.data, "Content4")

def test_processResponse_err(self):
mock_resp = MockHTTP()
mock_resp.status = 200
mock_resp.data = "Content4"
mock_resp.headers = {"Content-type": "text/html"}
cache = MemcachedCache()
cache.client = MockClient2()
cache.processResponse('mem', '/same1', mock_resp)

def test_binary_processResponse(self):
mock_resp = MockHTTP()
mock_resp.status = 200
Expand All @@ -94,3 +124,21 @@ def test_memcached_client(self):
response = hit["response"]
self.assertEquals(response.headers, {})
self.assertEquals(response.status, 200)


class MockClient1(Client):

def delete(self, key):
raise MemcachedException("err", 400)

def get(self, key, get_cas=False):
raise MemcachedException("err", 400)


class MockClient2(Client):

def replace(self, key, value, time=0, compress_level=-1):
raise MemcachedException("err", 400)

def set(self, key, value, time=0, compress_level=-1):
raise MemcachedException("err", 400)
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
include_package_data=True,
install_requires=[
'Django>=2.1,<3.0',
'UW-RestClients-Core>1.0,<2.0',
'django-userservice>3.1,<4.0',
'UW-RestClients-Core<2.0',
'django-userservice<4.0',
'python-binary-memcached',
'python-dateutil',
],
license='Apache License, Version 2.0',
description=('UW-RestClients-Django-Utils'),
Expand Down
1 change: 1 addition & 0 deletions travis-ci/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
USE_TZ = True

RESTCLIENTS_MEMCACHED_SERVERS=('localhost:11211', )
CACHE_ENABLE_LOGGING = 1