Merge lp:~david-goetz/swift/ratelimit_changes into lp:~hudson-openstack/swift/trunk

Proposed by David Goetz
Status: Merged
Approved by: John Dickinson
Approved revision: 328
Merged at revision: 327
Proposed branch: lp:~david-goetz/swift/ratelimit_changes
Merge into: lp:~hudson-openstack/swift/trunk
Diff against target: 220 lines (+39/-73)
3 files modified
doc/source/ratelimit.rst (+5/-8)
swift/common/middleware/ratelimit.py (+6/-7)
test/unit/common/middleware/test_ratelimit.py (+28/-58)
To merge this branch: bzr merge lp:~david-goetz/swift/ratelimit_changes
Reviewer Review Type Date Requested Status
John Dickinson Approve
gholt (community) Approve
David Goetz (community) Needs Resubmitting
Review via email: mp+67382@code.launchpad.net

Description of the change

Changing ratelimiting so that it only limits PUTs/DELETEs.

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

I should've thought of this before, but a/c/o POST and COPY should also be in the same pool as PUT and DELETE.

324. By David Goetz

limit posts and copies

Revision history for this message
gholt (gholt) wrote :

That won't work exactly. The COPY is sourced from the url and writes to the Destination header.

I don't think it's worth the extra crap to pull the destination header, parse it, and then rate limit, but if you want... Otherwise, just a quick note there stating that COPYs go unlimited should be fine.

review: Needs Fixing
325. By David Goetz

limit PUT, DELETE, POSTs to /a/c and /a/c/o

326. By David Goetz

fixing what is limited

Revision history for this message
David Goetz (david-goetz) wrote :

fixing:
limit PUT/DELETE to /a/c
limit PUT/DELETE/POST to /a/c/o

review: Needs Resubmitting
327. By David Goetz

fixing unit test

328. By David Goetz

fix docs

