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
1=== modified file 'swift/proxy/server.py'
2--- swift/proxy/server.py 2010-11-30 23:00:12 +0000
3+++ swift/proxy/server.py 2010-12-01 00:06:28 +0000
4@@ -88,10 +88,11 @@
5 return func(*a, **kw)
6 return wrapped
7
8+def get_account_memcache_key(account):
9+ return 'account/%s' % account
10
11 def get_container_memcache_key(account, container):
12- path = '/%s/%s' % (account, container)
13- return 'container%s' % path
14+ return 'container/%s/%s' % (account, container)
15
16
17 class Controller(object):
18@@ -176,13 +177,17 @@
19 if it does not exist
20 """
21 partition, nodes = self.app.account_ring.get_nodes(account)
22- path = '/%s' % account
23- cache_key = 'account%s' % path
24 # 0 = no responses, 200 = found, 404 = not found, -1 = mixed responses
25- if self.app.memcache and self.app.memcache.get(cache_key) == 200:
26- return partition, nodes
27+ if self.app.memcache:
28+ cache_key = get_account_memcache_key(account)
29+ result_code = self.app.memcache.get(cache_key)
30+ if result_code == 200:
31+ return partition, nodes
32+ elif result_code == 404:
33+ return None, None
34 result_code = 0
35 attempts_left = self.app.account_ring.replica_count
36+ path = '/%s' % account
37 headers = {'x-cf-trans-id': self.trans_id}
38 for node in self.iter_nodes(partition, nodes, self.app.account_ring):
39 if self.error_limited(node):
40@@ -213,16 +218,16 @@
41 except:
42 self.exception_occurred(node, 'Account',
43 'Trying to get account info for %s' % path)
44- if result_code == 200:
45- cache_timeout = self.app.recheck_account_existence
46- else:
47- cache_timeout = self.app.recheck_account_existence * 0.1
48- if self.app.memcache:
49+ if self.app.memcache and result_code in (200, 404):
50+ if result_code == 200:
51+ cache_timeout = self.app.recheck_account_existence
52+ else:
53+ cache_timeout = self.app.recheck_account_existence * 0.1
54 self.app.memcache.set(cache_key, result_code,
55 timeout=cache_timeout)
56 if result_code == 200:
57 return partition, nodes
58- return (None, None)
59+ return None, None
60
61 def container_info(self, account, container):
62 """
63@@ -239,7 +244,6 @@
64 partition, nodes = self.app.container_ring.get_nodes(
65 account, container)
66 path = '/%s/%s' % (account, container)
67- cache_key = None
68 if self.app.memcache:
69 cache_key = get_container_memcache_key(account, container)
70 cache_value = self.app.memcache.get(cache_key)
71@@ -249,8 +253,10 @@
72 write_acl = cache_value['write_acl']
73 if status == 200:
74 return partition, nodes, read_acl, write_acl
75+ elif status == 404:
76+ return None, None, None, None
77 if not self.account_info(account)[1]:
78- return (None, None, None, None)
79+ return None, None, None, None
80 result_code = 0
81 read_acl = None
82 write_acl = None
83@@ -290,11 +296,11 @@
84 except:
85 self.exception_occurred(node, 'Container',
86 'Trying to get container info for %s' % path)
87- if result_code == 200:
88- cache_timeout = self.app.recheck_container_existence
89- else:
90- cache_timeout = self.app.recheck_container_existence * 0.1
91- if cache_key and self.app.memcache:
92+ if self.app.memcache and result_code in (200, 404):
93+ if result_code == 200:
94+ cache_timeout = self.app.recheck_container_existence
95+ else:
96+ cache_timeout = self.app.recheck_container_existence * 0.1
97 self.app.memcache.set(cache_key,
98 {'status': result_code,
99 'read_acl': read_acl,
100@@ -303,7 +309,7 @@
101 timeout=cache_timeout)
102 if result_code == 200:
103 return partition, nodes, read_acl, write_acl
104- return (None, None, None, None)
105+ return None, None, None, None
106
107 def iter_nodes(self, partition, nodes, ring):
108 """
109
110=== modified file 'test/unit/proxy/test_server.py'
111--- test/unit/proxy/test_server.py 2010-11-30 16:01:05 +0000
112+++ test/unit/proxy/test_server.py 2010-12-01 00:06:28 +0000
113@@ -88,6 +88,8 @@
114 pass
115 if 'slow' in kwargs:
116 headers['content-length'] = '4'
117+ if 'headers' in kwargs:
118+ headers.update(kwargs['headers'])
119 return headers.items()
120
121 def read(self, amt=None):
122@@ -164,6 +166,9 @@
123 def get(self, key):
124 return self.store.get(key)
125
126+ def keys(self):
127+ return self.store.keys()
128+
129 def set(self, key, value, timeout=0):
130 self.store[key] = value
131 return True
132@@ -201,10 +206,12 @@
133 @contextmanager
134 def save_globals():
135 orig_http_connect = getattr(proxy_server, 'http_connect', None)
136+ orig_account_info = getattr(proxy_server.Controller, 'account_info', None)
137 try:
138 yield True
139 finally:
140 proxy_server.http_connect = orig_http_connect
141+ proxy_server.Controller.account_info = orig_account_info
142
143 # tests
144
145@@ -212,63 +219,155 @@
146
147 def setUp(self):
148 self.account_ring = FakeRing()
149+ self.container_ring = FakeRing()
150+ self.memcache = FakeMemcache()
151
152- app = proxy_server.Application(None, FakeMemcache(),
153- account_ring=self.account_ring, container_ring=FakeRing(),
154+ app = proxy_server.Application(None, self.memcache,
155+ account_ring=self.account_ring,
156+ container_ring=self.container_ring,
157 object_ring=FakeRing())
158 self.controller = proxy_server.Controller(app)
159
160- def check_account_info_return(self, account, partition, nodes):
161- p, n = self.account_ring.get_nodes(account)
162+ self.account = 'some_account'
163+ self.container = 'some_container'
164+ self.read_acl = 'read_acl'
165+ self.write_acl = 'write_acl'
166+
167+ def check_account_info_return(self, partition, nodes, is_none=False):
168+ if is_none:
169+ p, n = None, None
170+ else:
171+ p, n = self.account_ring.get_nodes(self.account)
172 self.assertEqual(p, partition)
173 self.assertEqual(n, nodes)
174
175- def test_account_info_404_200(self):
176- account = 'test_account_info_404_200'
177-
178+ # tests if 200 is cached and used
179+ def test_account_info_200(self):
180 with save_globals():
181- proxy_server.http_connect = fake_http_connect(404, 404, 404)
182- partition, nodes = self.controller.account_info(account)
183- self.assertEqual(partition, None)
184- self.assertEqual(nodes, None)
185-
186 proxy_server.http_connect = fake_http_connect(200)
187- partition, nodes = self.controller.account_info(account)
188- self.check_account_info_return(account, partition, nodes)
189-
190+ partition, nodes = self.controller.account_info(self.account)
191+ self.check_account_info_return(partition, nodes)
192+
193+ cache_key = proxy_server.get_account_memcache_key(self.account)
194+ self.assertEquals(200, self.memcache.get(cache_key))
195+
196+ proxy_server.http_connect = fake_http_connect()
197+ partition, nodes = self.controller.account_info(self.account)
198+ self.check_account_info_return(partition, nodes)
199+
200+ # tests if 404 is cached and used
201 def test_account_info_404(self):
202- account = 'test_account_info_404'
203-
204- with save_globals():
205- proxy_server.http_connect = fake_http_connect(404, 404, 404)
206- partition, nodes = self.controller.account_info(account)
207- self.assertEqual(partition, None)
208- self.assertEqual(nodes, None)
209-
210- proxy_server.http_connect = fake_http_connect(404, 404, 404)
211- partition, nodes = self.controller.account_info(account)
212- self.assertEqual(partition, None)
213- self.assertEqual(nodes, None)
214-
215- def test_account_info_200(self):
216- account = 'test_account_info_200'
217-
218- with save_globals():
219- proxy_server.http_connect = fake_http_connect(200)
220- partition, nodes = self.controller.account_info(account)
221- self.check_account_info_return(account, partition, nodes)
222-
223- def test_account_info_200_200(self):
224- account = 'test_account_info_200_200'
225-
226- with save_globals():
227- proxy_server.http_connect = fake_http_connect(200)
228- partition, nodes = self.controller.account_info(account)
229- self.check_account_info_return(account, partition, nodes)
230-
231- proxy_server.http_connect = fake_http_connect(200)
232- partition, nodes = self.controller.account_info(account)
233- self.check_account_info_return(account, partition, nodes)
234+ with save_globals():
235+ proxy_server.http_connect = fake_http_connect(404, 404, 404)
236+ partition, nodes = self.controller.account_info(self.account)
237+ self.check_account_info_return(partition, nodes, True)
238+
239+ cache_key = proxy_server.get_account_memcache_key(self.account)
240+ self.assertEquals(404, self.memcache.get(cache_key))
241+
242+ proxy_server.http_connect = fake_http_connect()
243+ partition, nodes = self.controller.account_info(self.account)
244+ self.check_account_info_return(partition, nodes, True)
245+
246+ # tests if some http status codes are not cached
247+ def test_account_info_no_cache(self):
248+ def test(*status_list):
249+ proxy_server.http_connect = fake_http_connect(*status_list)
250+ partition, nodes = self.controller.account_info(self.account)
251+ self.assertEqual(len(self.memcache.keys()), 0)
252+ self.check_account_info_return(partition, nodes, True)
253+
254+ with save_globals():
255+ test(503, 404, 404)
256+ test(404, 404, 503)
257+ test(404, 507, 503)
258+ test(503, 503, 503)
259+
260+ def check_container_info_return(self, ret, is_none=False):
261+ if is_none:
262+ partition, nodes, read_acl, write_acl = None, None, None, None
263+ else:
264+ partition, nodes = self.container_ring.get_nodes(self.account,
265+ self.container)
266+ read_acl, write_acl = self.read_acl, self.write_acl
267+ self.assertEqual(partition, ret[0])
268+ self.assertEqual(nodes, ret[1])
269+ self.assertEqual(read_acl, ret[2])
270+ self.assertEqual(write_acl, ret[3])
271+
272+ def test_container_info_invalid_account(self):
273+ def account_info(self, account):
274+ return None, None
275+
276+ with save_globals():
277+ proxy_server.Controller.account_info = account_info
278+ ret = self.controller.container_info(self.account,
279+ self.container)
280+ self.check_container_info_return(ret, True)
281+
282+ # tests if 200 is cached and used
283+ def test_container_info_200(self):
284+ def account_info(self, account):
285+ return True, True
286+
287+ with save_globals():
288+ headers = {'x-container-read': self.read_acl,
289+ 'x-container-write': self.write_acl}
290+ proxy_server.Controller.account_info = account_info
291+ proxy_server.http_connect = fake_http_connect(200,
292+ headers=headers)
293+ ret = self.controller.container_info(self.account,
294+ self.container)
295+ self.check_container_info_return(ret)
296+
297+ cache_key = proxy_server.get_container_memcache_key(self.account,
298+ self.container)
299+ cache_value = self.memcache.get(cache_key)
300+ self.assertEquals(dict, type(cache_value))
301+ self.assertEquals(200, cache_value.get('status'))
302+
303+ proxy_server.http_connect = fake_http_connect()
304+ ret = self.controller.container_info(self.account,
305+ self.container)
306+ self.check_container_info_return(ret)
307+
308+ # tests if 404 is cached and used
309+ def test_container_info_404(self):
310+ def account_info(self, account):
311+ return True, True
312+
313+ with save_globals():
314+ proxy_server.Controller.account_info = account_info
315+ proxy_server.http_connect = fake_http_connect(404, 404, 404)
316+ ret = self.controller.container_info(self.account,
317+ self.container)
318+ self.check_container_info_return(ret, True)
319+
320+ cache_key = proxy_server.get_container_memcache_key(self.account,
321+ self.container)
322+ cache_value = self.memcache.get(cache_key)
323+ self.assertEquals(dict, type(cache_value))
324+ self.assertEquals(404, cache_value.get('status'))
325+
326+ proxy_server.http_connect = fake_http_connect()
327+ ret = self.controller.container_info(self.account,
328+ self.container)
329+ self.check_container_info_return(ret, True)
330+
331+ # tests if some http status codes are not cached
332+ def test_container_info_no_cache(self):
333+ def test(*status_list):
334+ proxy_server.http_connect = fake_http_connect(*status_list)
335+ ret = self.controller.container_info(self.account,
336+ self.container)
337+ self.assertEqual(len(self.memcache.keys()), 0)
338+ self.check_container_info_return(ret, True)
339+
340+ with save_globals():
341+ test(503, 404, 404)
342+ test(404, 404, 503)
343+ test(404, 507, 503)
344+ test(503, 503, 503)
345
346 class TestProxyServer(unittest.TestCase):
347