##// END OF EJS Templates
tests: updated load tests CLIs
marcink -
r3811:0c0d4d8c stable
parent child Browse files
Show More
@@ -0,0 +1,2 b''
1 big/CPython
2 big/CPython/commits
@@ -1,69 +1,73 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import timeit
22 import logging
23 import click
22 24
23 server = "localhost:5000"
25 log = logging.getLogger(__name__)
26
24 27
25 pages = [
26 "cpython",
27 "cpython/annotate/74236c8bf064188516b32bf95016971227ec72a9/Makefile.pre.in",
28 "cpython/changelog",
29 "cpython/changeset/e0f681f4ade3af52915d5f32daac97ada580d71a",
30 "cpython/compare/tag@v3.4.1rc1...tag@v3.4.1?target_repo=cpython",
31 "cpython/files/tip/",
32 "cpython/files/74236c8bf064188516b32bf95016971227ec72a9/Grammar",
33 "",
34 "git",
35 "git/annotate/6c4ab27f2378ce67940b4496365043119d7ffff2/gitk-git/.gitignore",
36 "git/changelog",
37 "git/changeset/d299e9e550c1bf8640907fdba1f03cc585ee71df",
38 "git/compare/rev@1200...rev@1300?target_repo=git",
39 "git/files/tip/",
40 "git/files/6c4ab27f2378ce67940b4496365043119d7ffff2/.gitignore"
41 ]
28 @click.command()
29 @click.option('--server', help='Server url to connect to. e.g http://rc.local.com', required=True)
30 @click.option('--pages', help='load pages to visit from a file', required=True, type=click.File())
31 @click.option('--repeat', help='number of times to repeat', default=10, type=int)
32 def main(server, repeat, pages):
33
34 print("Repeating each URL %d times\n" % repeat)
35 pages = pages.readlines()
36
37 for page_url in pages:
38
39 url = "%s/%s" % (server, page_url.strip())
40 print(url)
41
42 stmt = "requests.get('%s', timeout=120)" % url
43 t = timeit.Timer(stmt=stmt, setup="import requests")
42 44
43 svn_pages = [
44 "svn-apache",
45 "svn-apache/annotate/672129/cocoon/trunk/README.txt",
46 "svn-apache/changelog",
47 "svn-apache/changeset/1164362",
48 "svn-apache/compare/rev@1164350...rev@1164360?target_repo=svn-apache",
49 "svn-apache/compare/rev@1164300...rev@1164360?target_repo=svn-apache",
50 "svn-apache/files/tip/",
51 "svn-apache/files/1164363/cocoon/trunk/README.txt",
52 ]
45 result = t.repeat(repeat=repeat, number=1)
46 print(" %.3f (min) - %.3f (max) - %.3f (avg)\n" %
47 (min(result), max(result), sum(result) / len(result)))
48
53 49
54 # Uncomment to check also svn performance
55 # pages = pages + svn_pages
50 if __name__ == '__main__':
51 main()
52
53
54
55
56
57
58
56 59
57 repeat = 10
60
61
58 62
59 print("Repeating each URL x%d\n" % repeat)
60 for page in pages:
61 url = "http://%s/%s" % (server, page)
62 print(url)
63
64
65
63 66
64 stmt = "urllib2.urlopen('%s', timeout=120)" % url
65 t = timeit.Timer(stmt=stmt, setup="import urllib2")
67
68
69
66 70
67 result = t.repeat(repeat=repeat, number=1)
68 print("\t%.3f (min) - %.3f (max) - %.3f (avg)\n" %
69 (min(result), max(result), sum(result)/len(result)))
71
72
73
@@ -1,305 +1,306 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 VCS Performance measurement tool
23 23
24 24 Usage:
25 25
26 26 - Check that required vcs keys can be found in ~/.hgrc and ~/.netrc
27 27
28 28 - Start a local instance of RhodeCode Enterprise
29 29
30 30 - Launch the script:
31 31
32 32 TMPDIR=/tmp python vcs_performance.py \
33 33 --host=http://vm:5000 \
34 34 --api-key=55c4a33688577da24183dcac5fde4dddfdbf18dc \
35 35 --commits=10 --repositories=100 --log-level=info
36 36 """
37 37
38 38 import argparse
39 39 import functools
40 40 import logging
41 41 import os
42 42 import shutil
43 43 import subprocess32
44 44 import tempfile
45 45 import time
46 46 from itertools import chain
47 47
48 48 from api import RCApi, ApiError
49 49
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 def timed(f):
55 55 """Decorator that returns the time it took to execute the function."""
56 56 @functools.wraps(f)
57 57 def wrapped_f(*args, **kwargs):
58 58 start_time = time.time()
59 59 try:
60 60 f(*args, **kwargs)
61 61 finally:
62 62 return time.time() - start_time
63 63
64 64 return wrapped_f
65 65
66 66
67 67 def mean(container):
68 68 """Return the mean of the container."""
69 69 if not container:
70 70 return -1.0
71 71 return sum(container) / len(container)
72 72
73 73
74 74 class Config(object):
75 75 args = None
76 76
77 77 def __init__(self):
78 78 parser = argparse.ArgumentParser(description='Runs VCS load tests')
79 79 parser.add_argument(
80 80 '--host', dest='host', action='store', required=True,
81 81 help='RhodeCode Enterprise host')
82 82 parser.add_argument(
83 83 '--api-key', dest='api_key', action='store', required=True,
84 84 help='API Key')
85 85 parser.add_argument(
86 86 '--file-size', dest='file_size', action='store', required=False,
87 87 default=1, type=int, help='File size in MB')
88 88 parser.add_argument(
89 89 '--repositories', dest='repositories', action='store',
90 90 required=False, default=1, type=int,
91 91 help='Number of repositories')
92 92 parser.add_argument(
93 93 '--commits', dest='commits', action='store', required=False,
94 94 default=1, type=int, help='Number of commits')
95 95 parser.add_argument(
96 96 '--log-level', dest='log_level', action='store', required=False,
97 97 default='error', help='Logging level')
98 98 self.args = parser.parse_args()
99 99
100 100 def __getattr__(self, attr):
101 101 return getattr(self.args, attr)
102 102
103 103
104 104 class Repository(object):
105 105 FILE_NAME_TEMPLATE = "test_{:09d}.bin"
106 106
107 107 def __init__(self, name, base_path, api):
108 108 self.name = name
109 109 self.path = os.path.join(base_path, name)
110 110 self.api = api
111 self.url = None
111 112
112 113 def create(self):
113 114 self._create_filesystem_repo(self.path)
114 115 try:
115 self.url = self.api.create_repo(
116 self.name, self.TYPE, 'Performance tests')
116 self.url = self.api.create_repo(self.name, self.TYPE, 'Performance tests')
117 117 except ApiError as e:
118 118 log.error('api: {}'.format(e))
119 119
120 120 def delete(self):
121 121 self._delete_filesystem_repo()
122 122 try:
123 123 self.api.delete_repo(self.name)
124 124 except ApiError as e:
125 125 log.error('api: {}'.format(e))
126 126
127 127 def create_commits(self, number, file_size):
128 128 for i in xrange(number):
129 129 file_name = self.FILE_NAME_TEMPLATE.format(i)
130 log.debug("Create commit {}".format(file_name))
130 log.debug("Create commit[{}] {}".format(self.name, file_name))
131 131 self._create_file(file_name, file_size)
132 132 self._create_commit(file_name)
133 133
134 134 @timed
135 135 def push(self):
136 136 raise NotImplementedError()
137 137
138 138 @timed
139 139 def clone(self, destination_path):
140 140 raise NotImplementedError()
141 141
142 142 @timed
143 143 def pull(self):
144 144 raise NotImplementedError()
145 145
146 146 def _run(self, *args):
147 147 command = [self.BASE_COMMAND] + list(args)
148 148 process = subprocess32.Popen(
149 149 command, stdout=subprocess32.PIPE, stderr=subprocess32.PIPE)
150 150 return process.communicate()
151 151
152 152 def _create_file(self, name, size):
153 153 file_name = os.path.join(self.path, name)
154 154 with open(file_name, 'wb') as f:
155 155 f.write(os.urandom(1024))
156 156
157 157 def _delete_filesystem_repo(self):
158 158 shutil.rmtree(self.path)
159 159
160 160 def _create_filesystem_repo(self, path):
161 161 raise NotImplementedError()
162 162
163 163 def _create_commit(self, file_name):
164 164 raise NotImplementedError()
165 165
166 166
167 167 class GitRepository(Repository):
168 168 TYPE = 'git'
169 169 BASE_COMMAND = 'git'
170 170
171 171 @timed
172 172 def push(self):
173 173 os.chdir(self.path)
174 174 self._run('push', '--set-upstream', self.url, 'master')
175 175
176 176 @timed
177 177 def clone(self, destination_path):
178 178 self._run('clone', self.url, os.path.join(destination_path, self.name))
179 179
180 180 @timed
181 181 def pull(self, destination_path):
182 182 path = os.path.join(destination_path, self.name)
183 183 self._create_filesystem_repo(path)
184 184 os.chdir(path)
185 185 self._run('remote', 'add', 'origin', self.url)
186 186 self._run('pull', 'origin', 'master')
187 187
188 188 def _create_filesystem_repo(self, path):
189 189 self._run('init', path)
190 190
191 191 def _create_commit(self, file_name):
192 192 os.chdir(self.path)
193 193 self._run('add', file_name)
194 194 self._run('commit', file_name, '-m', '"Add {}"'.format(file_name))
195 195
196 196
197 197 class HgRepository(Repository):
198 198 TYPE = 'hg'
199 199 BASE_COMMAND = 'hg'
200 200
201 201 @timed
202 202 def push(self):
203 203 os.chdir(self.path)
204 204 self._run('push', self.url)
205 205
206 206 @timed
207 207 def clone(self, destination_path):
208 208 self._run('clone', self.url, os.path.join(destination_path, self.name))
209 209
210 210 @timed
211 211 def pull(self, destination_path):
212 212 path = os.path.join(destination_path, self.name)
213 213 self._create_filesystem_repo(path)
214 214 os.chdir(path)
215 215 self._run('pull', '-r', 'tip', self.url)
216 216
217 217 def _create_filesystem_repo(self, path):
218 218 self._run('init', path)
219 219
220 220 def _create_commit(self, file_name):
221 221 os.chdir(self.path)
222 222 self._run('add', file_name)
223 223 self._run('commit', file_name, '-m', '"Add {}"'.format(file_name))
224 224
225 225
226 226 class Benchmark(object):
227 227 REPO_CLASSES = {
228 228 'git': GitRepository,
229 229 'hg': HgRepository
230 230 }
231 231 REPO_NAME = '{}_performance_{:03d}'
232 232
233 233 def __init__(self, config):
234 234 self.api = RCApi(api_key=config.api_key, rc_endpoint=config.host)
235 235 self.source_path = tempfile.mkdtemp(suffix='vcsperformance')
236 236
237 237 self.config = config
238 238 self.git_repos = []
239 239 self.hg_repos = []
240 240
241 241 self._set_log_level()
242 242
243 243 def start(self):
244 244 self._create_repos()
245 245 repos = {
246 246 'git': self.git_repos,
247 247 'hg': self.hg_repos
248 248 }
249 249
250 250 clone_destination_path = tempfile.mkdtemp(suffix='clone')
251 251 pull_destination_path = tempfile.mkdtemp(suffix='pull')
252 252 operations = [
253 253 ('push', ),
254 254 ('clone', clone_destination_path),
255 255 ('pull', pull_destination_path)
256 256 ]
257 257
258 258 for operation in operations:
259 259 for type_ in repos:
260 260 times = self._measure(repos[type_], *operation)
261 print("Mean {} {} time: {:.3f} sec.".format(
262 type_, operation[0], mean(times)))
261 print("Mean[of {}] {:5s} {:5s} time: {:.3f} sec.".format(
262 len(times), type_, operation[0], mean(times)))
263 263
264 264 def cleanup(self):
265 265 log.info("Cleaning up...")
266 266 for repo in chain(self.git_repos, self.hg_repos):
267 267 repo.delete()
268 268
269 269 def _measure(self, repos, operation, *args):
270 270 times = []
271 271 for repo in repos:
272 272 method = getattr(repo, operation)
273 273 times.append(method(*args))
274 274 return times
275 275
276 276 def _create_repos(self):
277 277 log.info("Creating repositories...")
278 278 for i in xrange(self.config.repositories):
279 279 self.git_repos.append(self._create_repo('git', i))
280 280 self.hg_repos.append(self._create_repo('hg', i))
281 281
282 282 def _create_repo(self, type_, id_):
283 283 RepoClass = self.REPO_CLASSES[type_]
284 284 repo = RepoClass(
285 285 self.REPO_NAME.format(type_, id_), self.source_path, self.api)
286 286 repo.create()
287 287 repo.create_commits(self.config.commits, self.config.file_size)
288 288 return repo
289 289
290 290 def _set_log_level(self):
291 291 try:
292 292 log_level = getattr(logging, config.log_level.upper())
293 293 except:
294 294 log_level = logging.ERROR
295 295 handler = logging.StreamHandler()
296 296 log.addHandler(handler)
297 297 log.setLevel(log_level)
298 298
299
299 300 if __name__ == '__main__':
300 301 config = Config()
301 302 benchmark = Benchmark(config)
302 303 try:
303 304 benchmark.start()
304 305 finally:
305 306 benchmark.cleanup()
General Comments 0
You need to be logged in to leave comments. Login now