PageRenderTime 15ms CodeModel.GetById 1ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 1ms

/historical/sqllog_gentest.py

https://bitbucket.org/lindenlab/apiary/
Python | 196 lines | 166 code | 6 blank | 24 comment | 0 complexity | 9d3cb197c7c3715698b1b3b80b5cc403 MD5 | raw file
  1#
  2# $LicenseInfo:firstyear=2010&license=mit$
  3# 
  4# Copyright (c) 2010, Linden Research, Inc.
  5# 
  6# Permission is hereby granted, free of charge, to any person obtaining a copy
  7# of this software and associated documentation files (the "Software"), to deal
  8# in the Software without restriction, including without limitation the rights
  9# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10# copies of the Software, and to permit persons to whom the Software is
 11# furnished to do so, subject to the following conditions:
 12# 
 13# The above copyright notice and this permission notice shall be included in
 14# all copies or substantial portions of the Software.
 15# 
 16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22# THE SOFTWARE.
 23# $/LicenseInfo$
 24#
 25
 26import math
 27import random
 28import sys
 29
 30import sqllog
 31import timestamp
 32
 33
 34class SQLModel(object):
 35    
 36    def __init__(self):
 37        self._sum_of_weights = 0.0
 38        self._entries = []
 39        self._weights = []
 40        self._accounts = []
 41        self._add_all_sql_options()
 42    
 43    def initial_sql(self):
 44        sql = """
 45DROP TABLE IF EXISTS `test`.`bankbalance`;
 46DROP TABLE IF EXISTS `test`.`banktransaction`;
 47CREATE TABLE `test`.`bankbalance` (
 48  `acct` VARCHAR(16) NOT NULL,
 49  `balance` INT NOT NULL DEFAULT 0,
 50  PRIMARY KEY (`acct`)
 51);
 52CREATE TABLE `test`.`banktransaction` (
 53  `sequence` INT NOT NULL AUTO_INCREMENT,
 54  `acct` VARCHAR(16) NOT NULL,
 55  `amount` INT NOT NULL DEFAULT 0,
 56  `cleared` ENUM('N', 'Y') DEFAULT 'N',
 57  PRIMARY KEY (`sequence`),
 58  INDEX acct(`acct`)
 59);
 60INSERT INTO bankbalance SET acct="zzz-99999", balance=0;
 61"""
 62        return sql.split(';')
 63
 64    def _add_sql_option(self, weight, sql):
 65        self._sum_of_weights += weight
 66        self._entries.append(sql)
 67        self._weights.append(weight)
 68
 69    def _add_all_sql_options(self):
 70        options = """
 712:INSERT INTO bankbalance SET acct="%(newacct)s", balance=100
 7225:INSERT INTO banktransaction SET acct="%(acct)s", amount=%(delta)d
 735:SELECT acct FROM bankbalance WHERE balance < 0
 7430:SELECT balance FROM bankbalance WHERE acct="%(acct)s"
 755:UPDATE bankbalance SET balance=balance+%(delta)d WHERE acct="%(acct)s"
 765:SELECT sequence, amount FROM banktransaction WHERE acct="%(acct)s" AND cleared="N"
 775:UPDATE banktransaction SET cleared="Y" WHERE acct="%(acct)s" AND cleared="N"
 78"""
 79        for line in options.split('\n'):
 80            if ':' in line:
 81                (weight,sql) = line.split(':',1)
 82                weight = float(weight)
 83                self._add_sql_option(weight,sql)
 84
 85    def _random_acct(self):
 86        if self._accounts:
 87            return random.choice(self._accounts)
 88        return 'zzz-99999'
 89    
 90    def _random_new_acct(self):
 91        acct = 'aaa-%05d' % random.randint(0,99999)
 92        self._accounts.append(acct)
 93        return acct
 94    
 95    def _random_delta(self):
 96        return random.randint(-20,20)
 97    
 98    def random_sql(self):
 99        v = random.uniform(0.0, self._sum_of_weights)
100        for i in xrange(0, len(self._entries)):
101            v -= self._weights[i]
102            if v < 0.0:
103                sql = self._entries[i]
104                break
105        values = {}
106        if '%(newacct)' in sql:
107            values['newacct'] = self._random_new_acct()
108        if '%(acct)' in sql:
109            values['acct'] = self._random_acct()
110        if '%(delta)' in sql:
111            values['delta'] = self._random_delta()
112        return sql % values
113
114
115
116class Generator(object):
117
118    def __init__(self, model, target_event_count,
119                        target_concurrency, target_sequence_length):
120        self._model = model
121        self._target_event_count = target_event_count
122        self._target_concurrency = target_concurrency
123        self._target_sequence_length = target_sequence_length
124
125    def run(self):
126        self._sequences = []
127        self._sequence_count = 0
128        self._sequence_events_to_go = { }
129        self._event_count = 0
130        self._time = timestamp.TimeStamp(1000000)
131        self._timeincr = timestamp.TimeStamp(0,1000) # 10ms
132        
133        self.initial();
134        while self.step():
135            pass
136    
137    def initial(self):
138        name = "7:999999"
139        start = self._time
140        for sql in self._model.initial_sql():
141            sql = sql.strip()
142            if sql:
143                self.output_event(name, "Init", sql)
144        self.gen_end(name)
145        self._time = start + timestamp.TimeStamp(1)
146        
147    def step(self):
148        winding_down = self._event_count > self._target_event_count
149        how_full = float(len(self._sequences)) / self._target_concurrency
150        should_add = math.exp(-1.0 * math.pow(how_full, 2.0))
151        if not winding_down and random.random() < should_add:
152            self.step_add()
153            return True
154        if self._sequences:
155            self.step_event(random.choice(self._sequences))
156            return True
157        return False # nothing to do!
158    
159    def step_add(self):
160        self._sequence_count += 1
161        name = "8:%06d" % self._sequence_count
162        count = max(1, int(random.normalvariate(
163                self._target_sequence_length,
164                self._target_sequence_length/2.0)))
165        self._sequences.append(name)
166        self._sequence_events_to_go[name] = count
167        self.step_event(name)
168    
169    def step_event(self, name):
170        if self._sequence_events_to_go[name] > 0:
171            self.gen_event(name)
172            self._sequence_events_to_go[name] -= 1
173            self._event_count += 1
174        else:
175            self.gen_end(name)
176            del self._sequence_events_to_go[name]
177            self._sequences = self._sequence_events_to_go.keys()
178    
179    def gen_event(self, name):
180        self.output_event(name, sqllog.Event.Query, self._model.random_sql())
181    
182    def gen_end(self, name):
183        self.output_event(name, sqllog.Event.End, "Quit")
184    
185    def output_event(self, seq, state, body):
186        t = self._time
187        self._time = self._time + self._timeincr
188        e = sqllog.Event(t, seq, "test", state, body)
189        sys.stdout.write(str(e))
190    
191
192if __name__ == '__main__':
193    m = SQLModel()
194    g = Generator(m, 1000, 5, 6.0)
195    g.run()
196