Hello / Privyet / Zdrastvoychye / Bongiorno / Aloha / Howdy - 
!!!  Reasonable Success !!!

Well, I had a free evening here so I just stayed at work.  

Here's my first cut at this feature:
* I've tested it at between 20% and 40% faster (exact results below).
* It is completely optional / requires no code changes to existing code.
* Code NOT using this feature will run at its current speed.
* To use it:  add a sqlmeta line, plus add lines to both your create
connection / transaction call, and add a param to your createTransaction
calls.
* The only sqlobject code modified is main.py's class SQLObject _init().
* Below is the diff for main.py and the getTransaction example.
* Attached is a file with testing code.

This is the new init:

def _init(self, id, connection=None, selectResults=None):
    assert id is not None
    # This function gets called only when the object is
    # created, unlike __init__ which would be called
    # anytime the object was returned from cache.
    self.id = id
    self._SO_writeLock = threading.Lock()

    # If no connection was given, we'll inherit the class
    # instance variable which should have a _connection
    # attribute.
    if connection is not None:
        self._connection = connection
        # Sometimes we need to know if this instance is
        # global or tied to a particular connection.
        # This flag tells us that:
        self.sqlmeta._perConnection = True

    gotAnswer = False
    prepName = None
    try:
        prepName = self.sqlmeta.getByIDPrepName
    except AttributeError:
        pass
        # could add assert here if wanted.
    if not selectResults and self.sqlmeta._perConnection and prepName:
        statement = "execute %s (%s)" % (prepName, self.id)
        import sys, traceback
        try:
            selectResults = self._connection.queryOne(statement)
            gotAnswer = True
        except:
            msg = apply(traceback.format_exception, sys.exc_info())
            print "Could not fetch using prepared statement: %s" % (msg)

    if not gotAnswer and not selectResults:
        dbNames = [col.dbName for col in self.sqlmeta.columnList]
        selectResults = self._connection._SO_selectOne(self, dbNames)
        if not selectResults:
            raise SQLObjectNotFound, "The object %s by the ID %s does not
exist" % (self.__class__.__name__, self.id)
    self._SO_selectInit(selectResults)
    self._SO_createValues = {}
    self.dirty = False

The class definition changes to add one sqlmeta:

class Org(SQLObject):
    class sqlmeta:
        idName = 'OrgID'
        table  = 'Org'
        getByIDPrepName = table + '_getByID'


The getTransaction everyone uses in their own code changes to something like
this:

def getTransaction(tableList = []):
    dsn = "host=127.0.0.1 dbname=dbrel203 user=krice password=chicken4lunch"

    con = PostgresConnection(dsn, debug=0)
    for t in tableList:
        prepName = None
        try:
            prepName = t.sqlmeta.getByIDPrepName
        except AttributeError:
            print "No getByIDPrepName defined."
        assert prepName, "Must define a sqlmeta getByIDPrepName for each
table object passed into getTransaction."    
        dbNames = [col.dbName for col in t.sqlmeta.columnList]
        prep = "prepare %s (int) as select %s from %s where (%s.%s) = $1;" %
\
            (t.sqlmeta.table + '_getByID', 
             ', '.join([x for x in dbNames]), 
             t.sqlmeta.table, 
             t.sqlmeta.table, 
             t.sqlmeta.idName)
        print "Table: %s, prep=%s." % (t.__name__, prep)
        result = con.query(prep) 
        print "Result: %s" % result
    return con

Then, when declaring transactions, pass in the names of the tables for which
the getByIDPrepName is defined.

t= getTransaction(tableList=[TableName1, TableName2, ...])

There's one (sort of optional) thing to do is make it more fault-tolerant.
That is, the getTransaction call has to match what's defined in the
SQLObject class, or there's a traceback. I'll probably do this when I plug
this into a branch of our codebase.

Here's what I found from timing tests on the attached test program:

[ ALL OLD/NEW MEASUREMENTS IN SECONDS)
Small OrgUser object (2 fields):
With 4 users per org, 400 iterations of the loop:  old=53.91, new=44.09
(17% faster)
With 80 users per org, 30 iterations of the loop:  old=139.17, new=101.47
(37% faster)

Large OrgUser object (50 fields):
With 80 users per org, 30 iterations of the loop: old=172.95, new=123.53
(40% faster)
With 150 users per org, 30 iterations of the loop: old=352.41, new=234.01
(50% faster)

