[mod_python] modpython, mysqldb best paractice

Jim Gallacher jpg at jgassociates.ca
Wed Jul 19 12:10:57 EDT 2006


Deron Meranda wrote:
> On 7/19/06, Martijn Moeling <martijn at xs4us.nu> wrote:
>> If mod python is running in Interpreter per directory mode, one
>> interpreter is created since all my content for mkbOK resides in / in
>> total over 1 in total over 14.000 different pages, and since we have
>> over 10.000 pageviews per day and aim for 100.000+ per day at the end of
>> the year I am preparing for a second server (which my system can handle)
>>
>> If mod python is running in Interpreter per directive mode I can and up
>> with god knows how many interpreters.
> 
> There's probably no need for you to use multiple python interpreters
> at all.  The only advantage is that it can provide you with some
> level of isolation (but not perfect); it will not provide any performance
> benifits (and actually is more likely to decrease performance somewhat).
> But as you're using it, you don't need more than one interpreter.  So just
> avoid all the PythonInter* directives.
> 
>> The register cleanup is clear now, since my system creates the database
>> connection in the class module (the init call creates the class) I might
>> have to alter that but.
> 
> Also don't forget about try:...finally:... blocks.  That's often the
> simplest way to make sure you clean up after something.
> 
> If the database connection is made inside your class, perhaps you
> should put a disconnect call in the class's destructor, __del__().  I
> don't know if you're using new-style classes, or traditional classes,
> but perhaps something like:
> 
>    class db_based_service(object):
>        def __init__(self):
>            self.db = None
>        def __del__(self):
>            self.disconnect()
>        def connect(self):
>            self.db = MySQLdb.connect( ..... )
>        def disconnect(self):
>            if self.db is not None:
>                self.db.disconnect()
>                self.db = None
>        def init(self):
>            self.connect()
> 
> Furthermore, if you're using transactions, you should make sure
> that you don't have any lingering open transactions.  If you're
> connecting and disconnecting on every request you probably don't
> need to worry quite so much.  But if you ever re-use or pool your
> database connections in the future, you may want to consider
> insuring that all your transactions get terminated at the end of
> the request.  Perhaps extending the framework to something like
> 
>    class ..... (same as above)
>        def __init__(self):
>            # same other stuff above
>            self.in_trans = False
>        def __del__(self):
>             self.rollback()
>             self.disconnect()
>        def start_transaction(self):
>            if self.in_trans:
>                raise RuntimeError("Attempted nested transaction")
>            self.db.begin()
>            self.in_trans = True
>        def commit(self):
>            if self.in_trans:
>                db.commit()
>                self.in_trans = False
>        def rollback(self):
>            if self.in_trans:
>                db.rollback()
>                self.in_trans = False
> 
> Of course you may want to see if too-many-connections or
> non-terminated transactions are even a problem.  Periodically
> run the mysql "show processlist" command.  Maybe even an
> occasional "show status" may be informative.
> 
>> The system goes from normal cpu utilization to 100% within a few
>> microseconds, and it happens now and then, sometimes within a few hours
>> after a reboot, sometimes it runs for weeks without trouble
> 
> Once it gets in that state will it ever eventually clear up?
> 
> Is your system going into an I/O paging fit?  Run the command
> "vmstat 5" and watch the "so" and "bo" columns for a minute.
> "so" should stay near 0, and "bo" should have faily low numbers
> (say <30), but really you should compare it against when the
> system is running okay.
> 
> Also run "top" and determine exactly which process(es) are
> charged with using the most cpu.
> 
>> I tried multiple cron thingies to investigate, but even cron slows down
>> so mutch that a "service httpd restart, and/or a service mysql restart"
>> take hours to complete,
> 
> Certainly sounds like heavy paging or swapping.
> 
>> in fact (but keep in mind I have had no interactive access) I think
>> mysql stops responding at all even to signals. I even tried "nice" in
>> the hope that mysql could not take 100% but that was not the case and it
>> slowed down the page building process (not surprised haha). Even
>> installing a second CPU did not help.
> 
>> The even more stupid thing is that this behavior does not happen on a
>> PIII 1 Ghz with a excact copy of the HDD (dd if=/dev/hd1 of=/dev/hd2)
>> Since the cpu in our production machine is 64 bit I suspected that, and
>> build apache, and mod_python and python all from scratch,.. no luck.
> 
> What about the amount of memory.  That can have an even bigger
> impact than the speed of the CPU.
> 
>> Different mysql versions did not matter too.
>>
>> The oddest thing is that after an update of my python code on the server
>> (new release of my system) is takes 1 or 2 days before it happens, than
>> it takes say 4 or 5 days, next it runs ok for weeks.
> 
> Perhaps you've got some suboptimal SQL.  For instance are you
> doing a lot of sorts, or very large joins?
> 
> Also what MySQL storage engine are you using?  InnoDB, or
> MyISAM, etc?

Also, I'm not sure if you told us anything about your OS, apache version
and mpm (prefork or worker) or mod_python version.

The fact is the mod_python has memory leaks. We are tracking them down -
3.2.8 is better than 3.1.4, and the next stable release will be better
yet. (Fixed a leak util.parse_qsl used in FieldStorage). Having
mod_python leak memory could impact mysql, causing heavy swapping when
trying to build a query result. Just a WAG and another thing to consider.

Jim



More information about the Mod_python mailing list