Revision history for this message
gholt (gholt) :
review: Approve
Revision history for this message
John Dickinson (notmyname) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/source/ratelimit.rst'
2--- doc/source/ratelimit.rst 2011-01-21 01:05:44 +0000
3+++ doc/source/ratelimit.rst 2011-07-18 22:21:24 +0000
4@@ -35,20 +35,17 @@
5 faster than listed rate). A larger number
6 will result in larger spikes in rate but
7 better average accuracy.
8-account_ratelimit 0 If set, will limit all requests to
9- /account_name and PUTs to
10- /account_name/container_name. Number is in
11- requests per second
12+account_ratelimit 0 If set, will limit PUT and DELETE requests
13+ to /account_name/container_name.
14+ Number is in requests per second.
15 account_whitelist '' Comma separated lists of account names that
16 will not be rate limited.
17 account_blacklist '' Comma separated lists of account names that
18 will not be allowed. Returns a 497 response.
19 container_ratelimit_size '' When set with container_limit_x = r:
20 for containers of size x, limit requests
21- per second to r. Will limit GET and HEAD
22- requests to /account_name/container_name
23- and PUTs and DELETEs to
24- /account_name/container_name/object_name
25+ per second to r. Will limit PUT, DELETE,
26+ and POST requests to /a/c/o.
27 ======================== ========= ===========================================
28
29 The container rate limits are linearly interpolated from the values given. A
30
31=== modified file 'swift/common/middleware/ratelimit.py'
32--- swift/common/middleware/ratelimit.py 2011-02-23 19:44:36 +0000
33+++ swift/common/middleware/ratelimit.py 2011-07-18 22:21:24 +0000
34@@ -105,16 +105,15 @@
35 :param obj_name: object name from path
36 """
37 keys = []
38- if self.account_ratelimit and account_name and (
39- not (container_name or obj_name) or
40- (container_name and not obj_name and
41- req_method in ('PUT', 'DELETE'))):
42+ # COPYs are not limited
43+ if self.account_ratelimit and \
44+ account_name and container_name and not obj_name and \
45+ req_method in ('PUT', 'DELETE'):
46 keys.append(("ratelimit/%s" % account_name,
47 self.account_ratelimit))
48
49- if account_name and container_name and (
50- (not obj_name and req_method in ('GET', 'HEAD')) or
51- (obj_name and req_method in ('PUT', 'DELETE'))):
52+ if account_name and container_name and obj_name and \
53+ req_method in ('PUT', 'DELETE', 'POST'):
54 container_size = None
55 memcache_key = get_container_memcache_key(account_name,
56 container_name)
57
58=== modified file 'test/unit/common/middleware/test_ratelimit.py'
59--- test/unit/common/middleware/test_ratelimit.py 2011-06-16 18:09:15 +0000
60+++ test/unit/common/middleware/test_ratelimit.py 2011-07-18 22:21:24 +0000
61@@ -139,13 +139,16 @@
62
63 class TestRateLimit(unittest.TestCase):
64
65+ def _reset_time(self):
66+ global time_ticker
67+ time_ticker = 0
68+
69 def setUp(self):
70- global time_ticker
71- time_ticker = 0
72 self.was_sleep = eventlet.sleep
73 eventlet.sleep = mock_sleep
74 self.was_time = time.time
75 time.time = mock_time
76+ self._reset_time()
77
78 def tearDown(self):
79 eventlet.sleep = self.was_sleep
80@@ -186,31 +189,34 @@
81 logger=FakeLogger())
82 the_app.memcache_client = fake_memcache
83 self.assertEquals(len(the_app.get_ratelimitable_key_tuples(
84- 'GET', 'a', None, None)), 1)
85- self.assertEquals(len(the_app.get_ratelimitable_key_tuples(
86- 'POST', 'a', 'c', None)), 0)
87+ 'DELETE', 'a', None, None)), 0)
88 self.assertEquals(len(the_app.get_ratelimitable_key_tuples(
89 'PUT', 'a', 'c', None)), 1)
90 self.assertEquals(len(the_app.get_ratelimitable_key_tuples(
91- 'GET', 'a', 'c', None)), 1)
92+ 'DELETE', 'a', 'c', None)), 1)
93 self.assertEquals(len(the_app.get_ratelimitable_key_tuples(
94 'GET', 'a', 'c', 'o')), 0)
95 self.assertEquals(len(the_app.get_ratelimitable_key_tuples(
96 'PUT', 'a', 'c', 'o')), 1)
97
98- def test_ratelimit(self):
99+ def test_account_ratelimit(self):
100 current_rate = 5
101 num_calls = 50
102 conf_dict = {'account_ratelimit': current_rate}
103 self.test_ratelimit = ratelimit.filter_factory(conf_dict)(FakeApp())
104 ratelimit.http_connect = mock_http_connect(204)
105- req = Request.blank('/v/a')
106- req.environ['swift.cache'] = FakeMemcache()
107- make_app_call = lambda: self.test_ratelimit(req.environ,
108- start_response)
109- begin = time.time()
110- self._run(make_app_call, num_calls, current_rate)
111- self.assertEquals(round(time.time() - begin, 1), 9.8)
112+ for meth, exp_time in [('DELETE', 9.8), ('GET', 0),
113+ ('POST', 0), ('PUT', 9.8)]:
114+ req = Request.blank('/v/a%s/c' % meth)
115+ req.method = meth
116+ req.environ['swift.cache'] = FakeMemcache()
117+ make_app_call = lambda: self.test_ratelimit(req.environ,
118+ start_response)
119+ begin = time.time()
120+ self._run(make_app_call, num_calls, current_rate,
121+ check_time=bool(exp_time))
122+ self.assertEquals(round(time.time() - begin, 1), exp_time)
123+ self._reset_time()
124
125 def test_ratelimit_set_incr(self):
126 current_rate = 5
127@@ -218,7 +224,8 @@
128 conf_dict = {'account_ratelimit': current_rate}
129 self.test_ratelimit = ratelimit.filter_factory(conf_dict)(FakeApp())
130 ratelimit.http_connect = mock_http_connect(204)
131- req = Request.blank('/v/a')
132+ req = Request.blank('/v/a/c')
133+ req.method = 'PUT'
134 req.environ['swift.cache'] = FakeMemcache()
135 req.environ['swift.cache'].init_incr_return_neg = True
136 make_app_call = lambda: self.test_ratelimit(req.environ,
137@@ -306,7 +313,8 @@
138 self.test_ratelimit = dummy_filter_factory(conf_dict)(FakeApp())
139 ratelimit.http_connect = mock_http_connect(204)
140 self.test_ratelimit.log_sleep_time_seconds = .00001
141- req = Request.blank('/v/a')
142+ req = Request.blank('/v/a/c')
143+ req.method = 'PUT'
144 req.environ['swift.cache'] = FakeMemcache()
145
146 time_override = [0, 0, 0, 0, None]
147@@ -335,7 +343,7 @@
148 logger=FakeLogger())
149 the_app.memcache_client = fake_memcache
150 req = lambda: None
151- req.method = 'GET'
152+ req.method = 'PUT'
153
154 class rate_caller(Thread):
155
156@@ -346,7 +354,7 @@
157 def run(self):
158 for j in range(num_calls):
159 self.result = the_app.handle_ratelimit(req, self.myname,
160- None, None)
161+ 'c', None)
162
163 nt = 15
164 begin = time.time()
165@@ -361,45 +369,6 @@
166 time_took = time.time() - begin
167 self.assertEquals(1.5, round(time_took, 1))
168
169- def test_ratelimit_acc_vrs_container(self):
170- conf_dict = {'clock_accuracy': 1000,
171- 'account_ratelimit': 10,
172- 'max_sleep_time_seconds': 4,
173- 'container_ratelimit_10': 6,
174- 'container_ratelimit_50': 2,
175- 'container_ratelimit_75': 1}
176- self.test_ratelimit = dummy_filter_factory(conf_dict)(FakeApp())
177- ratelimit.http_connect = mock_http_connect(204)
178- req = Request.blank('/v/a/c')
179- req.environ['swift.cache'] = FakeMemcache()
180- cont_key = get_container_memcache_key('a', 'c')
181-
182- class rate_caller(Thread):
183-
184- def __init__(self, parent, name):
185- Thread.__init__(self)
186- self.parent = parent
187- self.name = name
188-
189- def run(self):
190- self.result = self.parent.test_ratelimit(req.environ,
191- start_response)
192-
193- def runthreads(threads, nt):
194- for i in range(nt):
195- rc = rate_caller(self, "thread %s" % i)
196- rc.start()
197- threads.append(rc)
198- for thread in threads:
199- thread.join()
200- begin = time.time()
201- req.environ['swift.cache'].set(cont_key, {'container_size': 20})
202- begin = time.time()
203- threads = []
204- runthreads(threads, 3)
205- time_took = time.time() - begin
206- self.assertEquals(round(time_took, 1), .4)
207-
208 def test_call_invalid_path(self):
209 env = {'REQUEST_METHOD': 'GET',
210 'SCRIPT_NAME': '',
211@@ -441,7 +410,8 @@
212 conf_dict = {'account_ratelimit': current_rate}
213 self.test_ratelimit = ratelimit.filter_factory(conf_dict)(FakeApp())
214 ratelimit.http_connect = mock_http_connect(204)
215- req = Request.blank('/v/a')
216+ req = Request.blank('/v/a/c')
217+ req.method = 'PUT'
218 req.environ['swift.cache'] = FakeMemcache()
219 req.environ['swift.cache'].error_on_incr = True
220 make_app_call = lambda: self.test_ratelimit(req.environ,