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.

Reply via email to