Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
stress.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 # Groonga/Dicty server stress tool
4 # (c) Brazil 2007-
5 
6 # Copyright(C) 2007 Brazil
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 
22 import os
23 import sys
24 import time
25 import random
26 import pprint
27 import threading
28 from optparse import OptionParser
29 
30 def error(msg, info, lock):
31  lock.acquire()
32  try:
33  sys.stderr.write("* error *\n%s\n" % msg)
34  pp = pprint.PrettyPrinter(indent = 2, stream = sys.stderr)
35  pp.pprint(info)
36  finally:
37  lock.release()
38 
39 def worker(ctx, p, lock):
40  info = {'host': random.choice(p.targets)}
41  host = info['host'][0]
42  port = info['host'][1]
43 
44  if port == 'file':
45  db = ctx.Db.open(host)
46  c = db.ctx_open(ctx.CTX_USEQL)
47  if not c:
48  error('ctx_open failed', info, lock)
49  return
50  else:
51  c = ctx.Context.connect(host, port, ctx.CTX_USEQL)
52  if not c:
53  error('connect failed', info, lock)
54  return
55 
56  info['queries'] = 0
57  for i in xrange(p.n_loop):
58  for q in p.queries:
59  info['queries'] += 1
60  rc = c.send(q, 0)
61  if rc:
62  info['ctxinfo'] = c.info_get()
63  error('send error: %d' % rc, info, lock)
64  return
65  while True:
66  (rc, buf, flags) = c.recv()
67  # print buf
68  if rc:
69  info['ctxinfo'] = c.info_get()
70  error('recv error: %d' % rc, info, lock)
71  return
72  if not (flags & ctx.CTX_MORE):
73  break
74 
75  if p.verbose:
76  thd = threading.currentThread()
77  print 'end thread (threadname: %s)' % thd.getName()
78 
79 def parse_opts():
80  p = OptionParser(usage = '%prog [options] dest')
81  p.add_option('-p', '--process', dest = 'n_proc',
82  default = 4, type = 'int',
83  help = 'specify number of processes forked')
84  p.add_option('-t', '--thread', dest = 'n_thd',
85  default = 2, type = 'int',
86  help = 'specify number of threads by one process')
87  p.add_option('-l', '--loop', dest = 'n_loop',
88  default = 10, type = 'int',
89  help = 'specify number of loops on one worker thread')
90  p.add_option('', '--timeout', dest = 'timeout',
91  default = 3, type = 'int',
92  help = 'specify timeout on making context')
93  p.add_option('-q', '--query-file', dest = 'queryfile',
94  help = 'specify query file')
95  p.add_option('-v', '--verbose', dest = 'verbose',
96  action = 'store_true', default = False,
97  help = 'verbose mode')
98  return p.parse_args()
99 
100 def main():
101  def check_ctx(targets):
102  import groongactx as ctx
103  for name, port in targets:
104  if port == 'file':
105  db = ctx.Db.open(name)
106  if not db:
107  print 'file: %s cannot be opened with sen_db_open.'
108  sys.exit(1)
109  c = db.ctx_open(ctx.CTX_USEQL)
110  if not c:
111  print 'file: %s sen_ctx_open failed.'
112  sys.exit(1)
113  else:
114  c = ctx.Context.connect(name, port, ctx.CTX_USEQL)
115  if not c:
116  print 'cannot connect groonga/dicty server(host: %s port: %d)'
117  sys.exit(1)
118 
119  (opts, args) = parse_opts()
120 
121  # check and load queryfile
122  if not opts.queryfile:
123  print 'please specify query file with -q option.'
124  sys.exit(1)
125  qf = open(opts.queryfile, 'r')
126  opts.queries = qf.readlines()
127  qf.close()
128 
129  if args:
130  opts.targets = [x.split(':') for x in args]
131  opts.targets = [(x[0], int(x[1])) for x in opts.targets]
132  else:
133  opts.targets = [('localhost', 10041), ]
134 
135  if opts.verbose:
136  for name, port in opts.targets:
137  if port == 'file':
138  print 'local database file: %s' % name
139  else:
140  print 'host: %s port: %d' % (name, port)
141 
142  # check ctx
143  pid = os.fork() # avoid import groongactx/sen_init call
144  if pid == 0:
145  t = threading.Thread(target = check_ctx, args = (opts.targets, ))
146  t.setDaemon(True)
147  t.start()
148  t.join(3)
149  if t.isAlive():
150  print 'context timeout.'
151  sys.exit(1)
152  else:
153  sys.exit(0)
154  (pid, st) = os.wait()
155  if os.WEXITSTATUS(st) != 0:
156  sys.exit(1)
157 
158  print "start %d processs, each process make %d threads." % (
159  opts.n_proc, opts.n_thd)
160  procs = list()
161  stime = time.time()
162  for i in xrange(opts.n_proc):
163  pid = os.fork()
164  if pid == 0:
165  import groongactx as ctx # sen_init per process
166  lock = threading.Lock()
167  threads = list()
168  for j in xrange(opts.n_thd):
169  t = threading.Thread(target = worker, args = (ctx, opts, lock))
170  t.setDaemon(True)
171  threads.append(t)
172  for t in threads:
173  t.start()
174  for t in threads:
175  t.join()
176  if opts.verbose:
177  print 'end process(pid: %d)' % os.getpid()
178  sys.exit(0) # sen_fin per process
179  else:
180  procs.append(pid)
181  for pid in procs:
182  os.waitpid(pid, 0)
183  etime = time.time()
184  print 'end all processes'
185 
186  print """
187 processes : %d
188 threads : %d (%d per process)
189 loops : %d (%d per thread)
190 queries : %d (%d per loop)
191 start time : %s
192 end time : %s
193 total time : %f sec""" % (
194  opts.n_proc,
195  opts.n_proc * opts.n_thd, opts.n_thd,
196  opts.n_proc * opts.n_thd * opts.n_loop, opts.n_loop,
197  opts.n_proc * opts.n_thd * opts.n_loop * len(opts.queries), len(opts.queries),
198  time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(stime)),
199  time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(etime)),
200  etime - stime)
201 
202 main()