I'm not sure how people count these numbers, they could also be:
44/53 = .83 = 17% faster
101/139 = .72 = 28% faster
123/172 = .715 = 29% faster
234/352 = .665 = 34% faster

Regardless, for a simple hack, this seems like it could pay some
dividends...


 

Enjoy!

-- Kevin


___________________________________
Kevin J. Rice
Senior Software Engineer, Textura LLC
51-K Sherwood Terrace, Lake Bluff IL  
(847) 235-8437 (spells VISAFLUIDS)
(847) 845-7423 (845-RICE, cellphone)
___________________________________


# test multiple selects
# Kevin J. Rice (http://justanyone.com)

runWith = True
lotsOfColumns = True

import site, sys, os
site.addsitedir(os.path.realpath(__file__))
site.addsitedir(os.path.realpath('Lib/external'))
from sqlobject import *
from pprint import pprint, pformat
from sqlobject.sqlbuilder import IN
from sqlobject.col import popKey, pushKey
import sys, traceback

class SOTextCol(SOStringCol):
    def __init__(self, **kw):
        popKey(kw, 'length')
        popKey(kw, 'varchar')
        SOStringCol.__init__(self, **kw)

class TextCol(Col):
    baseClass = SOTextCol

def getTransaction(tableList = []):
    dsn = "host=127.0.0.1 dbname=dbrel203 user=krice password=chicken4lunch"    
    con = PostgresConnection(dsn, debug=0)
    for t in tableList:
        prepName = None
        try:
            prepName = t.sqlmeta.getByIDPrepName
        except AttributeError:
            print "*" * 80
            print "No getByIDPrepName defined."
        assert prepName, "Must define a sqlmeta getByIDPrepName for each table 
object passed into getTransaction."    
        dbNames = [col.dbName for col in t.sqlmeta.columnList]
        prep = "prepare %s (int) as select %s from %s where (%s.%s) = $1;" % \
            (t.sqlmeta.table + '_getByID', 
             ', '.join([x for x in dbNames]), 
             t.sqlmeta.table, 
             t.sqlmeta.table, 
             t.sqlmeta.idName)
        print "Table: %s, prep=%s." % (t.__name__, prep)
        result = con.query(prep) 
        print "Result: %s" % result

    return con

class Org(SQLObject):
    class sqlmeta:
        idName = 'OrgID'
        table  = 'Org'
        if runWith:
            getByIDPrepName = table + '_getByID'
        
    orgName = TextCol(dbName='orgName', default=None)
    users  = MultipleJoin('OrgUser', joinColumn='myOrgID')    

    
