document thread safety workaround for lambda statements

Change-Id: Idb7840ff64487ef985087a28bb6e96088e6a392e
References: #8098
This commit is contained in:
Mike Bayer
2022-06-08 16:03:26 -04:00
parent 73f867d996
commit bf40bade26
+35 -14
View File
@@ -1258,11 +1258,13 @@ SELECTs with LIMIT/OFFSET are correctly rendered and cached.
Using Lambdas to add significant speed gains to statement production
--------------------------------------------------------------------
.. deepalchemy:: This technique is generally non-essential except in very performance
intensive scenarios, and intended for experienced Python programmers.
While fairly straightforward, it involves metaprogramming concepts that are
not appropriate for novice Python developers. The lambda approach can be
applied to at a later time to existing code with a minimal amount of effort.
.. deprecated:: 1.4 The lambda statement feature is being considered
for deprecation in SQLAlchemy and removal from documentation for 2.0. The
internal workings have been shown to be not thread safe during construction
for multi-lambda statements, and the overall complexity of the feature has
proven to be mostly impractical outside of a particular narrow use case
in the ORM.
Python functions, typically expressed as lambdas, may be used to generate
SQL expressions which are cacheable based on the Python code location of
@@ -1296,11 +1298,15 @@ to also having closure variables, which are significant to the whole
approach::
from sqlalchemy import lambda_stmt
import threading
mutex = threading.Lock()
def run_my_statement(connection, parameter):
stmt = lambda_stmt(lambda: select(table))
stmt += lambda s: s.where(table.c.col == parameter)
stmt += lambda s: s.order_by(table.c.id)
with mutex:
stmt = lambda_stmt(lambda: select(table))
stmt += lambda s: s.where(table.c.col == parameter)
stmt += lambda s: s.order_by(table.c.id)
return connection.execute(stmt)
@@ -1331,15 +1337,26 @@ objects will run and analyze the given lambda in order to calculate how
it should be cached on each run, trying to detect any potential problems.
Basic guidelines include:
* **For multi-threaded applications, a mutex is required when building up
statements among multiple lambdas** -
this is a discovered limitation in the implementation; while SQLAlchemy
developers hope to repair it, code examples are illustrating this
usage for now until it can be fixed.
* **Any kind of statement is supported** - while it's expected that
:func:`_sql.select` constructs are the prime use case for :func:`_sql.lambda_stmt`,
DML statements such as :func:`_sql.insert` and :func:`_sql.update` are
equally usable::
import threading
mutex = threading.Lock()
def upd(id_, newname):
stmt = lambda_stmt(lambda: users.update())
stmt += lambda s: s.values(name=newname)
stmt += lambda s: s.where(users.c.id==id_)
with mutex:
stmt = lambda_stmt(lambda: users.update())
stmt += lambda s: s.values(name=newname)
stmt += lambda s: s.where(users.c.id==id_)
return stmt
with engine.begin() as conn:
@@ -1351,15 +1368,19 @@ Basic guidelines include:
can accommodate ORM functionality completely and used directly with
:meth:`_orm.Session.execute`::
import threading
mutex = threading.Lock()
def select_user(session, name):
stmt = lambda_stmt(lambda: select(User))
stmt += lambda s: s.where(User.name == name)
with mutex:
stmt = lambda_stmt(lambda: select(User))
stmt += lambda s: s.where(User.name == name)
row = session.execute(stmt).first()
return row
..
* **Bound parameters are automatically accommodated** - in contrast to SQLAlchemy's
previous "baked query" system, the lambda SQL system accommodates for
Python literal values which become SQL bound parameters automatically.