On Sun, May 27, 2018 at 1:34 PM, Dave Mittner <[email protected]> wrote:
>> so the stack trace shows that your application has *other* mappings in
>> it that are *not* coming from this automap block, which I assumed was
>> not the case.   I continue to have a very vague idea of the scope and
>> design of this application so I have to guess a lot.
>
>
> How are you figuring that?
> Every MySQL connection is coming strictly through the DB class
> I also have an Oracle connection that's established separately, but it's not
> doing any mapping at all (or shouldn't be) and only has a single SELECT to a
> stored procedure.

Upon further thought you're right, this is the segment of the trace in question:

File "/home/dmittner/loci2/app/api/mod_system/models.py", line 2281,
in update_organizations_from_oracle
    party_name=data['party_name']
  File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/session.py",
line 1909, in merge
    _resolve_conflict_map=_resolve_conflict_map)
  File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/session.py",
line 2019, in _merge
    for prop in mapper.iterate_properties:
  File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/mapper.py",
line 1920, in iterate_properties
    configure_mappers()
  File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/mapper.py",
line 3013, in configure_mappers
    mapper._post_configure_properties()
  File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/mapper.py",
line 1811, in _post_configure_properties
    prop.init()


which said to me, "configure_mappers() is finding unconfigured mappers
to configure", but that's the bug, that automap is still working while
this runs so a configure_mappers() call that should find nothing finds
unfinished work.

the final step is to confirm that the _CONFIGURE_MUTEX fix I gave you,
around the .prepare() step, fixes the problem.    The other mutex
logic that is there is not strictly necessary but still helpful to
prevent your program from running multple automaps against the same
cache id.