class OrgUser(SQLObject):
    class sqlmeta:
        idName = 'OrgUserID'
        table  = 'OrgUser'
        if runWith:
            getByIDPrepName = table + '_getByID'

    firstName = TextCol(dbName='firstName', default=None)
    myOrgID   = KeyCol(dbName='myOrgID', default=None, foreignKey="Org")    
   
    if lotsOfColumns:
        fieldNum01 = StringCol( dbName='fieldNum01', default='')
        fieldNum02 = StringCol( dbName='fieldNum02', default='')
        fieldNum03 = StringCol( dbName='fieldNum03', default='')
        fieldNum04 = StringCol( dbName='fieldNum04', default='')
        fieldNum05 = StringCol( dbName='fieldNum05', default='')
        fieldNum06 = StringCol( dbName='fieldNum06', default='')
        fieldNum07 = StringCol( dbName='fieldNum07', default='')
        fieldNum08 = StringCol( dbName='fieldNum08', default='')
        fieldNum09 = StringCol( dbName='fieldNum09', default='')
        fieldNum10 = StringCol( dbName='fieldNum10', default='')
        fieldNum11 = StringCol( dbName='fieldNum11', default='')
        fieldNum12 = StringCol( dbName='fieldNum12', default='')
        fieldNum13 = StringCol( dbName='fieldNum13', default='')
        fieldNum14 = StringCol( dbName='fieldNum14', default='')
        fieldNum15 = StringCol( dbName='fieldNum15', default='')
        fieldNum16 = StringCol( dbName='fieldNum16', default='')
        fieldNum17 = StringCol( dbName='fieldNum17', default='')
        fieldNum18 = StringCol( dbName='fieldNum18', default='')
        fieldNum19 = StringCol( dbName='fieldNum19', default='')
        fieldNum20 = StringCol( dbName='fieldNum20', default='')
        fieldNum21 = StringCol( dbName='fieldNum21', default='')
        fieldNum22 = StringCol( dbName='fieldNum22', default='')
        fieldNum23 = StringCol( dbName='fieldNum23', default='')
        fieldNum24 = StringCol( dbName='fieldNum24', default='')
        fieldNum25 = StringCol( dbName='fieldNum25', default='')
        fieldNum26 = StringCol( dbName='fieldNum26', default='')
        fieldNum27 = StringCol( dbName='fieldNum27', default='')
        fieldNum28 = StringCol( dbName='fieldNum28', default='')
        fieldNum29 = StringCol( dbName='fieldNum29', default='')
        fieldNum30 = StringCol( dbName='fieldNum30', default='')
        fieldNum31 = StringCol( dbName='fieldNum31', default='')
        fieldNum32 = StringCol( dbName='fieldNum32', default='')
        fieldNum33 = StringCol( dbName='fieldNum33', default='')
        fieldNum34 = StringCol( dbName='fieldNum34', default='')
        fieldNum35 = StringCol( dbName='fieldNum35', default='')
        fieldNum36 = StringCol( dbName='fieldNum36', default='')
        fieldNum37 = StringCol( dbName='fieldNum37', default='')
        fieldNum38 = StringCol( dbName='fieldNum38', default='')
        fieldNum39 = StringCol( dbName='fieldNum39', default='')
        fieldNum40 = StringCol( dbName='fieldNum40', default='')
        fieldNum41 = StringCol( dbName='fieldNum41', default='')
        fieldNum42 = StringCol( dbName='fieldNum42', default='')
        fieldNum43 = StringCol( dbName='fieldNum43', default='')
        fieldNum44 = StringCol( dbName='fieldNum44', default='')
        fieldNum45 = StringCol( dbName='fieldNum45', default='')
        fieldNum46 = StringCol( dbName='fieldNum46', default='')
        fieldNum47 = StringCol( dbName='fieldNum47', default='')
        fieldNum48 = StringCol( dbName='fieldNum48', default='')
        fieldNum49 = StringCol( dbName='fieldNum49', default='')
        fieldNum50 = StringCol( dbName='fieldNum50', default='')
  

t= getTransaction()
try:
    Org.dropTable(connection=t)
    OrgUser.dropTable(connection=t)
except:
    pass

t= getTransaction()
Org.createTable(connection=t)
OrgUser.createTable(connection=t)  

userNameList1 = '''bill bob dave ed john steve luke mark john 
matt joseph randy andy wendy carrie will michelle rebecca lisa mary'''.split()
orgNameList1  = '''Exxon Mobil Chevron Texaco Phillips66 BP Amoco 
Aramco Lukoil Gazprom 3M UBS Warburg IBM JCPenny NYSE CPSE ABC'''.split()

userNameList = range(1, 100)
orgNameList = orgNameList1

if runWith:
    t= getTransaction(tableList=[Org, OrgUser])
else:
    t= getTransaction()
    
#x = dieNow()
i = 0   
for oname in orgNameList:
    newOrg = Org(orgName=oname, connection=t)
    for x in range(1, 150):
        OrgUser(firstName="Name_%s" % x, myOrgID=newOrg.id, connection=t)

if runWith:
    t= getTransaction(tableList=[Org, OrgUser])
else:
    t= getTransaction()

from mx.DateTime import now, Date
start = now()
l = 0
print "starting at: ", start
for i in range(1, 30):
    #print "*" * 300
    if i % 10 == 0:
        print "i=%s" %i
    for thisOrg in Org.select(IN(Org.q.orgName, orgNameList), connection=t):
        string = "Have Org: %s, userslist=%s." % (thisOrg.orgName, \
            pformat([x.firstName for x in thisOrg.users]))
        l += len(string)
end = now()
print "ending at : %s elapsed = %s, l=%s" % (end, end - start, l)
# SQL statement for the same thing: 
# select firstname, orgName from Org, OrgUser where Org.orgID = OrgUser.myOrgID;
-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
sqlobject-discuss mailing list
sqlobject-discuss@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlobject-discuss

Reply via email to