Topic(s):   Informix

April 02, 2008

Writing a Simple Informix Engine Benchmark in Python
Posted by Andrew Ford @ 09:32 AM ET | Apr 2, 2008

The kind people at IBM Database Magazine have asked me to join their highly unpaid blogging staff to write a few paragraphs a month on all things Informix. I'll be pulling from my own day to day experiences as a DBA who frequently puts on the software developer, system administrator, database architect and storage administrator hats for content. Today I'll be wearing the software developer and DBA hats to write a quick and easy python script to hammer our database engine to get a rough idea of what kind of load our servers can handle.

Writing a simple benchmark in python for your environment is easy and a good way to get a feel for the maximum transaction rates of your platform. If you know this information you will be able to identify the limits of your system and prepare for growth before you hit those limits. I want to show you how I wrote a script to benchmark for the account lookup and balance update for our stored value telecom application using threads to simulate our multiple front ends accessing the account database.

My python script was written in one afternoon, but it gets the job done. I create an AccountDbThread class that extends the Thread class and continuously reads in account numbers from a flat file, attempts to select, lock, update and unlock these accounts until it is told to stop. From this benchmark I can easily see the maximum number of account lookup and balance update transactions my database engines can handle per second. If I see the number of production transactions per second trending toward this number I can intervene months before there are problems.

You will need python (www.python.org) and the informixdb python module (informixdb.sourceforge.net) if you would like to modify and run the following script against your test environment.

    import threading
    import informixdb
    import time
    
    # number of seconds test will run
    runTime = 1800
    
    # threads will stop when the main thread sets keepGoing to 0
    keepGoing = 1
    
    class AccountDbThread (threading.Thread):
    
       # AccountDbThread constructor
       def __init__(self, name, accountFile, dbName, user, password):
          # just store some stuff for now
          self.name = name
          self.accountFile = accountFile
          self.dbName = dbName
          self.user = user
          self.password = password
    
          # and call the parent thread constructor
          threading.Thread.__init__(self)
    
       # run will be called when the thread is started
       # when the thread is started it will connect to the database, open the account file and
       # continously process account lookup and balance update transactions until the
       # thread is told to stop
       def run(self):
          global keepGoing
    
          # total number of accounts processed by this thread
          totalAccounts = 0
    
          # connect this thread to the database, set the isolation level and set the lock mode
          dbConn = informixdb.connect(self.dbName, self.user, self.password, autocommit = True)
          dbCur = dbConn.cursor(rowformat=informixdb.ROW_AS_OBJECT)
          dbCur.execute("set isolation committed read")
          dbCur.execute("set lock mode to wait 5")
          dbCur.close()
    
          print "%s - connected to %s" % (self.name, self.dbName)
    
          # prepare the 4 SQL statements that make up a account lookup and
          # balance update transaction
    
          # lookup the account
          dbCur1 = dbConn.cursor(rowformat=informixdb.ROW_AS_OBJECT)
          dbCur1.prepare("select balance, status from account where account_id = :1 and customer_id = :2")
    
          # lock the account
          dbCur2 = dbConn.cursor(rowformat=informixdb.ROW_AS_OBJECT)
          dbCur2.prepare("insert into locked_accounts (account_id, customer_id, lock_time) values (:1, :2, CURRENT)")
    
          # update the account balance
          dbCur3 = dbConn.cursor(rowformat=informixdb.ROW_AS_OBJECT)
          dbCur3.prepare("update account set balance = balance - :1 where account_id = :2 and customer_id = :3")
    
          # unlock the account
          dbCur4 = dbConn.cursor(rowformat=informixdb.ROW_AS_OBJECT)
          dbCur4.prepare("delete from locked_accounts where account_id = :1 and customer_id = :2")
    
          # open the file containing our account numbers
          accountFile = open(self.accountFile, 'r')
          print "%s - opened %s" % (self.name, self.accountFile)
    
          # save the start time of the benchmark
          startTime = time.time()
    
          # continuously read each line of the account flat file one line at a time
          while True:
             for line in accountFile:
                # increment the number of accounts processed
                totalAccounts = totalAccounts + 1
    
                # initialize accountLocked to false
                accountLocked = False
    
                # each line of the file is in the form of account_id|customer_id|
                # we need to split this into variables to be passed to our prepared sql
                fields = line.split('|')
                accountId = fields[0]
                customerId = fields[1]
    
                # lookup the account to see if it exists
                dbCur1.execute(dbCur1.command, (accountId, customerId))
                dbRow = dbCur1.fetchone()
                if dbRow != None:
                   # the account exists, attempt to lock it
                   try:
                      dbCur2.execute(dbCur2.command, (accountId, customerId))
                      accountLocked = True
    
                   except informixdb.DatabaseError, e:
                      if e.sqlcode == -268:
                         print "account already in use"
    
                   if accountLocked == True:
                      # update the account balance
                      dbCur3.execute(dbCur3.command, (10, accountId, customerId))
    
                      # unlock the account
                      dbCur4.execute(dbCur4.command, (accountId, customerId))
    
                # print status every 10000 accounts
                if totalAccounts % 10000 == 0:
                   elapsedTime = time.time() - startTime
    
                   print "%s - %ld transactions in %f seconds, %f transactions per second" % 
                                  (self.name, totalAccounts, elapsedTime, totalAccounts / elapsedTime)
    
                if keepGoing == 0:
                   break;
    
             if keepGoing == 0:
                break;
    
             # we must have run out of lines in our input file
             # close and open the file containing our account numbers
             accountFile.close()
             accountFile = open(self.accountFile, 'r')
    
          elapsedTime = time.time() - startTime
    
          print "%s complete - %ld transactions in %f seconds, %f transactions per second" % 
                                  (self.name, totalAccounts, elapsedTime, totalAccounts / elapsedTime)
    
          accountFile.close()
    
    
    # instantiate 5 threads to simulate a production environment
    thread1 = AccountDbThread("thread1", "thread1_accounts.unl", "accountdb@testsvr", "user1", "12345")
    thread2 = AccountDbThread("thread2", "thread2_accounts.unl", "accountdb@testsvr", "user1", "12345")
    thread3 = AccountDbThread("thread3", "thread3_accounts.unl", "accountdb@testsvr", "user1", "12345")
    thread4 = AccountDbThread("thread4", "thread4_accounts.unl", "accountdb@testsvr", "user1", "12345")
    thread5 = AccountDbThread("thread5", "thread5_accounts.unl", "accountdb@testsvr", "user1", "12345")
    
    # start the 5 threads
    thread1.start()
    thread2.start()
    thread3.start()
    thread4.start()
    thread5.start()
    
    # have the main thread sleep for however long we want to run our test
    time.sleep(runTime)
    
    # and instruct the threads to stop running when we wake up to end our test
    keepGoing = 0
    
    # wait for the 5 threads to finish
    thread1.join()
    thread2.join()
    thread3.join()
    thread4.join()
    thread5.join()
    
    print "test complete"
    

