Skip to content

Commit

Permalink
Cope with cache connection errors
Browse files Browse the repository at this point in the history
Ensure requests don't get rate limited and don't throw errors when a
memcached instance is unavailable.
  • Loading branch information
tdhooper committed Oct 6, 2015
1 parent c8ab24a commit fa3f0af
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 18 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ python:
- "3.4"
- "pypy"
install:
- if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then pip install -q python-memcached>=1.57; fi
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then pip install -q python3-memcached>=1.51; fi
- if [[ $TRAVIS_PYTHON_VERSION == pypy ]]; then pip install -q python-memcached>=1.57; fi
- pip install -q "Django>=${DJANGO_VERSION},<${DJANGO_VERSION}.99" flake8
script:
- ./run.sh test
Expand Down
37 changes: 37 additions & 0 deletions ratelimit/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,16 @@ def view(request):
with self.assertRaises(InvalidCacheBackendError):
view(req)

@override_settings(RATELIMIT_USE_CACHE='connection-errors')
def test_cache_connection_error(self):

@ratelimit(key='ip', rate='1/m')
def view(request):
return request

req = rf.post('/')
assert view(req)

def test_user_or_ip(self):
"""Allow custom functions to set cache keys."""

Expand Down Expand Up @@ -389,6 +399,33 @@ def do_increment(request):
assert do_increment(req), 'Request should be rate limited.'
assert not_increment(req), 'Request should be rate limited.'

@override_settings(RATELIMIT_USE_CACHE='connection-errors')
def test_is_ratelimited_cache_connection_error_without_increment(self):
def get_key(group, request):
return 'test_is_ratelimited_key'

def not_increment(request):
return is_ratelimited(request, increment=False,
method=is_ratelimited.ALL, key=get_key,
rate='1/m', group='a')

req = rf.get('/')
assert not not_increment(req)

@override_settings(RATELIMIT_USE_CACHE='connection-errors')
def test_is_ratelimited_cache_connection_error_with_increment(self):
def get_key(group, request):
return 'test_is_ratelimited_key'

def do_increment(request):
return is_ratelimited(request, increment=True,
method=is_ratelimited.ALL, key=get_key,
rate='1/m', group='a')

req = rf.get('/')
assert not do_increment(req)
assert req.limited is False


class RatelimitCBVTests(TestCase):

Expand Down
7 changes: 5 additions & 2 deletions ratelimit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,12 @@ def is_ratelimited(request, group=None, fn=None, key=None, rate=None,
count = initial_value
else:
if increment:
count = cache.incr(cache_key)
try:
count = cache.incr(cache_key)
except ValueError:
count = 0
else:
count = cache.get(cache_key)
count = cache.get(cache_key) or 0
limited = count > limit
if increment:
request.limited = old_limited or limited
Expand Down
4 changes: 4 additions & 0 deletions test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'ratelimit-tests',
},
'connection-errors': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'test-connection-errors',
},
}

DATABASES = {
Expand Down
64 changes: 48 additions & 16 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,102 @@ commands = ./run.sh test

[testenv:py34-1.8]
basepython = python3.4
deps = Django>=1.8,<1.8.99
deps =
Django>=1.8,<1.8.99
python3-memcached>=1.51

[testenv:py34-1.7]
basepython = python3.4
deps = Django>=1.7,<1.7.99
deps =
Django>=1.7,<1.7.99
python3-memcached>=1.51

[testenv:py34-1.6]
basepython = python3.4
deps = Django>=1.6,<1.6.99
deps =
Django>=1.6,<1.6.99
python3-memcached>=1.51

[testenv:py34-1.5]
basepython = python3.4
deps = Django>=1.5,<1.5.99
deps =
Django>=1.5,<1.5.99
python3-memcached>=1.51

# python 3.3

[testenv:py33-1.8]
basepython = python3.3
deps = Django>=1.8,<1.8.99
deps =
Django>=1.8,<1.8.99
python3-memcached>=1.51

[testenv:py33-1.7]
basepython = python3.3
deps = Django>=1.7,<1.7.99
deps =
Django>=1.7,<1.7.99
python3-memcached>=1.51

[testenv:py33-1.6]
basepython = python3.3
deps = Django>=1.6,<1.6.99
deps =
Django>=1.6,<1.6.99
python3-memcached>=1.51

[testenv:py33-1.5]
basepython = python3.3
deps = Django>=1.5,<1.5.99
deps =
Django>=1.5,<1.5.99
python3-memcached>=1.51

# python 2.7

[testenv:py27-1.8]
basepython = python2.7
deps = Django>=1.7,<1.7.99
deps =
Django>=1.7,<1.7.99
python-memcached>=1.57

[testenv:py27-1.7]
basepython = python2.7
deps = Django>=1.7,<1.7.99
deps =
Django>=1.7,<1.7.99
python-memcached>=1.57

[testenv:py27-1.6]
basepython = python2.7
deps = Django>=1.6,<1.6.99
deps =
Django>=1.6,<1.6.99
python-memcached>=1.57

[testenv:py27-1.5]
basepython = python2.7
deps = Django>=1.5,<1.5.99
deps =
Django>=1.5,<1.5.99
python-memcached>=1.57

[testenv:py27-1.4]
basepython = python2.7
deps = Django>=1.4,<1.4.99
deps =
Django>=1.4,<1.4.99
python-memcached>=1.57

# python 2.6

[testenv:py26-1.6]
basepython = python2.6
deps = Django>=1.6,<1.6.99
deps =
Django>=1.6,<1.6.99
python-memcached>=1.57

[testenv:py26-1.5]
basepython = python2.6
deps = Django>=1.5,<1.5.99
deps =
Django>=1.5,<1.5.99
python-memcached>=1.57

[testenv:py26-1.4]
basepython = python2.6
deps = Django>=1.4,<1.4.99
deps =
Django>=1.4,<1.4.99
python-memcached>=1.57

0 comments on commit fa3f0af

Please sign in to comment.