Merge lp:~greglange/swift/lp682749 into lp:~hudson-openstack/swift/trunk
- lp682749
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
clayg | Approve | ||
gholt (community) | Approve | ||
Review via email: mp+42326@code.launchpad.net |
Commit message
Description of the change
Improved caching of results in the proxy server's account_info() and container_info() methods.
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_
Cause then instead of calling:
self.check_
you would get to say:
self.check_
:)
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.
clayg (clay-gerrard) wrote : | # |
The 507 thing probably doesn't need to be done - writes use handoffs, reads generally shouldn't. Good call.
Preview Diff
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 |
I'm good with this. I'll wait until Monday to mark it Approved in case anybody else has anything.