SQLAlchemy¶
from potatoq.contrib.sqlalchemy import install
install(app) # every Session; or install(app, my_sessionmaker)
From then on, .delay() follows the session's transaction:
with Session(engine) as session, session.begin():
session.add(User(email=email))
send_welcome.delay(email) # sent after COMMIT, dropped on ROLLBACK
- Only real writes defer tasks. SQLAlchemy 2.0 starts a transaction on any
query, so a task is deferred only once the transaction has written something or has
objects waiting to be flushed (
session.add()). Writes are anything but a SELECT run through the session or its connection: flushes, bulk operations, Core statements onsession.connection(),text()andexec_driver_sql()(where SQL starting withWITHcounts as a write, as it may beWITH ... INSERT). A read-only request that never commits doesn't swallow its tasks. Statements run on the raw DB-API connection (session.connection().connection.cursor()) aren't seen; passusing=sessionthere. - Same database, same transaction. When the broker is the session's database (Postgres or SQLite), the task row is written through the session's connection, so it commits atomically with your data.
- Explicit session:
task.apply_async(args, using=session)defers to that session's transaction regardless. - Rollback,
close()orreset()without a commit drops the deferred tasks. If sending after COMMIT fails (say, the broker is down), the error is logged on thepotatoq.sqlalchemylogger;commit()doesn't raise for data that did commit, and the other deferred tasks are still sent. - Works with
sessionmaker,scoped_session, Flask-SQLAlchemy andAsyncSession. WithAsyncSessionand the same database as the broker, tasks are sent right after COMMIT rather than written inside the transaction: the broker can't write through an async driver's connection.