Merge lp:~greglange/swift/lp682749 into lp:~hudson-openstack/swift/trunk

Proposed by Greg Lange
Status: Merged
Approved by: clayg
Approved revision: 136
Merged at revision: 142
Proposed branch: lp:~greglange/swift/lp682749
Merge into: lp:~hudson-openstack/swift/trunk
Diff against target: 346 lines (+172/-67)
2 files modified
swift/proxy/server.py (+26/-20)
test/unit/proxy/test_server.py (+146/-47)
To merge this branch: bzr merge lp:~greglange/swift/lp682749
Reviewer Review Type Date Requested Status
clayg Approve
gholt (community) Approve
Review via email: mp+42326@code.launchpad.net

Description of the change

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

To post a comment you must log in.
Revision history for this message
gholt (gholt) wrote :

I'm good with this. I'll wait until Monday to mark it Approved in case anybody else has anything.

review: Approve
Revision history for this message
clayg (clay-gerrard) wrote :

on 507 shouldn't we attempts_left += 1 in case of handoffs?

Other than that I'm good with this too.

I really like these tests, nice work glange.

Random thought of the day, this might be wonky, but I think it be kinda cool if in check_account_info_return you did "if is_none is None"

Cause then instead of calling:
self.check_account_info_return(partition, nodes, True)
you would get to say:
self.check_account_info_return(partition, nodes, None)

:)

Revision history for this message
Greg Lange (greglange) wrote :

I think we should merge this without the 507 thing or the unit test changes (I like those how they are anyway = ).

If the 507 thing needs to be done, we should do that in a separate bug.

That's outside of what this bug is about.

Revision history for this message
clayg (clay-gerrard) wrote :

