diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-06-07 15:00:20 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-06-08 09:45:10 -0400 |
commit | 117878f7870377f143917a22160320a891eb0211 (patch) | |
tree | 3b87965f0fa78a9643afccf8646f693e6782f4b8 /lib/sqlalchemy/sql/lambdas.py | |
parent | 938c5d1033085289b4cbbd4b9229eaa3ad90b66d (diff) | |
download | sqlalchemy-117878f7870377f143917a22160320a891eb0211.tar.gz |
fix race conditions in lambda statements
Fixed multiple observed race conditions related to :func:`.lambda_stmt`,
including an initial "dogpile" issue when a new Python code object is
initially analyzed among multiple simultaneous threads which created both a
performance issue as well as some internal corruption of state.
Additionally repaired observed race condition which could occur when
"cloning" an expression construct that is also in the process of being
compiled or otherwise accessed in a different thread due to memoized
attributes altering the ``__dict__`` while iterated, for Python versions
prior to 3.10; in particular the lambda SQL construct is sensitive to this
as it holds onto a single statement object persistently. The iteration has
been refined to use ``dict.copy()`` with or without an additional iteration
instead.
Fixes: #8098
Change-Id: I4e0b627bfa187f1780dc68ec81b94db1c78f846a
Diffstat (limited to 'lib/sqlalchemy/sql/lambdas.py')
-rw-r--r-- | lib/sqlalchemy/sql/lambdas.py | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/lib/sqlalchemy/sql/lambdas.py b/lib/sqlalchemy/sql/lambdas.py index 3e82a9a6a..c7464c91c 100644 --- a/lib/sqlalchemy/sql/lambdas.py +++ b/lib/sqlalchemy/sql/lambdas.py @@ -12,6 +12,7 @@ import collections.abc as collections_abc import inspect import itertools import operator +import threading import types from types import CodeType from typing import Any @@ -695,6 +696,8 @@ class AnalyzedCode: CodeType, AnalyzedCode ] = weakref.WeakKeyDictionary() + _generation_mutex = threading.RLock() + @classmethod def get(cls, fn, lambda_element, lambda_kw, **kw): try: @@ -703,11 +706,16 @@ class AnalyzedCode: except KeyError: pass - analyzed: AnalyzedCode - cls._fns[fn.__code__] = analyzed = AnalyzedCode( - fn, lambda_element, lambda_kw, **kw - ) - return analyzed + with cls._generation_mutex: + # check for other thread already created object + if fn.__code__ in cls._fns: + return cls._fns[fn.__code__] + + analyzed: AnalyzedCode + cls._fns[fn.__code__] = analyzed = AnalyzedCode( + fn, lambda_element, lambda_kw, **kw + ) + return analyzed def __init__(self, fn, lambda_element, opts): if inspect.ismethod(fn): |