>
> I'll try the provided code.
>
> On Sun, May 27, 2018 at 9:31 AM, Mike Bayer <[email protected]>
> wrote:
>>
>> so the stack trace shows that your application has *other* mappings in
>> it that are *not* coming from this automap block, which I assumed was
>> not the case.   I continue to have a very vague idea of the scope and
>> design of this application so I have to guess a lot.
>>
>> What we will do is absolultely prevent automap from having a
>> non-mapped class in play while any part of the process might call
>> configure mappers.  This could also be patched into automap.
>>
>> Add this import to the top:
>>
>> from sqlalchemy.orm.mapperlib import _CONFIGURE_MUTEX
>>
>>
>> Change the prepare line to be like this:
>>
>>
>> _CONFIGURE_MUTEX.acquire()
>> try:
>>     base.prepare(
>>
>> name_for_scalar_relationship=name_for_scalar_relationship)
>> finally:
>>     _CONFIGURE_MUTEX.release()
>>
>>
>> This will prevent any configuration from occurring while automap
>> creates classes and maps them.   if that fixes the problem I can add
>> this to automap.prepare().
>>
>>
>>
>>
>>
>> On Sun, May 27, 2018 at 2:59 AM, Dave Mittner <[email protected]>
>> wrote:
>> > Well, thus far the locking/collecting solution doesn't seem to be doing
>> > the
>> > trick.
>> >
>> > Here's my revised code. Note that I haven't instituted any specified
>> > table
>> > exclusion, and my caching is temporarily disabled. Also note I made the
>> > automap_mutex a global as a static attribute on the DB class. If it's
>> > defined per-thread then the lock only applies for that thread, which
>> > kind of
>> > defeated the purpose.
>> >
>> >>             db_url = engine.url.URL(drivername = self.drivername,
>> >>                                     username = self.username,
>> >>                                     password = self.password,
>> >>                                     host = self.host,
>> >>                                     database = self.database,
>> >>                                     query = {'charset':'utf8'})
>> >>             self.engine =
>> >> create_engine(db_url,encoding='utf8',convert_unicode=True)
>> >>             self.session = Session(self.engine)
>> >>             self.connection = self.engine.connect()
>> >>             id = (self.drivername,self.host,self.database)
>> >>             app.logger.debug('Locking')
>> >>             DB.automap_mutex.acquire()
>> >>             app.logger.debug('Locked')
>> >>             try:
>> >>                 if id not in DB.tables:
>> >>                     base = automap_base()
>> >>                     try:
>> >>                         base.prepare(
>> >>                             self.engine,
>> >>                             reflect = True,
>> >>                             name_for_scalar_relationship =
>> >> name_for_scalar_relationship
>> >>                         )
>> >>                         configure_mappers()
>> >>                     except:
>> >>                         del base
>> >>                         gc.collect()
>> >>                         raise
>> >>                     else:
>> >>                         self.tables = base.classes
>> >>                         #DB.tables[id] = base.classes
>> >>             finally:
>> >>                 app.logger.debug('Unlocking')
>> >>                 DB.automap_mutex.release()
>> >>                 app.logger.debug('Unlocked')
>> >>             #self.tables = DB.tables[id]
>> >
>> >
>> > The locking does appear to be working. I can tell that much from the log
>> > outputs. Here are dual threads trying to establish connections within
>> > close
>> > proximity of each other.
>> >
>> > [2018-05-26 23:16:00,024] [23223/139778217465600] [DEBUG] CORE Locking
>> > [2018-05-26 23:16:00,025] [23223/139778217465600] [DEBUG] CORE Locked
>> > [2018-05-26 23:16:00,026] [23223/139777630271232] [DEBUG] CORE Locking
>> > [2018-05-26 23:16:00,392] [23223/139778217465600] [DEBUG] CORE Unlocking
>> > [2018-05-26 23:16:00,392] [23223/139778217465600] [DEBUG] CORE Unlocked
>> > [2018-05-26 23:16:00,392] [23223/139777630271232] [DEBUG] CORE Locked
>> > [2018-05-26 23:16:00,800] [23223/139777630271232] [DEBUG] CORE Unlocking
>> > [2018-05-26 23:16:00,800] [23223/139777630271232] [DEBUG] CORE Unlocked
>> >
>> > But the locking isn't preventing the initial "UnmappedClassError", nor
>> > the
>> > subsequent and repeating "InvalidRequestError: One or more mappers
>> > failed to
>> > initialize" exceptions.
>> >
>> >> Traceback (most recent call last):
>> >>   File "/home/dmittner/loci2/app/jobs.py", line 507, in start
>> >>     result = self.callable(self,**self.parameters)
>> >>   File "/home/dmittner/loci2/app/jobs.py", line 406, in
>> >> job_update_organizations
>> >>     Core(db=job.db).update_organizations_from_oracle()
>> >>   File "/home/dmittner/loci2/app/api/mod_system/models.py", line 2281,
>> >> in
>> >> update_organizations_from_oracle
>> >>     party_name=data['party_name']
>> >>   File
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/session.py",
>> >> line 1909, in merge
>> >>     _resolve_conflict_map=_resolve_conflict_map)
>> >>   File
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/session.py",
>> >> line 2019, in _merge
>> >>     for prop in mapper.iterate_properties:
>> >>   File
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/mapper.py",
>> >> line 1920, in iterate_properties
>> >>     configure_mappers()
>> >>   File
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/mapper.py",
>> >> line 3013, in configure_mappers
>> >>     mapper._post_configure_properties()
>> >>   File
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/mapper.py",
>> >> line 1811, in _post_configure_properties
>> >>     prop.init()
>> >>   File
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/interfaces.py",
>> >> line
>> >> 184, in init
>> >>     self.do_init()
>> >>   File
>> >>
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/relationships.py",
>> >> line 1655, in do_init
>> >>     self._process_dependent_arguments()
>> >>   File
>> >>
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/relationships.py",
>> >> line 1712, in _process_dependent_arguments
>> >>     self.target = self.mapper.mapped_table
>> >>   File
>> >>
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/util/langhelpers.py",
>> >> line 767, in __get__
>> >>     obj.__dict__[self.__name__] = result = self.fget(obj)
>> >>   File
>> >>
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/relationships.py",
>> >> line 1634, in mapper
>> >>     configure=False)
>> >>   File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/base.py",
>> >> line 426, in class_mapper
>> >>     raise exc.UnmappedClassError(class_)
>> >> sqlalchemy.orm.exc.UnmappedClassError: Class
>> >> 'sqlalchemy.ext.automap.geo_country_map_data' is not mapped
>> >
>> >
>> >
>> >> Traceback (most recent call last):
>> >>   File "/home/dmittner/loci2/app/jobs.py", line 507, in start
>> >>     result = self.callable(self,**self.parameters)
>> >>   File "/home/dmittner/loci2/app/jobs.py", line 434, in
>> >> job_update_geo_database
>> >>     Core(db=job.db).update_geo()
>> >>   File "/home/dmittner/loci2/app/api/mod_system/models.py", line 4018,
>> >> in
>> >> update_geo
>> >>     database = app.config['GEO_SQL_DB']
>> >>   File "/home/dmittner/loci2/app/api/mod_system/models.py", line 790,
>> >> in
>> >> __init__
>> >>     configure_mappers()
>> >>   File
>> >> "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/mapper.py",
>> >> line 3010, in configure_mappers
>> >>     raise e
>> >> sqlalchemy.exc.InvalidRequestError: One or more mappers failed to
>> >> initialize - can't proceed with initialization of other mappers.
>> >> Triggering
>> >> mapper: 'Mapper|geo_country_label_data|geo_country_label_data'.
>> >> Original
>> >> exception was: Class 'sqlalchemy.ext.automap.geo_country_map_data' is
>> >> not
>> >> mapped
>> >
>> >
>> > Based on the above code I really don't think I'd have any references to
>> > anything persisting. My only thought was that maybe the engine is being
>> > given a reference when it's passed into prepare(), but I didn't notice
>> > anything like that when I glanced at the source code.
>> >
>> >
>> > On Sat, May 26, 2018 at 3:47 PM, Mike Bayer <[email protected]>
>> > wrote:
>> >>
>> >>
>> >>
>> >> On Sat, May 26, 2018, 1:49 PM Dave Mittner <[email protected]>
>> >> wrote:
>> >>>>
>> >>>>  # 3. configure now, instead of waiting to do it during a query.
>> >>>
>> >>>
>> >>> That right there is the reason this problem seems so hard to track
>> >>> down.
>> >>> I wasn't seeing any error at connection-time, but rather at query
>> >>> time. And
>> >>> that query could be triggered on any connection, in any area of the
>> >>> code. In
>> >>> theory it wouldn't even necessarily happen at the same time that
>> >>> connections
>> >>> are being established. So I'd get an error at Point A when the root
>> >>> cause
>> >>> actually tracks back to Point B, and this was made possibly because of
>> >>> the
>> >>> use of the global  _mapper_registry; it enabled a problem based on one
>> >>> connection to trigger an error in another connection.
>> >>>
>> >>> So by locking threading and triggering the error via
>> >>> configure_mappers(),
>> >>> I'm able to see the error within the proper connection context.
>> >>>
>> >>> I guess what I don't understand is why you do the mapping at
>> >>> query-time
>> >>> and not connection-time.
>> >>
>> >>
>> >> Well this is not "the mapping", this is the point at which the
>> >> disparate
>> >> mappers all over the application are configured against each other,
>> >> namely
>> >> the relationships defined between them as well as any event handlers
>> >> that
>> >> work post-configure.   The the configure_mappers() step is called
>> >> automatically both when a query is built up as well as when a mapped
>> >> object
>> >> is constructed.  Both of these things happen before any database
>> >> connection
>> >> is necessarily present.
>> >>
>> >> It is not uncommon that an application that has a well-defined point at
>> >> which things start up would call configure_mappers explicitly, which
>> >> fits
>> >> with what you're doing.   SQLAlchemy could also have decided to make
>> >> this
>> >> explicitness required, but we've made it automatic and usually with no
>> >> problems.     But the case I think you've found here warrants some
>> >> consideration, since automap does create mappings within a function,
>> >> rather
>> >> than at module import time as is more typical.
>> >>
>> >>
>> >>
>> >>> With the "base.classes" attribute being immediately available after
>> >>> base.prepare, I believed the mapping was already being done at
>> >>> connection-time, and therefor there should have been an exception
>> >>> throwing
>> >>> then, and this problem wouldn't exist. Of course you might also need
>> >>> to
>> >>> detect if threading is being used and lock it, but this could all be
>> >>> behavior triggered by a threading=True parameter on the "base.prepare"
>> >>> call.
>> >>>
>> >>> Anyway, I'll apply these conventions and see how it goes.
>> >>>
>> >>> On Sat, May 26, 2018 at 9:44 AM, Mike Bayer <[email protected]>
>> >>> wrote:
>> >>>>
>> >>>> On Sat, May 26, 2018 at 12:19 AM, Dave Mittner
>> >>>> <[email protected]>
>> >>>> wrote:
>> >>>> >> Can you elaborate on this system?   There's some open source code
>> >>>> >> that
>> >>>> >> creates SQLAlchemy mappings which you have no control over ?
>> >>>> >> What
>> >>>> >> do
>> >>>> >> you mean "on the database" ?
>> >>>> >
>> >>>> >
>> >>>> > I mean something must be happening on the database server, itself,
>> >>>> > causing
>> >>>> > SQLAlchemy's automap process to fail. Or there's a network problem.
>> >>>> > Or
>> >>>> > somewhere else in the line. I can say with certainty that there are
>> >>>> > no
>> >>>> > conditions in my application that vary from connection to
>> >>>> > connection.
>> >>>> > Every
>> >>>> > attempt to connect in my code is identical, yet sometimes it fails.
>> >>>> >
>> >>>> > And even if there were a race condition causing a mapping to fail,
>> >>>> > that
>> >>>> > still shouldn't prevent all future attempts by the application to
>> >>>> > automap
>> >>>> > other databases. This is the inherent problem of using the global
>> >>>> > space --
>> >>>> > it affects the entire application and not just one instantiation of
>> >>>> > a
>> >>>> > class.
>> >>>> > Each connection/engine isn't isolated from each other, so a failure
>> >>>> > on
>> >>>> > any
>> >>>> > one can impact others. A bug in one module of an application can
>> >>>> > shut
>> >>>> > down
>> >>>> > the entire application indefinitely.
>> >>>> >
>> >>>> > That brings us to this comment you made in one of your examples:
>> >>>> >
>> >>>> >> the program is not expected to be able to run with bad mappings in
>> >>>> >> it.
>> >>>> >
>> >>>> >
>> >>>> > But why not just allow exceptions to serve their purpose? If you
>> >>>> > throw
>> >>>> > an
>> >>>> > exception when a mapping fails, rather than keeping it stored in
>> >>>> > _mapper_registry in a failed state, then other connections are free
>> >>>> > to
>> >>>> > function. A failed mapping will only cause issues with the one
>> >>>> > connection
>> >>>> > and only with other mappings that reference it. My application, for
>> >>>> > example,
>> >>>> > has no such mappings that would fail, yet my application is unable
>> >>>> > to
>> >>>> > proceed with other functions due to how the mapping failure is
>> >>>> > handled.
>> >>>>
>> >>>>
>> >>>> I understand the temptation to see that this part of the mapping
>> >>>> system uses a global registry and then jump ahead to the common
>> >>>> advice
>> >>>> that "global variables are bad", and therefore this is an
>> >>>> inappropriate architecture.   However, there is a context, a history,
>> >>>> and a purpose for why the system is architected this way, and I've
>> >>>> shared an example program I wrote to illustrate one particular reason
>> >>>> why this is, however I didn't provide a full narrative explanation.
>> >>>>
>> >>>> I'll also note again that automap itself is not the standard way
>> >>>> SQLAlchemy was intended to be used, and if the ORM were entirely
>> >>>> focused on automap from the ground up, the mapper configuration
>> >>>> process would likely work differently.   In particular, your
>> >>>> references to "connections/engines not isolated from each other"
>> >>>> illustrates a misunderstanding that I am assuming originates from
>> >>>> looking at things in an "automap" perspective, because there is not
>> >>>> actually any relationship between a SQLAlchemy engine and a class
>> >>>> mapping in any way whatsoever.  A network error cannot cause the
>> >>>> problem you describe, for example, because the reflection process
>> >>>> completes fully before any mappers are constructed.  automap itself
>> >>>> does not anticipate a concurrency use case as you are doing, so at
>> >>>> the
>> >>>> very least the documentation needs to include examples on how to
>> >>>> accommodate for this use case which likely would have prevented this
>> >>>> issue from happening in the first place.
>> >>>>
>> >>>> I'll reiterate as well that the reflection process here can be
>> >>>> tailored to exclude tables you don't care about, and that you can
>> >>>> remove failed mappers from _mapper_registry as demonstrated in a
>> >>>> second example program I've shared, however I perhaps have not stated
>> >>>> clearly enough that this should be within the scope of a cleanup
>> >>>> process that you take when mappings fail.   That your program starts
>> >>>> to work eventually is very likely because Python's garbage collector
>> >>>> eventually runs.  My suggestion is to run it immediately in the
>> >>>> failure case.
>> >>>>
>> >>>> Within the scope of suggestions I've made like using mutexes,
>> >>>> limiting
>> >>>> the reflection process, and using garbage collection, these are all
>> >>>> efforts to provide things you can try to help solve your problem.  In
>> >>>> an effort to make this as easy as possible, below is the snippet of
>> >>>> code you've given me with these techniques integrated, including
>> >>>> comments with my latest thinking on what exactly the problem you are
>> >>>> seeing likely is.   I hope that you can work with these techniques to
>> >>>> see if it solves your issue.
>> >>>>
>> >>>> from sqlalchemy.orm import configure_mappers
>> >>>> import threading
>> >>>> import gc
>> >>>>
>> >>>> automap_mutex = threading.Lock()
>> >>>>
>> >>>> def automap_mapping_thing():
>> >>>>     db_url = engine.url.URL(drivername = self.drivername,
>> >>>>                                     username = self.username,
>> >>>>                                     password = self.password,
>> >>>>                                     host = self.host,
>> >>>>                                     database = self.database,
>> >>>>                                     query = {'charset':'utf8'})
>> >>>>     self.engine =
>> >>>> create_engine(db_url,encoding='utf8',convert_unicode=True)
>> >>>>     self.session = Session(self.engine)
>> >>>>
>> >>>>     self.connection = self.engine.connect()
>> >>>>
>> >>>>     # Automap functionality has a lot of overhead, so cache the
>> >>>> results on a per-host/database basis
>> >>>>     id = (self.drivername,self.host,self.database)
>> >>>>
>> >>>>     try:
>> >>>>         # look in the dictionary.   99% of the time the id will be
>> >>>>         # present, since this is a cache, so don't waste time with
>> >>>> 'in'
>> >>>> check
>> >>>>         self.tables = DB.tables[id]
>> >>>>     except KeyError:
>> >>>>         # more expensive KeyError catch for the 1% of the time the id
>> >>>>         # isn't there.  also allows the check for the key to be more
>> >>>> or less atomic
>> >>>>
>> >>>>         # step 1, lock.   The race you are getting is, one automap
>> >>>> process
>> >>>>         # has created a class but not mapped it yet, while some other
>> >>>> thread
>> >>>>         # calls configure_mappers() which hits it.   automap does not
>> >>>>         # anticipate this use case right now.
>> >>>>         automap_mutex.acquire()
>> >>>>
>> >>>>         # try/finally block for the mutex
>> >>>>         try:
>> >>>>
>> >>>>             # 1. since we are mutexing, multiple threads may have
>> >>>> reached
>> >>>>             # here, and we might not be the first, so, check if
>> >>>> someone
>> >>>> already
>> >>>>             # did the work:
>> >>>>
>> >>>>             if id in DB.tables:
>> >>>>                 return
>> >>>>
>> >>>>             # 2. reflect tables, exclude tables you don't need
>> >>>>             def exclude_tables_we_dont_need(table_name, metadata):
>> >>>>                 return table_name not in (
>> >>>>                     'geo_state_map_data', 'other_table', 'etc')
>> >>>>
>> >>>>                 # or, inclusive
>> >>>>                 # return table_name in (
>> >>>>                 #    'table_i_care_about',
>> >>>> 'other_table_i_care_about')
>> >>>>
>> >>>>             # do the reflection
>> >>>>             base = automap_base()
>> >>>>             base.metadata.reflect(
>> >>>>                 self.connection, only=exclude_tables_we_dont_need)
>> >>>>
>> >>>>             # try/except block for ensuring bad mappers are removed
>> >>>>             try:
>> >>>>                 # 2. prepare mappings from reflected tables.
>> >>>>                 base.prepare(
>> >>>>
>> >>>> name_for_scalar_relationship=name_for_scalar_relationship)
>> >>>>
>> >>>>                 # 3. configure now, instead of waiting to do it
>> >>>> during a
>> >>>> query.
>> >>>>                 configure_mappers()
>> >>>>             except:
>> >>>>
>> >>>>                 # 4. something went wrong w/ the prepare or
>> >>>> configure.
>> >>>>                 # clean up
>> >>>>
>> >>>>                 log.error("Error occurred", exc_info=True)
>> >>>>
>> >>>>                 # remove all the new mappers we just made from memory
>> >>>>                 del base
>> >>>>
>> >>>>                 # gc collect.  _mapper_registry will be emptied of
>> >>>> these
>> >>>>                 # mappers
>> >>>>                 gc.collect()
>> >>>>
>> >>>>                 # re-raise, or not.  whatever you want to do.
>> >>>>                 raise
>> >>>>             else:
>> >>>>                 # fully mapped classes ready to go.
>> >>>>                 DB.tables[id] = base.classes
>> >>>>         finally:
>> >>>>             # leave critical section
>> >>>>             automap_mutex.release()
>> >>>>
>> >>>>
>> >>>>
>> >>>>
>> >>>>
>> >>>> >
>> >>>> >
>> >>>> > In any case, I have my application attempting the mapping anew
>> >>>> > every
>> >>>> > minute
>> >>>> > and it's gone for a few hours now without the problem being
>> >>>> > triggered.
>> >>>> > I'm
>> >>>> > really just shooting in the dark here, though. I'm hoping that I
>> >>>> > can
>> >>>> > find
>> >>>> > something else going on at the same time as the failure to explain
>> >>>> > what's
>> >>>> > triggering it, but I'm not hopeful. And without knowing how it's
>> >>>> > being
>> >>>> > triggered, it's nigh impossible to intentionally trigger it to test
>> >>>> > fixes or
>> >>>> > bypasses. That's why my focus has been on the underlying conceptual
>> >>>> > issue of
>> >>>> > SQLAlchemy's error handling method blocking things altogether. And
>> >>>> > conceptually, I don't think it should matter what causes an error;
>> >>>> > as
>> >>>> > a
>> >>>> > matter of principle I think the existing mechanism is presumptuous
>> >>>> > in
>> >>>> > not
>> >>>> > allowing any subsequent mappers from working.
>> >>>> >
>> >>>> >
>> >>>> > On Fri, May 25, 2018 at 2:54 PM, Mike Bayer
>> >>>> > <[email protected]>
>> >>>> > wrote:
>> >>>> >>
>> >>>> >> On Fri, May 25, 2018 at 3:21 PM, Dave Mittner
>> >>>> >> <[email protected]>
>> >>>> >> wrote:
>> >>>> >> >> This statement does not make sense without more information.
>> >>>> >> >> automap
>> >>>> >> >> either succeeds against a given database, or it fails.  It is
>> >>>> >> >> not
>> >>>> >> >> easy
>> >>>> >> >> to produce a situation where it fails "only rarely".    Things
>> >>>> >> >> that
>> >>>> >> >> could cause this are: 1. the database you are automapping
>> >>>> >> >> against
>> >>>> >> >> is
>> >>>> >> >> constantly changing,   2. the mapping is relying upon some
>> >>>> >> >> subtle
>> >>>> >> >> hash
>> >>>> >> >> ordering or 3. automap is being used in a multi-threaded
>> >>>> >> >> context
>> >>>> >> >> where
>> >>>> >> >> more than one automap tries to hit the same tables at the same
>> >>>> >> >> time.
>> >>>> >> >> When something happens "only rarely", that points to race
>> >>>> >> >> conditions
>> >>>> >> >> or sometimes hash ordering issues.     To rule out #2, you
>> >>>> >> >> would
>> >>>> >> >> create a test script that does your automap by itself and try
>> >>>> >> >> to
>> >>>> >> >> run
>> >>>> >> >> automap hundreds of times against the particular database that
>> >>>> >> >> caused
>> >>>> >> >> a failure, if under Python 2 you'd want to set
>> >>>> >> >> PYTHONHASHSEED='random'
>> >>>> >> >> in the environment.    But the error you showed looks more like
>> >>>> >> >> a
>> >>>> >> >> race
>> >>>> >> >> of two automaps hitting the same table.
>> >>>> >> >
>> >>>> >> >
>> >>>> >> > That's my point though, if there's a condition causing the
>> >>>> >> > failure,
>> >>>> >> > it's
>> >>>> >> > not
>> >>>> >> > in my code. It's on the database. A third party system.
>> >>>> >>
>> >>>> >> Can you elaborate on this system?   There's some open source code
>> >>>> >> that
>> >>>> >> creates SQLAlchemy mappings which you have no control over ?
>> >>>> >> What
>> >>>> >> do
>> >>>> >> you mean "on the database" ?
>> >>>> >>
>> >>>> >>
>> >>>> >> >
>> >>>> >> > It comes down to this: SQLAlchemy doesn't handle that kind of
>> >>>> >> > problem
>> >>>> >> > gracefully.
>> >>>> >>
>> >>>> >> I love to support new use cases if I can get a complete definition
>> >>>> >> of
>> >>>> >> what it is you're trying to do and why my suggestions can't be
>> >>>> >> taken.
>> >>>> >>  You showed me the code that is causing the problem, and I
>> >>>> >> suggested
>> >>>> >> using a mutex and I can show you specifically how to fix what
>> >>>> >> might
>> >>>> >> be
>> >>>> >> causing the problem.   Your description of this code is "Here's
>> >>>> >> the
>> >>>> >> gist of my connection initiation code that's run upon
>> >>>> >> instantiation
>> >>>> >> of
>> >>>> >> the DB class. Mind you, a prior coworker wrote this and there's so
>> >>>> >> many ways of interfacing with SQLAlchemy that I'm not even sure
>> >>>> >> where
>> >>>> >> he got it from."   - now you're saying this is third party code
>> >>>> >> you
>> >>>> >> can't change?
>> >>>> >>
>> >>>> >>
>> >>>> >> > If something happens in an external system that causes problems
>> >>>> >> > in mine, that's something I should be able to handle and move
>> >>>> >> > on.
>> >>>> >> > If the
>> >>>> >> > same problem happens 10,000 times, every time I try it, that's
>> >>>> >> > fine.
>> >>>> >> > I'll
>> >>>> >> > catch the exception, log it, abort that thread, and other
>> >>>> >> > processes
>> >>>> >> > will
>> >>>> >> > continue on. Connections to other databases will still function.
>> >>>> >> >
>> >>>> >> > But that isn't the case here. One failure prevents all future
>> >>>> >> > mapping
>> >>>> >> > operations, even to other databases that would work.
>> >>>> >>
>> >>>> >> if you pull in third party code that places an event within
>> >>>> >> SQLAlchemy
>> >>>> >> against all new mappings, and that code is broken and fails, it
>> >>>> >> will
>> >>>> >> break your entire application.  It is not controversial that
>> >>>> >> installing broken code in your applciation in the same process
>> >>>> >> space
>> >>>> >> will break that process altogether.  Perhaps I have not been
>> >>>> >> clear,
>> >>>> >> but it is not normal for automap to "fail occasionally".  If used
>> >>>> >> correctly, it will not fail.  I've offered to help you repair that
>> >>>> >> system.
>> >>>> >>
>> >>>> >>
>> >>>> >> >
>> >>>> >> > Regardless, I don't want the development overhead of having to
>> >>>> >> > maintain
>> >>>> >> > static mappings and I don't have the time or manpower to go back
>> >>>> >> > and
>> >>>> >> > replace
>> >>>> >> > ORM use with more direct query text building,
>> >>>> >>
>> >>>> >> This has not been suggested as your only option.
>> >>>> >>
>> >>>> >> > so I'll probably just dig in
>> >>>> >> > and find a way to undo what SQLAlchemy is doing in the global
>> >>>> >> > space
>> >>>> >> > --
>> >>>> >> > maybe
>> >>>> >> > remove any mappers in _mapper_registry that have
>> >>>> >> > _configure_failed
>> >>>> >> > as
>> >>>> >> > true,
>> >>>> >> > when these exceptions occur, since that seems to be precisely
>> >>>> >> > what's
>> >>>> >> > preventing subsequent attempts from even being tried.
>> >>>> >>
>> >>>> >> That's a system that's been that way for about 9 years, and like
>> >>>> >> any
>> >>>> >> system it certainly can be changed if a new use case is introduced
>> >>>> >> which has no reasonable alternative, but to undertake such a
>> >>>> >> change
>> >>>> >> would need a well defined rationale which I haven't gotten here.
>> >>>> >>
>> >>>> >>
>> >>>> >>
>> >>>> >>
>> >>>> >> >
>> >>>> >> > On Fri, May 25, 2018 at 6:44 AM, Mike Bayer
>> >>>> >> > <[email protected]>
>> >>>> >> > wrote:
>> >>>> >> >>
>> >>>> >> >> Here's a second demo why _mapper_registry exists and has to be
>> >>>> >> >> global:
>> >>>> >> >>
>> >>>> >> >> from sqlalchemy import *
>> >>>> >> >> from sqlalchemy.orm import *
>> >>>> >> >> from sqlalchemy.ext.declarative import declarative_base
>> >>>> >> >> from sqlalchemy import inspect
>> >>>> >> >>
>> >>>> >> >> Base = declarative_base()
>> >>>> >> >>
>> >>>> >> >>
>> >>>> >> >> class A(Base):
>> >>>> >> >>     __tablename__ = 'a'
>> >>>> >> >>
>> >>>> >> >>     id = Column(Integer, primary_key=True)
>> >>>> >> >>     data = Column(String)
>> >>>> >> >>
>> >>>> >> >>
>> >>>> >> >> class B(Base):
>> >>>> >> >>     __tablename__ = 'b'
>> >>>> >> >>
>> >>>> >> >>     id = Column(Integer, primary_key=True)
>> >>>> >> >>     aid = Column(ForeignKey('a.id'))
>> >>>> >> >>     data = Column(String)
>> >>>> >> >>     a = relationship("A", backref="bs")
>> >>>> >> >>
>> >>>> >> >>
>> >>>> >> >> e = create_engine("sqlite://", echo=True)
>> >>>> >> >> Base.metadata.create_all(e)
>> >>>> >> >>
>> >>>> >> >> # let's assume SQLAlchemy did not keep a global list of all
>> >>>> >> >> mappers.
>> >>>> >> >> # let's take away "configure_mappers" and instead only
>> >>>> >> >> configure
>> >>>> >> >> mappers
>> >>>> >> >> # as the program refers to them.   Patch out configure_mappers
>> >>>> >> >> to
>> >>>> >> >> do
>> >>>> >> >> nothing.
>> >>>> >> >> from sqlalchemy.orm import mapperlib
>> >>>> >> >> mapperlib.configure_mappers = lambda: None
>> >>>> >> >>
>> >>>> >> >>
>> >>>> >> >> # here's the specific steps configure_mappers() needs to do for
>> >>>> >> >> a
>> >>>> >> >> particular
>> >>>> >> >> # mapper.
>> >>>> >> >> def configure_mapper(cls):
>> >>>> >> >>     a_mapper = inspect(cls).mapper
>> >>>> >> >>     a_mapper._post_configure_properties()
>> >>>> >> >>     a_mapper._expire_memoizations()
>> >>>> >> >>     a_mapper.dispatch.mapper_configured(
>> >>>> >> >>         a_mapper, a_mapper.class_)
>> >>>> >> >>
>> >>>> >> >> # about to use "A".  Let's configure:
>> >>>> >> >> configure_mapper(A)
>> >>>> >> >> a1 = A()
>> >>>> >> >>
>> >>>> >> >> # check the a1.bs collection.   Except.  There isn't one :)
>> >>>> >> >> mappers
>> >>>> >> >> often
>> >>>> >> >> # contain instructions to add attributes to other mappers.  if
>> >>>> >> >> the
>> >>>> >> >> program
>> >>>> >> >> # refers to those dependent mappers first, we need to have
>> >>>> >> >> configured
>> >>>> >> >> # all mappers.   if one of the mappers is failing, that's a bug
>> >>>> >> >> in
>> >>>> >> >> the
>> >>>> >> >> program.
>> >>>> >> >> # the program is not expected to be able to run with bad
>> >>>> >> >> mappings
>> >>>> >> >> in
>> >>>> >> >> it.
>> >>>> >> >> assert a1.bs == []
>> >>>> >> >>
>> >>>> >> >> # program has failed
>> >>>> >> >>
>> >>>> >> >> # maybe we could use the "Base" here as the collection, not
>> >>>> >> >> global
>> >>>> >> >> _mapper_registry.
>> >>>> >> >> # but we support relationships and other linkages between
>> >>>> >> >> mappers
>> >>>> >> >> that
>> >>>> >> >> have different
>> >>>> >> >> # "bases", and declarative is only an extension to the core
>> >>>> >> >> mapping
>> >>>> >> >> system in any case.
>> >>>> >> >> # perhaps a "SQLAlchemy 2" can tighten and modernize this
>> >>>> >> >> system
>> >>>> >> >> so
>> >>>> >> >> that all mappers
>> >>>> >> >> # are truly segmented into independent namespaces but this can
>> >>>> >> >> also
>> >>>> >> >> cause more confusion
>> >>>> >> >> # too.   Overall, programs just aren't expected to have invalid
>> >>>> >> >> mappings.
>> >>>> >> >>
>> >>>> >> >>
>> >>>> >> >> a1bs = a1.bs
>> >>>> >> >>
>> >>>> >> >> # about to use "B". Let's configure:
>> >>>> >> >> configure_mapper(B)
>> >>>> >> >> a1bs.append(B())
>> >>>> >> >>
>> >>>> >> >>
>> >>>> >> >>
>> >>>> >> >> On Fri, May 25, 2018 at 9:24 AM, Mike Bayer
>> >>>> >> >> <[email protected]>
>> >>>> >> >> wrote:
>> >>>> >> >> > Here's a demo, you want to also call gc_collect() when you
>> >>>> >> >> > dispose of
>> >>>> >> >> > your failed mappers:
>> >>>> >> >> >
>> >>>> >> >> > from sqlalchemy import *
>> >>>> >> >> > from sqlalchemy.orm import *
>> >>>> >> >> > from sqlalchemy.ext.declarative import declarative_base
>> >>>> >> >> > from sqlalchemy import exc
>> >>>> >> >> >
>> >>>> >> >> > Base = declarative_base()
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> > class A(Base):
>> >>>> >> >> >     __tablename__ = 'a'
>> >>>> >> >> >
>> >>>> >> >> >     id = Column(Integer, primary_key=True)
>> >>>> >> >> >     data = Column(String)
>> >>>> >> >> >     bs = relationship("B")
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> > class B(Base):
>> >>>> >> >> >     __tablename__ = 'b'
>> >>>> >> >> >
>> >>>> >> >> >     id = Column(Integer, primary_key=True)
>> >>>> >> >> >     aid = Column(ForeignKey('a.id'))
>> >>>> >> >> >     data = Column(String)
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> > # bad mapper
>> >>>> >> >> > class C(Base):
>> >>>> >> >> >     __tablename__ = 'c'
>> >>>> >> >> >
>> >>>> >> >> >     id = Column(Integer, primary_key=True)
>> >>>> >> >> >
>> >>>> >> >> >     # nonexistent relationship
>> >>>> >> >> >     q = relationship("q")
>> >>>> >> >> >
>> >>>> >> >> > try:
>> >>>> >> >> >     configure_mappers()
>> >>>> >> >> > except exc.InvalidRequestError as err:
>> >>>> >> >> >     print("First exception: %s" % err)
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> > class D(Base):
>> >>>> >> >> >     __tablename__ = 'd'
>> >>>> >> >> >     id = Column(Integer, primary_key=True)
>> >>>> >> >> >     aid = Column(ForeignKey('a.id'))
>> >>>> >> >> >     a = relationship("A")
>> >>>> >> >> >
>> >>>> >> >> > # other mappers are blocked
>> >>>> >> >> > try:
>> >>>> >> >> >     Session().query(D)
>> >>>> >> >> > except exc.InvalidRequestError as err:
>> >>>> >> >> >     print("Second exception: %s" % err)
>> >>>> >> >> >
>> >>>> >> >> > # delete offending class
>> >>>> >> >> > del C
>> >>>> >> >> >
>> >>>> >> >> > # garbage collect, as mappings contain cycles
>> >>>> >> >> > import gc
>> >>>> >> >> > gc.collect()
>> >>>> >> >> >
>> >>>> >> >> > # mapper is gone
>> >>>> >> >> > print(Session().query(D))
>> >>>> >> >> > print("it worked!")
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> >
>> >>>> >> >> > On Fri, May 25, 2018 at 9:16 AM, Mike Bayer
>> >>>> >> >> > <[email protected]>
>> >>>> >> >> > wrote:
>> >>>> >> >> >> On Fri, May 25, 2018 at 7:11 AM, Dave Mittner
>> >>>> >> >> >> <[email protected]>
>> >>>> >> >> >> wrote:
>> >>>> >> >> >>>> So that is already a new fact (which i sort of guessed
>> >>>> >> >> >>>> might
>> >>>> >> >> >>>> be in
>> >>>> >> >> >>>> play) that this is a multi-tenant system.  How many
>> >>>> >> >> >>>> databases
>> >>>> >> >> >>>> are
>> >>>> >> >> >>>> we
>> >>>> >> >> >>>> talking about that are accessed by a single application?
>> >>>> >> >> >>>> What do
>> >>>> >> >> >>>> the
>> >>>> >> >> >>>> tables in these databases look like, are they all the same
>> >>>> >> >> >>>> across
>> >>>> >> >> >>>> all
>> >>>> >> >> >>>> DBs (in which case use fixed mappings) or are they totally
>> >>>> >> >> >>>> different?
>> >>>> >> >> >>>> if all the DBs have the same table structure then you
>> >>>> >> >> >>>> should
>> >>>> >> >> >>>> use
>> >>>> >> >> >>>> only
>> >>>> >> >> >>>> a single table/mapping structure for each DB.
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>> There's probably something like 5 or 6 distinct MySQL
>> >>>> >> >> >>> databases I
>> >>>> >> >> >>> connect
>> >>>> >> >> >>> to, each on a separate host, each unique. That's why I
>> >>>> >> >> >>> cache
>> >>>> >> >> >>> the
>> >>>> >> >> >>> automapped
>> >>>> >> >> >>> classes on a per-server-per-host basis..
>> >>>> >> >> >>
>> >>>> >> >> >> the critical questions are:   1. do each of these databases
>> >>>> >> >> >> have the
>> >>>> >> >> >> same table structures?    or if not 2. are these *fixed*
>> >>>> >> >> >> structures
>> >>>> >> >> >> that you could map statically without using reflection?
>> >>>> >> >> >>
>> >>>> >> >> >>
>> >>>> >> >> >>
>> >>>> >> >> >>>
>> >>>> >> >> >>>> I've never seen that before but I might guess that you
>> >>>> >> >> >>>> have
>> >>>> >> >> >>>> multiple threads reflecting tables and creating classes of
>> >>>> >> >> >>>> the
>> >>>> >> >> >>>> identical name in different threads at the same time?
>> >>>> >> >> >>>> You
>> >>>> >> >> >>>> definitely
>> >>>> >> >> >>>> can't do that without modifying how the
>> >>>> >> >> >>>> sqlalchemy.orm.mapper._mapper_registry works.   You need
>> >>>> >> >> >>>> to
>> >>>> >> >> >>>> either
>> >>>> >> >> >>>> ensure these names are unique at all times, and if you
>> >>>> >> >> >>>> expect
>> >>>> >> >> >>>> multiple
>> >>>> >> >> >>>> threads to access the same names, you need to use a mutex
>> >>>> >> >> >>>> to
>> >>>> >> >> >>>> prevent
>> >>>> >> >> >>>> them from doing so concurrently.
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>> I'm not entirely sure I understand what you're getting at
>> >>>> >> >> >>> here.
>> >>>> >> >> >>> Before
>> >>>> >> >> >>> I
>> >>>> >> >> >>> even added the caching, I was fully able to automap the
>> >>>> >> >> >>> same
>> >>>> >> >> >>> database
>> >>>> >> >> >>> over
>> >>>> >> >> >>> and over again on each new connection without any issue.
>> >>>> >> >> >>> And
>> >>>> >> >> >>> it
>> >>>> >> >> >>> functions
>> >>>> >> >> >>> perfectly fine now with multiple threads running multiple
>> >>>> >> >> >>> connections
>> >>>> >> >> >>> to the
>> >>>> >> >> >>> same database. The only problem I'm having is after an
>> >>>> >> >> >>> automap
>> >>>> >> >> >>> fails
>> >>>> >> >> >>> to
>> >>>> >> >> >>> properly read a database structure and map it to classes,
>> >>>> >> >> >>> which
>> >>>> >> >> >>> happens only
>> >>>> >> >> >>> rarely.
>> >>>> >> >> >>
>> >>>> >> >> >> This statement does not make sense without more information.
>> >>>> >> >> >> automap
>> >>>> >> >> >> either succeeds against a given database, or it fails.  It
>> >>>> >> >> >> is
>> >>>> >> >> >> not
>> >>>> >> >> >> easy
>> >>>> >> >> >> to produce a situation where it fails "only rarely".
>> >>>> >> >> >> Things
>> >>>> >> >> >> that
>> >>>> >> >> >> could cause this are: 1. the database you are automapping
>> >>>> >> >> >> against is
>> >>>> >> >> >> constantly changing,   2. the mapping is relying upon some
>> >>>> >> >> >> subtle
>> >>>> >> >> >> hash
>> >>>> >> >> >> ordering or 3. automap is being used in a multi-threaded
>> >>>> >> >> >> context
>> >>>> >> >> >> where
>> >>>> >> >> >> more than one automap tries to hit the same tables at the
>> >>>> >> >> >> same
>> >>>> >> >> >> time.
>> >>>> >> >> >> When something happens "only rarely", that points to race
>> >>>> >> >> >> conditions
>> >>>> >> >> >> or sometimes hash ordering issues.     To rule out #2, you
>> >>>> >> >> >> would
>> >>>> >> >> >> create a test script that does your automap by itself and
>> >>>> >> >> >> try
>> >>>> >> >> >> to run
>> >>>> >> >> >> automap hundreds of times against the particular database
>> >>>> >> >> >> that
>> >>>> >> >> >> caused
>> >>>> >> >> >> a failure, if under Python 2 you'd want to set
>> >>>> >> >> >> PYTHONHASHSEED='random'
>> >>>> >> >> >> in the environment.    But the error you showed looks more
>> >>>> >> >> >> like
>> >>>> >> >> >> a
>> >>>> >> >> >> race
>> >>>> >> >> >> of two automaps hitting the same table.
>> >>>> >> >> >>
>> >>>> >> >> >>
>> >>>> >> >> >>
>> >>>> >> >> >> What you're suggesting seems to indicate I should be having
>> >>>> >> >> >> problems
>> >>>> >> >> >>> constantly, but I'm not.
>> >>>> >> >> >>
>> >>>> >> >> >> the prevalence of a race condition is proportional to how
>> >>>> >> >> >> unlikely
>> >>>> >> >> >> the
>> >>>> >> >> >> race is in the first place and how much concurrency is in
>> >>>> >> >> >> play
>> >>>> >> >> >> on a
>> >>>> >> >> >> given basis.  Whether it happens every minute or once a
>> >>>> >> >> >> month
>> >>>> >> >> >> doesn't
>> >>>> >> >> >> really matter.
>> >>>> >> >> >>
>> >>>> >> >> >>>
>> >>>> >> >> >>>> How does your program know exactly how to interact with
>> >>>> >> >> >>>> these
>> >>>> >> >> >>>> automap
>> >>>> >> >> >>>> databases if it knows nothing of what tables are present
>> >>>> >> >> >>>> or
>> >>>> >> >> >>>> their
>> >>>> >> >> >>>> structure?     If your application *does* know these
>> >>>> >> >> >>>> things,
>> >>>> >> >> >>>> then
>> >>>> >> >> >>>> you
>> >>>> >> >> >>>> should tell automap about it.
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>> Well, the entire point of using automap is to make the data
>> >>>> >> >> >>> easily
>> >>>> >> >> >>> accessible through the mapped objects, so the higher level
>> >>>> >> >> >>> code
>> >>>> >> >> >>> certainly
>> >>>> >> >> >>> knows what it's doing. This low-level DB class, however, is
>> >>>> >> >> >>> only
>> >>>> >> >> >>> meant
>> >>>> >> >> >>> to
>> >>>> >> >> >>> expedite the connection configuration process. Ostensibly
>> >>>> >> >> >>> the
>> >>>> >> >> >>> higher
>> >>>> >> >> >>> level
>> >>>> >> >> >>> code could pass more information into the lower level class
>> >>>> >> >> >>> in
>> >>>> >> >> >>> terms
>> >>>> >> >> >>> of
>> >>>> >> >> >>> tables it'll be using and so forth, but that's a pretty
>> >>>> >> >> >>> expensive
>> >>>> >> >> >>> way
>> >>>> >> >> >>> to
>> >>>> >> >> >>> bypass this problem.
>> >>>> >> >> >>
>> >>>> >> >> >> If the schemas of these databases are fixed, it's typical to
>> >>>> >> >> >> have
>> >>>> >> >> >> them
>> >>>> >> >> >> mapped up front.  Since you have a finite, small number of
>> >>>> >> >> >> databases,
>> >>>> >> >> >> you should look into having them mapped either up front or
>> >>>> >> >> >> within a
>> >>>> >> >> >> section that is mutexed by a threading.Lock, and in either
>> >>>> >> >> >> case
>> >>>> >> >> >> you
>> >>>> >> >> >> call configure_mappers() as soon as they are all set up.
>> >>>> >> >> >>
>> >>>> >> >> >>
>> >>>> >> >> >>
>> >>>> >> >> >>>
>> >>>> >> >> >>>> As far as legitimate mappings failing afterwards, that's
>> >>>> >> >> >>>> only
>> >>>> >> >> >>>> if
>> >>>> >> >> >>>> you
>> >>>> >> >> >>>> don't get rid of these failed mappers.  If you "del" a
>> >>>> >> >> >>>> mapper
>> >>>> >> >> >>>> that
>> >>>> >> >> >>>> failed to configure and make sure it is garbage collected,
>> >>>> >> >> >>>> it
>> >>>> >> >> >>>> will
>> >>>> >> >> >>>> not
>> >>>> >> >> >>>> interfere with subsequent mappings.   so you probably want
>> >>>> >> >> >>>> to
>> >>>> >> >> >>>> call
>> >>>> >> >> >>>> configure_mappers(), then if it fails, make sure you lose
>> >>>> >> >> >>>> references
>> >>>> >> >> >>>> to those mappers that failed.
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>> As I indicated in my original post, it's not my code that's
>> >>>> >> >> >>> holding
>> >>>> >> >> >>> on
>> >>>> >> >> >>> to
>> >>>> >> >> >>> the fact that a mapping failed. It's being done in the
>> >>>> >> >> >>> global
>> >>>> >> >> >>> space
>> >>>> >> >> >>> within
>> >>>> >> >> >>> SQLAlchemy, itself. That's what I find utterly mind
>> >>>> >> >> >>> boggling.
>> >>>> >> >> >>> I
>> >>>> >> >> >>> don't
>> >>>> >> >> >>> know
>> >>>> >> >> >>> why it would hold on to anything in the global space and
>> >>>> >> >> >>> certainly
>> >>>> >> >> >>> not
>> >>>> >> >> >>> trigger failures on subsequent mapping attempts if a past
>> >>>> >> >> >>> attempt
>> >>>> >> >> >>> failed,
>> >>>> >> >> >>> but that's exactly what it's doing.
>> >>>> >> >> >>>
>> >>>> >> >> >>> You can see the code here:
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/mapper.py#L3017
>> >>>> >> >> >>
>> >>>> >> >> >> SQLAlchemy does not hold onto mappers that your program does
>> >>>> >> >> >> not
>> >>>> >> >> >> refer
>> >>>> >> >> >> towards.  I can demonstrate this if you like.   The code at
>> >>>> >> >> >> 3017 is
>> >>>> >> >> >> only invoked if the mapper is still in the _mapper_registry,
>> >>>> >> >> >> which
>> >>>> >> >> >> is
>> >>>> >> >> >> a WeakKeyDictionary.  SQLAlchemy only maintains weak
>> >>>> >> >> >> references
>> >>>> >> >> >> to a
>> >>>> >> >> >> particular mapper.
>> >>>> >> >> >>
>> >>>> >> >> >> Now, there are a lot of challenges in getting your program
>> >>>> >> >> >> to
>> >>>> >> >> >> no
>> >>>> >> >> >> longer refer to a particular mapper, depending on what
>> >>>> >> >> >> you're
>> >>>> >> >> >> doing.
>> >>>> >> >> >> If other mappers extend from your mapper, that's a strong
>> >>>> >> >> >> reference.
>> >>>> >> >> >> If other mappers refer to your mapper via relationship(),
>> >>>> >> >> >> thats
>> >>>> >> >> >> a
>> >>>> >> >> >> strong reference.   But here, if you are keeping these
>> >>>> >> >> >> per-connection
>> >>>> >> >> >> mappings all independent of each other, they should get
>> >>>> >> >> >> garbage
>> >>>> >> >> >> collected.     But if there's a bug in the weak referencing
>> >>>> >> >> >> in
>> >>>> >> >> >> SQLAlchemy or there's a particularly difficult mapping
>> >>>> >> >> >> pattern
>> >>>> >> >> >> in
>> >>>> >> >> >> your
>> >>>> >> >> >> program you need help releasing, I'd need to see specific
>> >>>> >> >> >> scripts
>> >>>> >> >> >> that
>> >>>> >> >> >> illustrate this.
>> >>>> >> >> >>
>> >>>> >> >> >>
>> >>>> >> >> >>>
>> >>>> >> >> >>> On Thu, May 24, 2018 at 8:11 PM, Mike Bayer
>> >>>> >> >> >>> <[email protected]>
>> >>>> >> >> >>> wrote:
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> On Thu, May 24, 2018 at 10:34 PM, Dave Mittner
>> >>>> >> >> >>>> <[email protected]>
>> >>>> >> >> >>>> wrote:
>> >>>> >> >> >>>> > Automap was, at the most regular, only ever used upon
>> >>>> >> >> >>>> > connection
>> >>>> >> >> >>>> > creation to
>> >>>> >> >> >>>> > a given database. (obvious implication is that DB
>> >>>> >> >> >>>> > changes
>> >>>> >> >> >>>> > within
>> >>>> >> >> >>>> > a
>> >>>> >> >> >>>> > connection might be problematic? not an applicable
>> >>>> >> >> >>>> > scenario
>> >>>> >> >> >>>> > thus
>> >>>> >> >> >>>> > far in
>> >>>> >> >> >>>> > our
>> >>>> >> >> >>>> > code)
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> So that is already a new fact (which i sort of guessed
>> >>>> >> >> >>>> might
>> >>>> >> >> >>>> be in
>> >>>> >> >> >>>> play) that this is a multi-tenant system.  How many
>> >>>> >> >> >>>> databases
>> >>>> >> >> >>>> are
>> >>>> >> >> >>>> we
>> >>>> >> >> >>>> talking about that are accessed by a single application?
>> >>>> >> >> >>>> What do
>> >>>> >> >> >>>> the
>> >>>> >> >> >>>> tables in these databases look like, are they all the same
>> >>>> >> >> >>>> across
>> >>>> >> >> >>>> all
>> >>>> >> >> >>>> DBs (in which case use fixed mappings) or are they totally
>> >>>> >> >> >>>> different?
>> >>>> >> >> >>>> if all the DBs have the same table structure then you
>> >>>> >> >> >>>> should
>> >>>> >> >> >>>> use
>> >>>> >> >> >>>> only
>> >>>> >> >> >>>> a single table/mapping structure for each DB.
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> If I am reading the error you are getting over at
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> https://stackoverflow.com/questions/50123090/application-process-unusable-after-cant-proceed-with-initialization-of-o
>> >>>> >> >> >>>> correctly, I've never seen that before but I might guess
>> >>>> >> >> >>>> that
>> >>>> >> >> >>>> you
>> >>>> >> >> >>>> have
>> >>>> >> >> >>>> multiple threads reflecting tables and creating classes of
>> >>>> >> >> >>>> the
>> >>>> >> >> >>>> identical name in different threads at the same time?
>> >>>> >> >> >>>> You
>> >>>> >> >> >>>> definitely
>> >>>> >> >> >>>> can't do that without modifying how the
>> >>>> >> >> >>>> sqlalchemy.orm.mapper._mapper_registry works.   You need
>> >>>> >> >> >>>> to
>> >>>> >> >> >>>> either
>> >>>> >> >> >>>> ensure these names are unique at all times, and if you
>> >>>> >> >> >>>> expect
>> >>>> >> >> >>>> multiple
>> >>>> >> >> >>>> threads to access the same names, you need to use a mutex
>> >>>> >> >> >>>> to
>> >>>> >> >> >>>> prevent
>> >>>> >> >> >>>> them from doing so concurrently.
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> > Here's the gist of my connection initiation code that's
>> >>>> >> >> >>>> > run
>> >>>> >> >> >>>> > upon
>> >>>> >> >> >>>> > instantiation of the DB class.
>> >>>> >> >> >>>> > Mind you, a prior coworker wrote this and there's so
>> >>>> >> >> >>>> > many
>> >>>> >> >> >>>> > ways
>> >>>> >> >> >>>> > of
>> >>>> >> >> >>>> > interfacing with SQLAlchemy that I'm not even sure where
>> >>>> >> >> >>>> > he
>> >>>> >> >> >>>> > got
>> >>>> >> >> >>>> > it
>> >>>> >> >> >>>> > from.
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >>             db_url = engine.url.URL(drivername =
>> >>>> >> >> >>>> >> self.drivername,
>> >>>> >> >> >>>> >>                                     username =
>> >>>> >> >> >>>> >> self.username,
>> >>>> >> >> >>>> >>                                     password =
>> >>>> >> >> >>>> >> self.password,
>> >>>> >> >> >>>> >>                                     host = self.host,
>> >>>> >> >> >>>> >>                                     database =
>> >>>> >> >> >>>> >> self.database,
>> >>>> >> >> >>>> >>                                     query =
>> >>>> >> >> >>>> >> {'charset':'utf8'})
>> >>>> >> >> >>>> >>             self.engine =
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> create_engine(db_url,encoding='utf8',convert_unicode=True)
>> >>>> >> >> >>>> >>             self.session = Session(self.engine)
>> >>>> >> >> >>>> >>             self.connection = self.engine.connect()
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >>             # Automap functionality has a lot of
>> >>>> >> >> >>>> >> overhead,
>> >>>> >> >> >>>> >> so
>> >>>> >> >> >>>> >> cache the
>> >>>> >> >> >>>> >> results on a per-host/database basis
>> >>>> >> >> >>>> >>             id =
>> >>>> >> >> >>>> >> (self.drivername,self.host,self.database)
>> >>>> >> >> >>>> >>             if id not in DB.tables:
>> >>>> >> >> >>>> >>                 base = automap_base()
>> >>>> >> >> >>>> >>                 base.prepare(self.engine, reflect=True,
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> name_for_scalar_relationship=name_for_scalar_relationship)
>> >>>> >> >> >>>> >>                 DB.tables[id] = base.classes
>> >>>> >> >> >>>> >>             self.tables = DB.tables[id]
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> How does your program know exactly how to interact with
>> >>>> >> >> >>>> these
>> >>>> >> >> >>>> automap
>> >>>> >> >> >>>> databases if it knows nothing of what tables are present
>> >>>> >> >> >>>> or
>> >>>> >> >> >>>> their
>> >>>> >> >> >>>> structure?     If your application *does* know these
>> >>>> >> >> >>>> things,
>> >>>> >> >> >>>> then
>> >>>> >> >> >>>> you
>> >>>> >> >> >>>> should tell automap about it.   Especially if only care
>> >>>> >> >> >>>> about
>> >>>> >> >> >>>> three
>> >>>> >> >> >>>> tables, use metadata.reflect() and pass those names to
>> >>>> >> >> >>>> "only":
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> http://docs.sqlalchemy.org/en/latest/core/metadata.html?highlight=metadata%20reflect#sqlalchemy.schema.MetaData.reflect.params.only
>> >>>> >> >> >>>> .   Further, your application, assuming it isn't just
>> >>>> >> >> >>>> letting
>> >>>> >> >> >>>> users
>> >>>> >> >> >>>> query whatever they want, would need to know about
>> >>>> >> >> >>>> specific
>> >>>> >> >> >>>> fields
>> >>>> >> >> >>>> and
>> >>>> >> >> >>>> columns on these tables in order to work with queries and
>> >>>> >> >> >>>> mapped
>> >>>> >> >> >>>> objects effectively.  You can map these fields and columns
>> >>>> >> >> >>>> up
>> >>>> >> >> >>>> front
>> >>>> >> >> >>>> since you know what they are, without using automap.
>> >>>> >> >> >>>> then
>> >>>> >> >> >>>> you
>> >>>> >> >> >>>> add
>> >>>> >> >> >>>> unit tests for these mappings to make sure they work.
>> >>>> >> >> >>>> that's
>> >>>> >> >> >>>> the
>> >>>> >> >> >>>> typical structure of a live production application.
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> automap probably can use another green warning box at
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> http://docs.sqlalchemy.org/en/latest/orm/extensions/automap.html#module-sqlalchemy.ext.automap
>> >>>> >> >> >>>> but the idea is that it's intended for expedient access to
>> >>>> >> >> >>>> a
>> >>>> >> >> >>>> particular database in an "offline", ad-hoc context.   It
>> >>>> >> >> >>>> isn't
>> >>>> >> >> >>>> oriented towards high capacity use in a production
>> >>>> >> >> >>>> application
>> >>>> >> >> >>>> against
>> >>>> >> >> >>>> databases of unknown structure, because that's not
>> >>>> >> >> >>>> generally
>> >>>> >> >> >>>> useful
>> >>>> >> >> >>>> anyway, a high capacity production application would have
>> >>>> >> >> >>>> a
>> >>>> >> >> >>>> more
>> >>>> >> >> >>>> formalized notion of its schema.    automap expects to
>> >>>> >> >> >>>> fail
>> >>>> >> >> >>>> against
>> >>>> >> >> >>>> an
>> >>>> >> >> >>>> unknown database until it is customized to work around the
>> >>>> >> >> >>>> issues
>> >>>> >> >> >>>> in
>> >>>> >> >> >>>> that DB, such as the functions for resolving naming
>> >>>> >> >> >>>> conflicts
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> (http://docs.sqlalchemy.org/en/latest/orm/extensions/automap.html#handling-simple-naming-conflicts).
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> As far as legitimate mappings failing afterwards, that's
>> >>>> >> >> >>>> only
>> >>>> >> >> >>>> if
>> >>>> >> >> >>>> you
>> >>>> >> >> >>>> don't get rid of these failed mappers.  If you "del" a
>> >>>> >> >> >>>> mapper
>> >>>> >> >> >>>> that
>> >>>> >> >> >>>> failed to configure and make sure it is garbage collected,
>> >>>> >> >> >>>> it
>> >>>> >> >> >>>> will
>> >>>> >> >> >>>> not
>> >>>> >> >> >>>> interfere with subsequent mappings.   so you probably want
>> >>>> >> >> >>>> to
>> >>>> >> >> >>>> call
>> >>>> >> >> >>>> configure_mappers(), then if it fails, make sure you lose
>> >>>> >> >> >>>> references
>> >>>> >> >> >>>> to those mappers that failed.
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> > On Thu, May 24, 2018 at 5:50 PM, Mike Bayer
>> >>>> >> >> >>>> > <[email protected]>
>> >>>> >> >> >>>> > wrote:
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> On Thu, May 24, 2018 at 5:40 PM, Dave Mittner
>> >>>> >> >> >>>> >> <[email protected]>
>> >>>> >> >> >>>> >> wrote:
>> >>>> >> >> >>>> >> > Also posted here:
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > https://stackoverflow.com/questions/50123090/application-process-unusable-after-cant-proceed-with-initialization-of-o
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > I have a multithreaded application that runs various
>> >>>> >> >> >>>> >> > jobs in
>> >>>> >> >> >>>> >> > threads.
>> >>>> >> >> >>>> >> > One of
>> >>>> >> >> >>>> >> > these jobs goes out to various data sources to query
>> >>>> >> >> >>>> >> > for
>> >>>> >> >> >>>> >> > data.
>> >>>> >> >> >>>> >> > On
>> >>>> >> >> >>>> >> > occasion
>> >>>> >> >> >>>> >> > the mapping process fails and an exception is thrown.
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > That on its own isn't a big deal; my system is
>> >>>> >> >> >>>> >> > designed
>> >>>> >> >> >>>> >> > to
>> >>>> >> >> >>>> >> > compensate
>> >>>> >> >> >>>> >> > for
>> >>>> >> >> >>>> >> > periodically failing jobs.
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > The problem is that that mapping failure seems to be
>> >>>> >> >> >>>> >> > recorded
>> >>>> >> >> >>>> >> > in
>> >>>> >> >> >>>> >> > a
>> >>>> >> >> >>>> >> > global
>> >>>> >> >> >>>> >> > space that then prevents all future mapping attempts
>> >>>> >> >> >>>> >> > to
>> >>>> >> >> >>>> >> > be
>> >>>> >> >> >>>> >> > aborted.
>> >>>> >> >> >>>> >> > Even
>> >>>> >> >> >>>> >> > attempts on completely different threads using
>> >>>> >> >> >>>> >> > completely
>> >>>> >> >> >>>> >> > different
>> >>>> >> >> >>>> >> > databases. This renders my entire application
>> >>>> >> >> >>>> >> > effectively
>> >>>> >> >> >>>> >> > broken
>> >>>> >> >> >>>> >> > from
>> >>>> >> >> >>>> >> > that
>> >>>> >> >> >>>> >> > point on.
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > After looking in SQLAlchemy's code, mappers are
>> >>>> >> >> >>>> >> > stored
>> >>>> >> >> >>>> >> > in a
>> >>>> >> >> >>>> >> > _mapper_registry
>> >>>> >> >> >>>> >> > global space variable and once any mapper in the
>> >>>> >> >> >>>> >> > registry
>> >>>> >> >> >>>> >> > errors
>> >>>> >> >> >>>> >> > out,
>> >>>> >> >> >>>> >> > any
>> >>>> >> >> >>>> >> > attempt to configure a new mapper will fail.
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > Mapping failures of this nature may be rare -- and
>> >>>> >> >> >>>> >> > indeed it
>> >>>> >> >> >>>> >> > only
>> >>>> >> >> >>>> >> > rarely
>> >>>> >> >> >>>> >> > happens on the connection I'm having a problem with
>> >>>> >> >> >>>> >> > --
>> >>>> >> >> >>>> >> > but
>> >>>> >> >> >>>> >> > this
>> >>>> >> >> >>>> >> > complete
>> >>>> >> >> >>>> >> > locking behavior of all future mapping seems very odd
>> >>>> >> >> >>>> >> > to
>> >>>> >> >> >>>> >> > me.
>> >>>> >> >> >>>> >> > If
>> >>>> >> >> >>>> >> > there
>> >>>> >> >> >>>> >> > isn't
>> >>>> >> >> >>>> >> > a way around this I might have no choice but to have
>> >>>> >> >> >>>> >> > my
>> >>>> >> >> >>>> >> > process
>> >>>> >> >> >>>> >> > completely
>> >>>> >> >> >>>> >> > exit when the exception is encountered, even if that
>> >>>> >> >> >>>> >> > means
>> >>>> >> >> >>>> >> > killing
>> >>>> >> >> >>>> >> > other
>> >>>> >> >> >>>> >> > running threads.
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> are you creating mappers on the fly or on a per-request
>> >>>> >> >> >>>> >> basis?
>> >>>> >> >> >>>> >> You'd
>> >>>> >> >> >>>> >> want to ideally have mappings created just once at the
>> >>>> >> >> >>>> >> module
>> >>>> >> >> >>>> >> import
>> >>>> >> >> >>>> >> level.  Then when your application is ready to start
>> >>>> >> >> >>>> >> up,
>> >>>> >> >> >>>> >> call
>> >>>> >> >> >>>> >> configure_mappers() and everything will be set up.
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> if those are not patterns you're able to use, then
>> >>>> >> >> >>>> >> please
>> >>>> >> >> >>>> >> provide
>> >>>> >> >> >>>> >> more
>> >>>> >> >> >>>> >> specifics.   from your stack trace on SO, it seems like
>> >>>> >> >> >>>> >> you are
>> >>>> >> >> >>>> >> using
>> >>>> >> >> >>>> >> automap.   When is that running?  If per request, this
>> >>>> >> >> >>>> >> very
>> >>>> >> >> >>>> >> expensive
>> >>>> >> >> >>>> >> and will have problems.
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> The mapping process *is* guarded by a mutex so it is
>> >>>> >> >> >>>> >> difficult
>> >>>> >> >> >>>> >> to
>> >>>> >> >> >>>> >> produce an issue with mappings failing - the stack
>> >>>> >> >> >>>> >> trace
>> >>>> >> >> >>>> >> you
>> >>>> >> >> >>>> >> post
>> >>>> >> >> >>>> >> almost appears like there is some kind of naming issue
>> >>>> >> >> >>>> >> happening
>> >>>> >> >> >>>> >> where
>> >>>> >> >> >>>> >> a particular mapper has been garbage collected or
>> >>>> >> >> >>>> >> something
>> >>>> >> >> >>>> >> like
>> >>>> >> >> >>>> >> that
>> >>>> >> >> >>>> >> yet still being referred towards by other mappers that
>> >>>> >> >> >>>> >> are
>> >>>> >> >> >>>> >> being
>> >>>> >> >> >>>> >> configured.     need to see details of how your code
>> >>>> >> >> >>>> >> works.
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > Any ideas?
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > --
>> >>>> >> >> >>>> >> > SQLAlchemy -
>> >>>> >> >> >>>> >> > The Python SQL Toolkit and Object Relational Mapper
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > http://www.sqlalchemy.org/
>> >>>> >> >> >>>> >> >
>> >>>> >> >> >>>> >> > To post example code, please provide an MCVE:
>> >>>> >> >> >>>> >> > Minimal,
>> >>>> >> >> >>>> >> > Complete,
>> >>>> >> >> >>>> >> > and
>> >>>> >> >> >>>> >> > Verifiable Example. See
>> >>>> >> >> >>>> >> > http://stackoverflow.com/help/mcve
>> >>>> >> >> >>>> >> > for a
>> >>>> >> >> >>>> >> > full
>> >>>> >> >> >>>> >> > description.
>> >>>> >> >> >>>> >> > ---
>> >>>> >> >> >>>> >> > You received this message because you are subscribed
>> >>>> >> >> >>>> >> > to
>> >>>> >> >> >>>> >> > the
>> >>>> >> >> >>>> >> > Google
>> >>>> >> >> >>>> >> > Groups
>> >>>> >> >> >>>> >> > "sqlalchemy" group.
>> >>>> >> >> >>>> >> > To unsubscribe from this group and stop receiving
>> >>>> >> >> >>>> >> > emails
>> >>>> >> >> >>>> >> > from
>> >>>> >> >> >>>> >> > it,
>> >>>> >> >> >>>> >> > send
>> >>>> >> >> >>>> >> > an
>> >>>> >> >> >>>> >> > email to [email protected].
>> >>>> >> >> >>>> >> > To post to this group, send email to
>> >>>> >> >> >>>> >> > [email protected].
>> >>>> >> >> >>>> >> > Visit this group at
>> >>>> >> >> >>>> >> > https://groups.google.com/group/sqlalchemy.
>> >>>> >> >> >>>> >> > For more options, visit
>> >>>> >> >> >>>> >> > https://groups.google.com/d/optout.
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> --
>> >>>> >> >> >>>> >> SQLAlchemy -
>> >>>> >> >> >>>> >> The Python SQL Toolkit and Object Relational Mapper
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> http://www.sqlalchemy.org/
>> >>>> >> >> >>>> >>
>> >>>> >> >> >>>> >> To post example code, please provide an MCVE: Minimal,
>> >>>> >> >> >>>> >> Complete,
>> >>>> >> >> >>>> >> and
>> >>>> >> >> >>>> >> Verifiable Example.  See
>> >>>> >> >> >>>> >> http://stackoverflow.com/help/mcve
>> >>>> >> >> >>>> >> for a
>> >>>> >> >> >>>> >> full
>> >>>> >> >> >>>> >> description.
>> >>>> >> >> >>>> >> ---
>> >>>> >> >> >>>> >> You received this message because you are subscribed to
>> >>>> >> >> >>>> >> the
>> >>>> >> >> >>>> >> Google
>> >>>> >> >> >>>> >> Groups
>> >>>> >> >> >>>> >> "sqlalchemy" group.
>> >>>> >> >> >>>> >> To unsubscribe from this group and stop receiving
>> >>>> >> >> >>>> >> emails
>> >>>> >> >> >>>> >> from
>> >>>> >> >> >>>> >> it,
>> >>>> >> >> >>>> >> send
>> >>>> >> >> >>>> >> an
>> >>>> >> >> >>>> >> email to [email protected].
>> >>>> >> >> >>>> >> To post to this group, send email to
>> >>>> >> >> >>>> >> [email protected].
>> >>>> >> >> >>>> >> Visit this group at
>> >>>> >> >> >>>> >> https://groups.google.com/group/sqlalchemy.
>> >>>> >> >> >>>> >> For more options, visit
>> >>>> >> >> >>>> >> https://groups.google.com/d/optout.
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> > --
>> >>>> >> >> >>>> > SQLAlchemy -
>> >>>> >> >> >>>> > The Python SQL Toolkit and Object Relational Mapper
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> > http://www.sqlalchemy.org/
>> >>>> >> >> >>>> >
>> >>>> >> >> >>>> > To post example code, please provide an MCVE: Minimal,
>> >>>> >> >> >>>> > Complete,
>> >>>> >> >> >>>> > and
>> >>>> >> >> >>>> > Verifiable Example. See
>> >>>> >> >> >>>> > http://stackoverflow.com/help/mcve
>> >>>> >> >> >>>> > for a
>> >>>> >> >> >>>> > full
>> >>>> >> >> >>>> > description.
>> >>>> >> >> >>>> > ---
>> >>>> >> >> >>>> > You received this message because you are subscribed to
>> >>>> >> >> >>>> > the
>> >>>> >> >> >>>> > Google
>> >>>> >> >> >>>> > Groups
>> >>>> >> >> >>>> > "sqlalchemy" group.
>> >>>> >> >> >>>> > To unsubscribe from this group and stop receiving emails
>> >>>> >> >> >>>> > from
>> >>>> >> >> >>>> > it,
>> >>>> >> >> >>>> > send
>> >>>> >> >> >>>> > an
>> >>>> >> >> >>>> > email to [email protected].
>> >>>> >> >> >>>> > To post to this group, send email to
>> >>>> >> >> >>>> > [email protected].
>> >>>> >> >> >>>> > Visit this group at
>> >>>> >> >> >>>> > https://groups.google.com/group/sqlalchemy.
>> >>>> >> >> >>>> > For more options, visit
>> >>>> >> >> >>>> > https://groups.google.com/d/optout.
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> --
>> >>>> >> >> >>>> SQLAlchemy -
>> >>>> >> >> >>>> The Python SQL Toolkit and Object Relational Mapper
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> http://www.sqlalchemy.org/
>> >>>> >> >> >>>>
>> >>>> >> >> >>>> To post example code, please provide an MCVE: Minimal,
>> >>>> >> >> >>>> Complete,
>> >>>> >> >> >>>> and
>> >>>> >> >> >>>> Verifiable Example.  See
>> >>>> >> >> >>>> http://stackoverflow.com/help/mcve
>> >>>> >> >> >>>> for a
>> >>>> >> >> >>>> full
>> >>>> >> >> >>>> description.
>> >>>> >> >> >>>> ---
>> >>>> >> >> >>>> You received this message because you are subscribed to
>> >>>> >> >> >>>> the
>> >>>> >> >> >>>> Google
>> >>>> >> >> >>>> Groups
>> >>>> >> >> >>>> "sqlalchemy" group.
>> >>>> >> >> >>>> To unsubscribe from this group and stop receiving emails
>> >>>> >> >> >>>> from
>> >>>> >> >> >>>> it,
>> >>>> >> >> >>>> send an
>> >>>> >> >> >>>> email to [email protected].
>> >>>> >> >> >>>> To post to this group, send email to
>> >>>> >> >> >>>> [email protected].
>> >>>> >> >> >>>> Visit this group at
>> >>>> >> >> >>>> https://groups.google.com/group/sqlalchemy.
>> >>>> >> >> >>>> For more options, visit
>> >>>> >> >> >>>> https://groups.google.com/d/optout.
>> >>>> >> >> >>>
>> >>>> >> >> >>>
>> >>>> >> >> >>> --
>> >>>> >> >> >>> SQLAlchemy -
>> >>>> >> >> >>> The Python SQL Toolkit and Object Relational Mapper
>> >>>> >> >> >>>
>> >>>> >> >> >>> http://www.sqlalchemy.org/
>> >>>> >> >> >>>
>> >>>> >> >> >>> To post example code, please provide an MCVE: Minimal,
>> >>>> >> >> >>> Complete,
>> >>>> >> >> >>> and
>> >>>> >> >> >>> Verifiable Example. See http://stackoverflow.com/help/mcve
>> >>>> >> >> >>> for
>> >>>> >> >> >>> a
>> >>>> >> >> >>> full
>> >>>> >> >> >>> description.
>> >>>> >> >> >>> ---
>> >>>> >> >> >>> You received this message because you are subscribed to the
>> >>>> >> >> >>> Google
>> >>>> >> >> >>> Groups
>> >>>> >> >> >>> "sqlalchemy" group.
>> >>>> >> >> >>> To unsubscribe from this group and stop receiving emails
>> >>>> >> >> >>> from
>> >>>> >> >> >>> it,
>> >>>> >> >> >>> send
>> >>>> >> >> >>> an
>> >>>> >> >> >>> email to [email protected].
>> >>>> >> >> >>> To post to this group, send email to
>> >>>> >> >> >>> [email protected].
>> >>>> >> >> >>> Visit this group at
>> >>>> >> >> >>> https://groups.google.com/group/sqlalchemy.
>> >>>> >> >> >>> For more options, visit https://groups.google.com/d/optout.
>> >>>> >> >>
>> >>>> >> >> --
>> >>>> >> >> SQLAlchemy -
>> >>>> >> >> The Python SQL Toolkit and Object Relational Mapper
>> >>>> >> >>
>> >>>> >> >> http://www.sqlalchemy.org/
>> >>>> >> >>
>> >>>> >> >> To post example code, please provide an MCVE: Minimal,
>> >>>> >> >> Complete,
>> >>>> >> >> and
>> >>>> >> >> Verifiable Example.  See  http://stackoverflow.com/help/mcve
>> >>>> >> >> for a
>> >>>> >> >> full
>> >>>> >> >> description.
>> >>>> >> >> ---
>> >>>> >> >> You received this message because you are subscribed to the
>> >>>> >> >> Google
>> >>>> >> >> Groups
>> >>>> >> >> "sqlalchemy" group.
>> >>>> >> >> To unsubscribe from this group and stop receiving emails from
>> >>>> >> >> it,
>> >>>> >> >> send
>> >>>> >> >> an
>> >>>> >> >> email to [email protected].
>> >>>> >> >> To post to this group, send email to
>> >>>> >> >> [email protected].
>> >>>> >> >> Visit this group at https://groups.google.com/group/sqlalchemy.
>> >>>> >> >> For more options, visit https://groups.google.com/d/optout.
>> >>>> >> >
>> >>>> >> >
>> >>>> >> > --
>> >>>> >> > SQLAlchemy -
>> >>>> >> > The Python SQL Toolkit and Object Relational Mapper
>> >>>> >> >
>> >>>> >> > http://www.sqlalchemy.org/
>> >>>> >> >
>> >>>> >> > To post example code, please provide an MCVE: Minimal, Complete,
>> >>>> >> > and
>> >>>> >> > Verifiable Example. See http://stackoverflow.com/help/mcve for a
>> >>>> >> > full
>> >>>> >> > description.
>> >>>> >> > ---
>> >>>> >> > You received this message because you are subscribed to the
>> >>>> >> > Google
>> >>>> >> > Groups
>> >>>> >> > "sqlalchemy" group.
>> >>>> >> > To unsubscribe from this group and stop receiving emails from
>> >>>> >> > it,
>> >>>> >> > send
>> >>>> >> > an
>> >>>> >> > email to [email protected].
>> >>>> >> > To post to this group, send email to
>> >>>> >> > [email protected].
>> >>>> >> > Visit this group at https://groups.google.com/group/sqlalchemy.
>> >>>> >> > For more options, visit https://groups.google.com/d/optout.
>> >>>> >>
>> >>>> >> --
>> >>>> >> SQLAlchemy -
>> >>>> >> The Python SQL Toolkit and Object Relational Mapper
>> >>>> >>
>> >>>> >> http://www.sqlalchemy.org/
>> >>>> >>
>> >>>> >> To post example code, please provide an MCVE: Minimal, Complete,
>> >>>> >> and
>> >>>> >> Verifiable Example.  See  http://stackoverflow.com/help/mcve for a
>> >>>> >> full
>> >>>> >> description.
>> >>>> >> ---
>> >>>> >> You received this message because you are subscribed to the Google
>> >>>> >> Groups
>> >>>> >> "sqlalchemy" group.
>> >>>> >> To unsubscribe from this group and stop receiving emails from it,
>> >>>> >> send an
>> >>>> >> email to [email protected].
>> >>>> >> To post to this group, send email to [email protected].
>> >>>> >> Visit this group at https://groups.google.com/group/sqlalchemy.
>> >>>> >> For more options, visit https://groups.google.com/d/optout.
>> >>>> >
>> >>>> >
>> >>>> > --
>> >>>> > SQLAlchemy -
>> >>>> > The Python SQL Toolkit and Object Relational Mapper
>> >>>> >
>> >>>> > http://www.sqlalchemy.org/
>> >>>> >
>> >>>> > To post example code, please provide an MCVE: Minimal, Complete,
>> >>>> > and
>> >>>> > Verifiable Example. See http://stackoverflow.com/help/mcve for a
>> >>>> > full
>> >>>> > description.
>> >>>> > ---
>> >>>> > You received this message because you are subscribed to the Google
>> >>>> > Groups
>> >>>> > "sqlalchemy" group.
>> >>>> > To unsubscribe from this group and stop receiving emails from it,
>> >>>> > send
>> >>>> > an
>> >>>> > email to [email protected].
>> >>>> > To post to this group, send email to [email protected].
>> >>>> > Visit this group at https://groups.google.com/group/sqlalchemy.
>> >>>> > For more options, visit https://groups.google.com/d/optout.
>> >>>>
>> >>>> --
>> >>>> SQLAlchemy -
>> >>>> The Python SQL Toolkit and Object Relational Mapper
>> >>>>
>> >>>> http://www.sqlalchemy.org/
>> >>>>
>> >>>> To post example code, please provide an MCVE: Minimal, Complete, and
>> >>>> Verifiable Example.  See  http://stackoverflow.com/help/mcve for a
>> >>>> full
>> >>>> description.
>> >>>> ---
>> >>>> You received this message because you are subscribed to the Google
>> >>>> Groups "sqlalchemy" group.
>> >>>> To unsubscribe from this group and stop receiving emails from it,
>> >>>> send
>> >>>> an email to [email protected].
>> >>>> To post to this group, send email to [email protected].
>> >>>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> >>>> For more options, visit https://groups.google.com/d/optout.
>> >>>
>> >>>
>> >>> --
>> >>> SQLAlchemy -
>> >>> The Python SQL Toolkit and Object Relational Mapper
>> >>>
>> >>> http://www.sqlalchemy.org/
>> >>>
>> >>> To post example code, please provide an MCVE: Minimal, Complete, and
>> >>> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
>> >>> description.
>> >>> ---
>> >>> You received this message because you are subscribed to the Google
>> >>> Groups
>> >>> "sqlalchemy" group.
>> >>> To unsubscribe from this group and stop receiving emails from it, send
>> >>> an
>> >>> email to [email protected].
>> >>> To post to this group, send email to [email protected].
>> >>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> >>> For more options, visit https://groups.google.com/d/optout.
>> >>
>> >> --
>> >> SQLAlchemy -
>> >> The Python SQL Toolkit and Object Relational Mapper
>> >>
>> >> http://www.sqlalchemy.org/
>> >>
>> >> To post example code, please provide an MCVE: Minimal, Complete, and
>> >> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
>> >> description.
>> >> ---
>> >> You received this message because you are subscribed to the Google
>> >> Groups
>> >> "sqlalchemy" group.
>> >> To unsubscribe from this group and stop receiving emails from it, send
>> >> an
>> >> email to [email protected].
>> >> To post to this group, send email to [email protected].
>> >> Visit this group at https://groups.google.com/group/sqlalchemy.
>> >> For more options, visit https://groups.google.com/d/optout.
>> >
>> >
>> > --
>> > SQLAlchemy -
>> > The Python SQL Toolkit and Object Relational Mapper
>> >
>> > http://www.sqlalchemy.org/
>> >
>> > To post example code, please provide an MCVE: Minimal, Complete, and
>> > Verifiable Example. See http://stackoverflow.com/help/mcve for a full
>> > description.
>> > ---
>> > You received this message because you are subscribed to the Google
>> > Groups
>> > "sqlalchemy" group.
>> > To unsubscribe from this group and stop receiving emails from it, send
>> > an
>> > email to [email protected].
>> > To post to this group, send email to [email protected].
>> > Visit this group at https://groups.google.com/group/sqlalchemy.
>> > For more options, visit https://groups.google.com/d/optout.
>>
>> --
>> SQLAlchemy -
>> The Python SQL Toolkit and Object Relational Mapper
>>
>> http://www.sqlalchemy.org/
>>
>> To post example code, please provide an MCVE: Minimal, Complete, and
>> Verifiable Example.  See  http://stackoverflow.com/help/mcve for a full
>> description.
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To post to this group, send email to [email protected].
>> Visit this group at https://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/d/optout.
>
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at https://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to