Running this benchmark against test hardware and data that mirrors production I get the following results

    thread1 - connected to accountdb@testsvr
    thread3 - connected to accountdb@testsvr
    thread1 - opened thread1_accounts.unl
    thread3 - opened thread3_accounts.unl
    thread4 - connected to accountdb@testsvr
    thread2 - connected to accountdb@testsvr
    thread5 - connected to accountdb@testsvr
    thread5 - opened thread5_accounts.unl
    thread2 - opened thread2_accounts.unl
    thread4 - opened thread4_accounts.unl
    account already in use
    account already in use
    account already in use
    account already in use
    account already in use
    account already in use
    account already in use
    account already in use
    thread3 - 10000 transactions in 52.273773 seconds, 191.300520 transactions per second
    thread1 - 10000 transactions in 52.491554 seconds, 190.506838 transactions per second
    thread4 - 10000 transactions in 52.502110 seconds, 190.468535 transactions per second
    thread5 - 10000 transactions in 52.649253 seconds, 189.936218 transactions per second
    thread2 - 10000 transactions in 52.703727 seconds, 189.739902 transactions per second
    account already in use
    thread3 - 20000 transactions in 104.554338 seconds, 191.288093 transactions per second
    thread4 - 20000 transactions in 105.068118 seconds, 190.352701 transactions per second
    thread1 - 20000 transactions in 105.105420 seconds, 190.285144 transactions per second
    thread5 - 20000 transactions in 105.350764 seconds, 189.842002 transactions per second
    thread2 - 20000 transactions in 105.536202 seconds, 189.508431 transactions per second
    account already in use
    account already in use
    account already in use
    account already in use
    thread3 - 30000 transactions in 157.190037 seconds, 190.851790 transactions per second
    thread1 - 30000 transactions in 157.445474 seconds, 190.542156 transactions per second
    thread5 - 30000 transactions in 157.948753 seconds, 189.935023 transactions per second
    thread4 - 30000 transactions in 158.176442 seconds, 189.661619 transactions per second
    thread2 - 30000 transactions in 158.329003 seconds, 189.478867 transactions per second
    .
    .
    .
    thread1 - 320000 transactions in 1696.054366 seconds, 188.673197 transactions per second
    thread5 - 320000 transactions in 1700.813605 seconds, 188.145249 transactions per second
    thread2 - 320000 transactions in 1703.851234 seconds, 187.809824 transactions per second
    thread3 - 320000 transactions in 1706.857070 seconds, 187.479084 transactions per second
    thread4 - 320000 transactions in 1716.252144 seconds, 186.452790 transactions per second
    account already in use
    account already in use
    account already in use
    thread1 - 330000 transactions in 1748.419306 seconds, 188.741910 transactions per second
    thread5 - 330000 transactions in 1753.094668 seconds, 188.238551 transactions per second
    thread2 - 330000 transactions in 1756.496155 seconds, 187.874024 transactions per second
    thread3 - 330000 transactions in 1759.540814 seconds, 187.548932 transactions per second
    account already in use
    account already in use
    account already in use
    account already in use
    account already in use
    thread4 - 330000 transactions in 1769.437438 seconds, 186.499954 transactions per second
    account already in use
    thread5 complete - 338864 transactions in 1799.783685 seconds, 188.280404 transactions per second
    thread3 complete - 337639 transactions in 1799.793745 seconds, 187.598718 transactions per second
    thread2 complete - 338187 transactions in 1799.789034 seconds, 187.903690 transactions per second
    thread1 complete - 339628 transactions in 1799.799293 seconds, 188.703263 transactions per second
    thread4 complete - 335589 transactions in 1799.791470 seconds, 186.459935 transactions per second
    test complete
    