The 507 thing probably doesn't need to be done - writes use handoffs, reads generally shouldn't. Good call.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'swift/proxy/server.py'
--- swift/proxy/server.py 2010-11-30 23:00:12 +0000
+++ swift/proxy/server.py 2010-12-01 00:06:28 +0000
@@ -88,10 +88,11 @@
88 return func(*a, **kw)88 return func(*a, **kw)
89 return wrapped89 return wrapped
9090
91def get_account_memcache_key(account):
92 return 'account/%s' % account
9193
92def get_container_memcache_key(account, container):94def get_container_memcache_key(account, container):
93 path = '/%s/%s' % (account, container)95 return 'container/%s/%s' % (account, container)
94 return 'container%s' % path
9596
9697
97class Controller(object):98class Controller(object):
@@ -176,13 +177,17 @@
176 if it does not exist177 if it does not exist
177 """178 """
178 partition, nodes = self.app.account_ring.get_nodes(account)179 partition, nodes = self.app.account_ring.get_nodes(account)
179 path = '/%s' % account
180 cache_key = 'account%s' % path
181 # 0 = no responses, 200 = found, 404 = not found, -1 = mixed responses180 # 0 = no responses, 200 = found, 404 = not found, -1 = mixed responses
182 if self.app.memcache and self.app.memcache.get(cache_key) == 200:181 if self.app.memcache:
183 return partition, nodes182 cache_key = get_account_memcache_key(account)
183 result_code = self.app.memcache.get(cache_key)
184 if result_code == 200:
185 return partition, nodes
186 elif result_code == 404:
187 return None, None
184 result_code = 0188 result_code = 0
185 attempts_left = self.app.account_ring.replica_count189 attempts_left = self.app.account_ring.replica_count
190 path = '/%s' % account
186 headers = {'x-cf-trans-id': self.trans_id}191 headers = {'x-cf-trans-id': self.trans_id}
187 for node in self.iter_nodes(partition, nodes, self.app.account_ring):192 for node in self.iter_nodes(partition, nodes, self.app.account_ring):
188 if self.error_limited(node):193 if self.error_limited(node):
@@ -213,16 +218,16 @@
213 except:218 except:
214 self.exception_occurred(node, 'Account',219 self.exception_occurred(node, 'Account',
215 'Trying to get account info for %s' % path)220 'Trying to get account info for %s' % path)
216 if result_code == 200:221 if self.app.memcache and result_code in (200, 404):
217 cache_timeout = self.app.recheck_account_existence222 if result_code == 200:
218 else:223 cache_timeout = self.app.recheck_account_existence
219 cache_timeout = self.app.recheck_account_existence * 0.1224 else:
220 if self.app.memcache:225 cache_timeout = self.app.recheck_account_existence * 0.1
221 self.app.memcache.set(cache_key, result_code,226 self.app.memcache.set(cache_key, result_code,
222 timeout=cache_timeout)227 timeout=cache_timeout)
223 if result_code == 200:228 if result_code == 200:
224 return partition, nodes229 return partition, nodes
225 return (None, None)230 return None, None
226231
227 def container_info(self, account, container):232 def container_info(self, account, container):
228 """233 """
@@ -239,7 +244,6 @@
239 partition, nodes = self.app.container_ring.get_nodes(244 partition, nodes = self.app.container_ring.get_nodes(
240 account, container)245 account, container)
241 path = '/%s/%s' % (account, container)246 path = '/%s/%s' % (account, container)
242 cache_key = None
243 if self.app.memcache:247 if self.app.memcache:
244 cache_key = get_container_memcache_key(account, container)248 cache_key = get_container_memcache_key(account, container)
245 cache_value = self.app.memcache.get(cache_key)249 cache_value = self.app.memcache.get(cache_key)
@@ -249,8 +253,10 @@
249 write_acl = cache_value['write_acl']253 write_acl = cache_value['write_acl']
250 if status == 200:254 if status == 200:
251 return partition, nodes, read_acl, write_acl255 return partition, nodes, read_acl, write_acl
256 elif status == 404:
257 return None, None, None, None
252 if not self.account_info(account)[1]:258 if not self.account_info(account)[1]:
253 return (None, None, None, None)259 return None, None, None, None
254 result_code = 0260 result_code = 0
255 read_acl = None261 read_acl = None
256 write_acl = None262 write_acl = None
@@ -290,11 +296,11 @@
290 except:296 except:
291 self.exception_occurred(node, 'Container',297 self.exception_occurred(node, 'Container',
292 'Trying to get container info for %s' % path)298 'Trying to get container info for %s' % path)
293 if result_code == 200:299 if self.app.memcache and result_code in (200, 404):
294 cache_timeout = self.app.recheck_container_existence300 if result_code == 200:
295 else:301 cache_timeout = self.app.recheck_container_existence
296 cache_timeout = self.app.recheck_container_existence * 0.1302 else:
297 if cache_key and self.app.memcache:303 cache_timeout = self.app.recheck_container_existence * 0.1
298 self.app.memcache.set(cache_key,304 self.app.memcache.set(cache_key,
299 {'status': result_code,305 {'status': result_code,
300 'read_acl': read_acl,306 'read_acl': read_acl,
@@ -303,7 +309,7 @@
303 timeout=cache_timeout)309 timeout=cache_timeout)
304 if result_code == 200:310 if result_code == 200:
305 return partition, nodes, read_acl, write_acl311 return partition, nodes, read_acl, write_acl
306 return (None, None, None, None)312 return None, None, None, None
307313
308 def iter_nodes(self, partition, nodes, ring):314 def iter_nodes(self, partition, nodes, ring):
309 """315 """
310316
=== modified file 'test/unit/proxy/test_server.py'
--- test/unit/proxy/test_server.py 2010-11-30 16:01:05 +0000
+++ test/unit/proxy/test_server.py 2010-12-01 00:06:28 +0000
@@ -88,6 +88,8 @@
88 pass88 pass
89 if 'slow' in kwargs:89 if 'slow' in kwargs:
90 headers['content-length'] = '4'90 headers['content-length'] = '4'
91 if 'headers' in kwargs:
92 headers.update(kwargs['headers'])
91 return headers.items()93 return headers.items()
9294
93 def read(self, amt=None):95 def read(self, amt=None):
@@ -164,6 +166,9 @@
164 def get(self, key):166 def get(self, key):
165 return self.store.get(key)167 return self.store.get(key)
166168
169 def keys(self):
170 return self.store.keys()
171
167 def set(self, key, value, timeout=0):172 def set(self, key, value, timeout=0):
168 self.store[key] = value173 self.store[key] = value
169 return True174 return True
@@ -201,10 +206,12 @@
201@contextmanager206@contextmanager
202def save_globals():207def save_globals():
203 orig_http_connect = getattr(proxy_server, 'http_connect', None)208 orig_http_connect = getattr(proxy_server, 'http_connect', None)
209 orig_account_info = getattr(proxy_server.Controller, 'account_info', None)
204 try:210 try:
205 yield True211 yield True
206 finally:212 finally:
207 proxy_server.http_connect = orig_http_connect213 proxy_server.http_connect = orig_http_connect
214 proxy_server.Controller.account_info = orig_account_info
208215
209# tests216# tests
210217
@@ -212,63 +219,155 @@
212219
213 def setUp(self):220 def setUp(self):
214 self.account_ring = FakeRing()221 self.account_ring = FakeRing()
222 self.container_ring = FakeRing()
223 self.memcache = FakeMemcache()
215224
216 app = proxy_server.Application(None, FakeMemcache(),225 app = proxy_server.Application(None, self.memcache,
217 account_ring=self.account_ring, container_ring=FakeRing(),226 account_ring=self.account_ring,
227 container_ring=self.container_ring,
218 object_ring=FakeRing())228 object_ring=FakeRing())
219 self.controller = proxy_server.Controller(app)229 self.controller = proxy_server.Controller(app)
220230
221 def check_account_info_return(self, account, partition, nodes):231 self.account = 'some_account'
222 p, n = self.account_ring.get_nodes(account)232 self.container = 'some_container'
233 self.read_acl = 'read_acl'
234 self.write_acl = 'write_acl'
235
236 def check_account_info_return(self, partition, nodes, is_none=False):
237 if is_none:
238 p, n = None, None
239 else:
240 p, n = self.account_ring.get_nodes(self.account)
223 self.assertEqual(p, partition)241 self.assertEqual(p, partition)
224 self.assertEqual(n, nodes)242 self.assertEqual(n, nodes)
225243
226 def test_account_info_404_200(self):244 # tests if 200 is cached and used
227 account = 'test_account_info_404_200'245 def test_account_info_200(self):
228
229 with save_globals():246 with save_globals():
230 proxy_server.http_connect = fake_http_connect(404, 404, 404)
231 partition, nodes = self.controller.account_info(account)
232 self.assertEqual(partition, None)
233 self.assertEqual(nodes, None)
234
235 proxy_server.http_connect = fake_http_connect(200)247 proxy_server.http_connect = fake_http_connect(200)
236 partition, nodes = self.controller.account_info(account)248 partition, nodes = self.controller.account_info(self.account)
237 self.check_account_info_return(account, partition, nodes)249 self.check_account_info_return(partition, nodes)
238250
251 cache_key = proxy_server.get_account_memcache_key(self.account)
252 self.assertEquals(200, self.memcache.get(cache_key))
253
254 proxy_server.http_connect = fake_http_connect()
255 partition, nodes = self.controller.account_info(self.account)
256 self.check_account_info_return(partition, nodes)
257
258 # tests if 404 is cached and used
239 def test_account_info_404(self):259 def test_account_info_404(self):
240 account = 'test_account_info_404'260 with save_globals():
241261 proxy_server.http_connect = fake_http_connect(404, 404, 404)
242 with save_globals():262 partition, nodes = self.controller.account_info(self.account)
243 proxy_server.http_connect = fake_http_connect(404, 404, 404)263 self.check_account_info_return(partition, nodes, True)
244 partition, nodes = self.controller.account_info(account)264
245 self.assertEqual(partition, None)265 cache_key = proxy_server.get_account_memcache_key(self.account)
246 self.assertEqual(nodes, None)266 self.assertEquals(404, self.memcache.get(cache_key))
247267
248 proxy_server.http_connect = fake_http_connect(404, 404, 404)268 proxy_server.http_connect = fake_http_connect()
249 partition, nodes = self.controller.account_info(account)269 partition, nodes = self.controller.account_info(self.account)
250 self.assertEqual(partition, None)270 self.check_account_info_return(partition, nodes, True)
251 self.assertEqual(nodes, None)271
252272 # tests if some http status codes are not cached
253 def test_account_info_200(self):273 def test_account_info_no_cache(self):
254 account = 'test_account_info_200'274 def test(*status_list):
255275 proxy_server.http_connect = fake_http_connect(*status_list)
256 with save_globals():276 partition, nodes = self.controller.account_info(self.account)
257 proxy_server.http_connect = fake_http_connect(200)277 self.assertEqual(len(self.memcache.keys()), 0)
258 partition, nodes = self.controller.account_info(account)278 self.check_account_info_return(partition, nodes, True)
259 self.check_account_info_return(account, partition, nodes)279
260280 with save_globals():
261 def test_account_info_200_200(self):281 test(503, 404, 404)
262 account = 'test_account_info_200_200'282 test(404, 404, 503)
263283 test(404, 507, 503)
264 with save_globals():284 test(503, 503, 503)
265 proxy_server.http_connect = fake_http_connect(200)285
266 partition, nodes = self.controller.account_info(account)286 def check_container_info_return(self, ret, is_none=False):
267 self.check_account_info_return(account, partition, nodes)287 if is_none:
268288 partition, nodes, read_acl, write_acl = None, None, None, None
269 proxy_server.http_connect = fake_http_connect(200)289 else:
270 partition, nodes = self.controller.account_info(account)290 partition, nodes = self.container_ring.get_nodes(self.account,
271 self.check_account_info_return(account, partition, nodes)291 self.container)
292 read_acl, write_acl = self.read_acl, self.write_acl
293 self.assertEqual(partition, ret[0])
294 self.assertEqual(nodes, ret[1])
295 self.assertEqual(read_acl, ret[2])
296 self.assertEqual(write_acl, ret[3])
297
298 def test_container_info_invalid_account(self):
299 def account_info(self, account):
300 return None, None
301
302 with save_globals():
303 proxy_server.Controller.account_info = account_info
304 ret = self.controller.container_info(self.account,
305 self.container)
306 self.check_container_info_return(ret, True)
307
308 # tests if 200 is cached and used
309 def test_container_info_200(self):
310 def account_info(self, account):
311 return True, True
312
313 with save_globals():
314 headers = {'x-container-read': self.read_acl,
315 'x-container-write': self.write_acl}
316 proxy_server.Controller.account_info = account_info
317 proxy_server.http_connect = fake_http_connect(200,
318 headers=headers)
319 ret = self.controller.container_info(self.account,
320 self.container)
321 self.check_container_info_return(ret)
322
323 cache_key = proxy_server.get_container_memcache_key(self.account,
324 self.container)
325 cache_value = self.memcache.get(cache_key)
326 self.assertEquals(dict, type(cache_value))
327 self.assertEquals(200, cache_value.get('status'))
328
329 proxy_server.http_connect = fake_http_connect()
330 ret = self.controller.container_info(self.account,
331 self.container)
332 self.check_container_info_return(ret)
333
334 # tests if 404 is cached and used
335 def test_container_info_404(self):
336 def account_info(self, account):
337 return True, True
338
339 with save_globals():
340 proxy_server.Controller.account_info = account_info
341 proxy_server.http_connect = fake_http_connect(404, 404, 404)
342 ret = self.controller.container_info(self.account,
343 self.container)
344 self.check_container_info_return(ret, True)
345
346 cache_key = proxy_server.get_container_memcache_key(self.account,
347 self.container)
348 cache_value = self.memcache.get(cache_key)
349 self.assertEquals(dict, type(cache_value))
350 self.assertEquals(404, cache_value.get('status'))
351
352 proxy_server.http_connect = fake_http_connect()
353 ret = self.controller.container_info(self.account,
354 self.container)
355 self.check_container_info_return(ret, True)
356
357 # tests if some http status codes are not cached
358 def test_container_info_no_cache(self):
359 def test(*status_list):
360 proxy_server.http_connect = fake_http_connect(*status_list)
361 ret = self.controller.container_info(self.account,
362 self.container)
363 self.assertEqual(len(self.memcache.keys()), 0)
364 self.check_container_info_return(ret, True)
365
366 with save_globals():
367 test(503, 404, 404)
368 test(404, 404, 503)
369 test(404, 507, 503)
370 test(503, 503, 503)
272371
273class TestProxyServer(unittest.TestCase):372class TestProxyServer(unittest.TestCase):
274373