
    Mother and Threads 
        or
    Mother Sessions


0- Why?

 When Mother is used, a persistent database connection is established
 and each query is executed inside this connection.

 The problem arises with a threaded enviroment: for example using
 Mother on Zope, on a web app, or generally with threads.

 The problem is very simple to understand: if we have two threads, 
 A and B, the following could happen: A begins a transaction, B 
 calls rollback() because it encountered an error. 

 The error on the thread B is a fatal error for A, because the
 transaction is rollbacked globally: each query executed by A will
 be removed.

 In a threaded environmente we need isolated sessions: A and B have
 to execute queries and to use transacations indipendently.



1- How?

 There are two way to use Sessions.
 We can create an isolated Session each time that we need it or
 we can use a Mother feature that allows to use a pool of persistent
 Sessions in a thread safe way.

 In the first case, each time that a Session is initialized, a new 
 database connection is established.

 In the second case, a pool of persistent connections is used.

 Obviously, the second way is better. 

 However, a Session is always initialized with the function:

    MySession= MotherSession()
 
 The behaviour of this function depends only on the Mother 
 configuration: edit conf.py to let Mother use the first or the
 second way (the file is heavy commented).

 Each session is transaction'ed: all the queries executed inside
 the session are not committed until the method endSession() is
 called:

    MySession= MotherSession()
    do_some_work()
    MySession.endSession()
 
 So, if we call the methods 'beginTrans', 'commit', 'rollback' of 
 some Mother instance, these calls will be ignored.

 IMPORTANT: after a call to endSession(), you must stop to  use 
 this Session! You have to initialize another Session.



2- The MotherSession Function

 We said that to initialize a Session, we use MotherSession().
 The function is defined in mothers.py, and accepts two
 optional arguments:

    MotherSession(name= None, pg_conn= None)

 If a connection pool is used, "pg_conn" is ignored.

 If you don't want to use the Mother Pool (but why?) you can pass
 a psycopg connection to the function and the new Session will use
 this connection: just use "pg_conn".
 Otherwise, a new connection will be established automatically.

 Each session has a name; this is very useful: debugging and reading 
 multi threaded process becomes more easy.

 By default, each Session is named: "MoSession-%d", where %d is a
 random integer.

 We can name our sessions with ergument "name".



3- Putting Mother Instances inside Sessions

 Once a session is initialized, we have to bind a Mother class
 with this session. The initialization of a Mother accepts the
 optional argument "session". If not specified, the persistent,
 globally defined, database connection will be used.

 Take the following declaration of a Mother class:

  from mother.mothers import *

  class ClsStars(DbMother):
    table_name= 'stars'
    def __init__(self, store= {}, flag= MO_NOA, session= None):
      DbMother.__init__(self, store, flag, session)

 When we do:

   Star= ClsStars(some_dict, some_flag)
 
 we are using the persistent, globally defined, no thread safe, 
 connection. To use this class inside a session is very easy:

   MySession= MotherSession('SessionExample')
   Star= ClsStars(some_dict, some_flag, session= MySession)
 
 Now, ths Star insance will use always the isolated session
 returned by MotherSession().

 Note that if we use MotherManager, for example to let ClsStars
 instances handle ClsPlanets, the Session is maintained:

   Star= ClsStars(some_dict, some_flag)
   Planet= Star.insertPlanets(dict(planet_name= 'some_planet'))
 
 Here, Earth borns inside the Session and, as Star, it will use
 this session to execute queries.



4- Mother Instances Methods

 Considere the following class:

  class ClsStars(DbMother):
    table_name= 'stars'

    def __init__(self, store= {}, flag= MO_NOA, session= None):
      DbMother.__init__(self, store, flag, session)

    def HandleLife(self, mydict):
      ClsLifes(mydict, MO_SAVE)

 The method HandleLife() accepts a dictionnary and inserts a new 
 record on the table lifes.

 But this method is really bad defined: HandleLife() will use 
 always the persistent, not thread safe, connection, altough
 the ClsStar instance is living inside a Session.

 To declare thread safe methods we have to know that when we
 use a Mother instance inside a Session, we can access the 
 session doing:

   MySession= MotherSession()
   Star= ClsStars(some_dict, some_flag, session= MySession)
   print Star.session== MySession

 In other words, when a Mother istance borns with a session,
 the session is stored in instance.session.
 If no session is used, instance.session is None.

 So, the solution to develop thread safe methods is the following:

  class ClsStars(DbMother):
    table_name= 'stars'

    def __init__(self, store= {}, flag= MO_NOA, session= None):
      DbMother.__init__(self, store, flag, session)

    def HandleLife(self, mydict):
      ClsLifes(mydict, MO_SAVE, session= self.session)

 If the ClsStars instance is using a session, HandleLife will
 use it. Otherwise the default, persistent connection will be
 used, because this is the behaviour when session is None.