Each front end can service around 190 transactions per second so the system can handle about 950 transactions per second. This is good information for me to know. If my peak transaction rate is 100 per second in the production environment, I can feel pretty confident that I do not need to worry about running into performance problems with my account lookup and balance update transactions.

Trackback Pings

TrackBack URL for this entry:
http://www.ibmdatabasemag.com/blog/main/archives/2008/04/writing_a_simpl.html

« DB2 LUW Performance: Table Read I/O and Overflows | Main | DB2 DBA Certification Sample Questions: Using the Tools Settings notebook and deleting all records from a specific table »





This is a public forum. CMP Media and its affiliates are not responsible for and do not control what is posted herein. CMP Media makes no warranties or guarantees concerning any advice dispensed by its staff members or readers.

Community standards in this comment area do not permit hate language, excessive profanity, or other patently offensive language. Please be aware that all information posted to this comment area becomes the property of CMP Media LLC and may be edited and republished in print or electronic format as outlined in CMP Media's Terms of Service.

Important Note: This comment area is NOT intended for commercial messages or solicitations of business.



CAREER CENTER
Ready to take that job and shove it?
SEARCH JOBS
RECENT JOB POSTINGS
CAREER NEWS
10 Search Engines You Don't Know About
Go beyond Google and get vertical. These specialized search sites will help you find the business information you need -- fast.

Subscribe to the new digital version of IBM Database Magazine
New Digital Version

Sponsored links:



Subscribe to the IBM Database Magazine Newsletter

Email Address *
First Name
Last Name
HTML Preference
HTML Text
 

Fields with * are required.

 




Visit these other IBM and TechWeb Partner Sites: :
Maximizing ROI Through Business Process Management (BPM) and Service-Oriented Architecture (SOA)
Internet Evolution – The Macrosite for News, Analysis, & Opinion About the Future of the Internet
Business Innovation – Technology Strategies and Solutions for Driving Business Success