Improved caching of results in the proxy server's account_info() and container_info() methods.

This commit is contained in:
Greg Lange 2010-12-06 20:02:43 +00:00 committed by Tarmac
commit 84aa5f0fc8
2 changed files with 168 additions and 63 deletions

View File

@ -88,10 +88,11 @@ def delay_denial(func):
return func(*a, **kw)
return wrapped
def get_account_memcache_key(account):
return 'account/%s' % account
def get_container_memcache_key(account, container):
path = '/%s/%s' % (account, container)
return 'container%s' % path
return 'container/%s/%s' % (account, container)
class Controller(object):
@ -176,13 +177,17 @@ class Controller(object):
if it does not exist
"""
partition, nodes = self.app.account_ring.get_nodes(account)
path = '/%s' % account
cache_key = 'account%s' % path
# 0 = no responses, 200 = found, 404 = not found, -1 = mixed responses
if self.app.memcache and self.app.memcache.get(cache_key) == 200:
return partition, nodes
if self.app.memcache:
cache_key = get_account_memcache_key(account)
result_code = self.app.memcache.get(cache_key)
if result_code == 200:
return partition, nodes
elif result_code == 404:
return None, None
result_code = 0
attempts_left = self.app.account_ring.replica_count
path = '/%s' % account
headers = {'x-cf-trans-id': self.trans_id}
for node in self.iter_nodes(partition, nodes, self.app.account_ring):
if self.error_limited(node):
@ -213,16 +218,16 @@ class Controller(object):
except:
self.exception_occurred(node, 'Account',
'Trying to get account info for %s' % path)
if result_code == 200:
cache_timeout = self.app.recheck_account_existence
else:
cache_timeout = self.app.recheck_account_existence * 0.1
if self.app.memcache:
if self.app.memcache and result_code in (200, 404):
if result_code == 200:
cache_timeout = self.app.recheck_account_existence
else:
cache_timeout = self.app.recheck_account_existence * 0.1
self.app.memcache.set(cache_key, result_code,
timeout=cache_timeout)
if result_code == 200:
return partition, nodes
return (None, None)
return None, None
def container_info(self, account, container):
"""
@ -239,7 +244,6 @@ class Controller(object):
partition, nodes = self.app.container_ring.get_nodes(
account, container)
path = '/%s/%s' % (account, container)
cache_key = None
if self.app.memcache:
cache_key = get_container_memcache_key(account, container)
cache_value = self.app.memcache.get(cache_key)
@ -249,8 +253,10 @@ class Controller(object):
write_acl = cache_value['write_acl']
if status == 200:
return partition, nodes, read_acl, write_acl
elif status == 404:
return None, None, None, None
if not self.account_info(account)[1]:
return (None, None, None, None)
return None, None, None, None
result_code = 0
read_acl = None
write_acl = None
@ -290,11 +296,11 @@ class Controller(object):
except:
self.exception_occurred(node, 'Container',
'Trying to get container info for %s' % path)
if result_code == 200:
cache_timeout = self.app.recheck_container_existence
else:
cache_timeout = self.app.recheck_container_existence * 0.1
if cache_key and self.app.memcache:
if self.app.memcache and result_code in (200, 404):
if result_code == 200:
cache_timeout = self.app.recheck_container_existence
else:
cache_timeout = self.app.recheck_container_existence * 0.1
self.app.memcache.set(cache_key,
{'status': result_code,
'read_acl': read_acl,
@ -303,7 +309,7 @@ class Controller(object):
timeout=cache_timeout)
if result_code == 200:
return partition, nodes, read_acl, write_acl
return (None, None, None, None)
return None, None, None, None
def iter_nodes(self, partition, nodes, ring):
"""

View File

@ -88,6 +88,8 @@ def fake_http_connect(*code_iter, **kwargs):
pass
if 'slow' in kwargs:
headers['content-length'] = '4'
if 'headers' in kwargs:
headers.update(kwargs['headers'])
return headers.items()
def read(self, amt=None):
@ -164,6 +166,9 @@ class FakeMemcache(object):
def get(self, key):
return self.store.get(key)
def keys(self):
return self.store.keys()
def set(self, key, value, timeout=0):
self.store[key] = value
return True
@ -201,10 +206,12 @@ class NullLoggingHandler(logging.Handler):
@contextmanager
def save_globals():
orig_http_connect = getattr(proxy_server, 'http_connect', None)
orig_account_info = getattr(proxy_server.Controller, 'account_info', None)
try:
yield True
finally:
proxy_server.http_connect = orig_http_connect
proxy_server.Controller.account_info = orig_account_info
# tests
@ -212,63 +219,155 @@ class TestController(unittest.TestCase):
def setUp(self):
self.account_ring = FakeRing()
self.container_ring = FakeRing()
self.memcache = FakeMemcache()
app = proxy_server.Application(None, FakeMemcache(),
account_ring=self.account_ring, container_ring=FakeRing(),
app = proxy_server.Application(None, self.memcache,
account_ring=self.account_ring,
container_ring=self.container_ring,
object_ring=FakeRing())
self.controller = proxy_server.Controller(app)
def check_account_info_return(self, account, partition, nodes):
p, n = self.account_ring.get_nodes(account)
self.account = 'some_account'
self.container = 'some_container'
self.read_acl = 'read_acl'
self.write_acl = 'write_acl'
def check_account_info_return(self, partition, nodes, is_none=False):
if is_none:
p, n = None, None
else:
p, n = self.account_ring.get_nodes(self.account)
self.assertEqual(p, partition)
self.assertEqual(n, nodes)
def test_account_info_404_200(self):
account = 'test_account_info_404_200'
with save_globals():
proxy_server.http_connect = fake_http_connect(404, 404, 404)
partition, nodes = self.controller.account_info(account)
self.assertEqual(partition, None)
self.assertEqual(nodes, None)
proxy_server.http_connect = fake_http_connect(200)
partition, nodes = self.controller.account_info(account)
self.check_account_info_return(account, partition, nodes)
def test_account_info_404(self):
account = 'test_account_info_404'
with save_globals():
proxy_server.http_connect = fake_http_connect(404, 404, 404)
partition, nodes = self.controller.account_info(account)
self.assertEqual(partition, None)
self.assertEqual(nodes, None)
proxy_server.http_connect = fake_http_connect(404, 404, 404)
partition, nodes = self.controller.account_info(account)
self.assertEqual(partition, None)
self.assertEqual(nodes, None)
# tests if 200 is cached and used
def test_account_info_200(self):
account = 'test_account_info_200'
with save_globals():
proxy_server.http_connect = fake_http_connect(200)
partition, nodes = self.controller.account_info(account)
self.check_account_info_return(account, partition, nodes)
partition, nodes = self.controller.account_info(self.account)
self.check_account_info_return(partition, nodes)
def test_account_info_200_200(self):
account = 'test_account_info_200_200'
cache_key = proxy_server.get_account_memcache_key(self.account)
self.assertEquals(200, self.memcache.get(cache_key))
proxy_server.http_connect = fake_http_connect()
partition, nodes = self.controller.account_info(self.account)
self.check_account_info_return(partition, nodes)
# tests if 404 is cached and used
def test_account_info_404(self):
with save_globals():
proxy_server.http_connect = fake_http_connect(404, 404, 404)
partition, nodes = self.controller.account_info(self.account)
self.check_account_info_return(partition, nodes, True)
cache_key = proxy_server.get_account_memcache_key(self.account)
self.assertEquals(404, self.memcache.get(cache_key))
proxy_server.http_connect = fake_http_connect()
partition, nodes = self.controller.account_info(self.account)
self.check_account_info_return(partition, nodes, True)
# tests if some http status codes are not cached
def test_account_info_no_cache(self):
def test(*status_list):
proxy_server.http_connect = fake_http_connect(*status_list)
partition, nodes = self.controller.account_info(self.account)
self.assertEqual(len(self.memcache.keys()), 0)
self.check_account_info_return(partition, nodes, True)
with save_globals():
proxy_server.http_connect = fake_http_connect(200)
partition, nodes = self.controller.account_info(account)
self.check_account_info_return(account, partition, nodes)
test(503, 404, 404)
test(404, 404, 503)
test(404, 507, 503)
test(503, 503, 503)
proxy_server.http_connect = fake_http_connect(200)
partition, nodes = self.controller.account_info(account)
self.check_account_info_return(account, partition, nodes)
def check_container_info_return(self, ret, is_none=False):
if is_none:
partition, nodes, read_acl, write_acl = None, None, None, None
else:
partition, nodes = self.container_ring.get_nodes(self.account,
self.container)
read_acl, write_acl = self.read_acl, self.write_acl
self.assertEqual(partition, ret[0])
self.assertEqual(nodes, ret[1])
self.assertEqual(read_acl, ret[2])
self.assertEqual(write_acl, ret[3])
def test_container_info_invalid_account(self):
def account_info(self, account):
return None, None
with save_globals():
proxy_server.Controller.account_info = account_info
ret = self.controller.container_info(self.account,
self.container)
self.check_container_info_return(ret, True)
# tests if 200 is cached and used
def test_container_info_200(self):
def account_info(self, account):
return True, True
with save_globals():
headers = {'x-container-read': self.read_acl,
'x-container-write': self.write_acl}
proxy_server.Controller.account_info = account_info
proxy_server.http_connect = fake_http_connect(200,
headers=headers)
ret = self.controller.container_info(self.account,
self.container)
self.check_container_info_return(ret)
cache_key = proxy_server.get_container_memcache_key(self.account,
self.container)
cache_value = self.memcache.get(cache_key)
self.assertEquals(dict, type(cache_value))
self.assertEquals(200, cache_value.get('status'))
proxy_server.http_connect = fake_http_connect()
ret = self.controller.container_info(self.account,
self.container)
self.check_container_info_return(ret)
# tests if 404 is cached and used
def test_container_info_404(self):
def account_info(self, account):
return True, True
with save_globals():
proxy_server.Controller.account_info = account_info
proxy_server.http_connect = fake_http_connect(404, 404, 404)
ret = self.controller.container_info(self.account,
self.container)
self.check_container_info_return(ret, True)
cache_key = proxy_server.get_container_memcache_key(self.account,
self.container)
cache_value = self.memcache.get(cache_key)
self.assertEquals(dict, type(cache_value))
self.assertEquals(404, cache_value.get('status'))
proxy_server.http_connect = fake_http_connect()
ret = self.controller.container_info(self.account,
self.container)
self.check_container_info_return(ret, True)
# tests if some http status codes are not cached
def test_container_info_no_cache(self):
def test(*status_list):
proxy_server.http_connect = fake_http_connect(*status_list)
ret = self.controller.container_info(self.account,
self.container)
self.assertEqual(len(self.memcache.keys()), 0)
self.check_container_info_return(ret, True)
with save_globals():
test(503, 404, 404)
test(404, 404, 503)
test(404, 507, 503)
test(503, 503, 503)
class TestProxyServer(unittest.TestCase):