Reputation: 22533
I created a repository class for Person with the below method in it:
def find_by_order_id(self, order_id: str) -> [Person]:
results = []
client_table = Table(TBL_CLIENT, self._metadata, autoload=True,
autoload_with=self._connection)
order_table = Table(TBL_ORDER, self._metadata, autoload=True,
autoload_with=self._connection)
query = select([client_table.c.first_name, client_table.c.last_name, client_table.c.date_of_birth])
.select_from(
order_table.join(client_table, client_table.c.order_id = order_table.c.order_id)
).where(order_table.c.order_id == order_id).distinct()
for row in self._connection.execute(query).fetchall():
results.append(dict(row))
return results
for some odd reason which I have no explanation for, I sometimes get KeyError from SQLAlchemy on a key that actually exists when I debug the code:
File "/home/tghasemi/miniconda3/envs/myproj/lib/python3.6/site-packages/sqlalchemy/util/_collections.py", line 210, in __getattr__
return self._data[key]
KeyError: 'order_id'
I noticed this only happens when I pull the connection from a connection pool while multithreading (each thread only uses one connection - I know connections are not threadsafe):
if use_pooling:
self._engine = create_engine(connection_string, pool_size=db_pool_size,
pool_pre_ping=db_pool_pre_ping, echo=db_echo)
else:
self._engine = create_engine(connection_string, echo=db_echo)
Considering the fact that the key exists(even checked it when the exception happens) when I put a breakpoint where the exception happens, I suspect loading the table is not completed yet when the query is being constructed.
Does anyone have any idea why something like this can happen? I gave up!
Upvotes: 3
Views: 786
Reputation: 22533
Well, I think I managed to fix the problem.
Something I did not mention in my question (in fact I did not think of it as a possibility) was that both the Engine and the MetaData object come from a Singleton class I created for my Database:
class Database(metaclass=SingletonMetaClass):
def __init__(self, config: Config, use_pooling: bool = True, logger: Logger = None):
# Initializing the stuff here ...
@property
def metadata(self): # used to return self._metadata here
return MetaData(self._engine, reflect=False, schema=self._db_schema)
@property
def engine(self):
return self._engine
Although the SQLAlchemy's official documentation says that the MetaData object is thread-safe for read operations, but for some reason in my case (which is a read operation) it was causing this issue. Somehow after not sharing this object among my threads the issue went away (not 100% sure if it literally went away, but it's not happening anymore).
Upvotes: 2