Last active
September 14, 2023 13:44
-
-
Save jdavcs/a0b4c15c326c079cd28b433581bf6cc1 to your computer and use it in GitHub Desktop.
savepoint transaction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import contextlib | |
from sqlalchemy import Column, Integer, String | |
from sqlalchemy import select | |
from sqlalchemy import create_engine | |
from sqlalchemy.orm import ( | |
declarative_base, | |
sessionmaker, | |
) | |
#################### setup #################### | |
engine = create_engine([connection string], echo=True) | |
Base = declarative_base() | |
class Job(Base): | |
__tablename__ = "job" | |
id = Column(Integer, primary_key=True) | |
state = Column(String) | |
Base.metadata.create_all(engine) | |
@contextlib.contextmanager | |
def transaction(session): | |
if not session.in_transaction(): | |
with session.begin(): | |
yield | |
else: | |
yield | |
def get_jobs(session): | |
return session.scalars(select(Job).order_by(Job.id)) | |
#################### tests #################### | |
def test_autocommit(): | |
""" | |
Works with both autocommit settings. | |
Pass job error_id to simulate error at a given step in the iteration. | |
Expeted result: jobs processed before error should be commited, errored | |
job: rolled back, subsequent jobs: not reached. | |
""" | |
#sm = sessionmaker(bind=engine, autoflush=False, autocommit=True) | |
sm = sessionmaker(bind=engine, autoflush=False, autocommit=False) | |
with sm() as session, session.begin(): | |
jobs = get_jobs(session) | |
try: | |
for job in jobs: | |
with session.begin_nested(): | |
change_state(session, job) | |
finally: | |
session.commit() | |
def change_state(session, job, error_id=None): | |
with transaction(session): | |
job.state = 'updated' | |
if job.id == error_id: | |
raise Exception("MY EXCEPTION") # simulate error | |
session.commit() | |
test_autocommit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(.venv_sa14) rivendell$ python savepoint_demo.py | |
2023-09-14 09:38:57,035 INFO sqlalchemy.engine.Engine select pg_catalog.version() | |
2023-09-14 09:38:57,035 INFO sqlalchemy.engine.Engine [raw sql] {} | |
2023-09-14 09:38:57,036 INFO sqlalchemy.engine.Engine select current_schema() | |
2023-09-14 09:38:57,036 INFO sqlalchemy.engine.Engine [raw sql] {} | |
2023-09-14 09:38:57,036 INFO sqlalchemy.engine.Engine show standard_conforming_strings | |
2023-09-14 09:38:57,036 INFO sqlalchemy.engine.Engine [raw sql] {} | |
2023-09-14 09:38:57,037 INFO sqlalchemy.engine.Engine BEGIN (implicit) | |
2023-09-14 09:38:57,037 INFO sqlalchemy.engine.Engine select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%(name)s | |
2023-09-14 09:38:57,037 INFO sqlalchemy.engine.Engine [generated in 0.00008s] {'name': 'job'} | |
2023-09-14 09:38:57,037 INFO sqlalchemy.engine.Engine COMMIT | |
/home/john/0dev/drafts/python/sqlalchemy/sa20/savepoint_demo.py:50: RemovedIn20Warning: Deprecated API features detected! These feature(s) are not compatible with SQLAlchemy 2.0. To prevent incompatible upgrades prior to updating applications, ensure requirements files are pinned to "sqlalchemy<2.0". Set environment variable SQLALCHEMY_WARN_20=1 to show all deprecation warnings. Set environment variable SQLALCHEMY_SILENCE_UBER_WARNING=1 to silence this message. (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9) | |
with sm() as session, session.begin(): | |
2023-09-14 09:38:57,038 INFO sqlalchemy.engine.Engine BEGIN (implicit) | |
2023-09-14 09:38:57,039 INFO sqlalchemy.engine.Engine SELECT job.id, job.state | |
FROM job ORDER BY job.id | |
2023-09-14 09:38:57,039 INFO sqlalchemy.engine.Engine [generated in 0.00006s] {} | |
2023-09-14 09:38:57,040 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_1 | |
2023-09-14 09:38:57,040 INFO sqlalchemy.engine.Engine [no key 0.00005s] {} | |
2023-09-14 09:38:57,040 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine [generated in 0.00006s] {'state': 'updated', 'job_id': 13} | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_1 | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine [no key 0.00005s] {} | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_2 | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine [cached since 0.0007861s ago] {'state': 'updated', 'job_id': 14} | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_2 | |
2023-09-14 09:38:57,041 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_3 | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine [cached since 0.001357s ago] {'state': 'updated', 'job_id': 15} | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_3 | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_4 | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine [no key 0.00005s] {} | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:38:57,042 INFO sqlalchemy.engine.Engine [cached since 0.001909s ago] {'state': 'updated', 'job_id': 16} | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_4 | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_5 | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine [cached since 0.002437s ago] {'state': 'updated', 'job_id': 17} | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_5 | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:38:57,043 INFO sqlalchemy.engine.Engine COMMIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(.venv_sa14) rivendell$ python savepoint_demo.py | |
2023-09-14 09:43:45,991 INFO sqlalchemy.engine.Engine select pg_catalog.version() | |
2023-09-14 09:43:45,991 INFO sqlalchemy.engine.Engine [raw sql] {} | |
2023-09-14 09:43:45,992 INFO sqlalchemy.engine.Engine select current_schema() | |
2023-09-14 09:43:45,992 INFO sqlalchemy.engine.Engine [raw sql] {} | |
2023-09-14 09:43:45,993 INFO sqlalchemy.engine.Engine show standard_conforming_strings | |
2023-09-14 09:43:45,993 INFO sqlalchemy.engine.Engine [raw sql] {} | |
2023-09-14 09:43:45,993 INFO sqlalchemy.engine.Engine BEGIN (implicit) | |
2023-09-14 09:43:45,993 INFO sqlalchemy.engine.Engine select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where pg_catalog.pg_table_is_visible(c.oid) and relname=%(name)s | |
2023-09-14 09:43:45,993 INFO sqlalchemy.engine.Engine [generated in 0.00008s] {'name': 'job'} | |
2023-09-14 09:43:45,994 INFO sqlalchemy.engine.Engine COMMIT | |
2023-09-14 09:43:45,994 INFO sqlalchemy.engine.Engine BEGIN (implicit) | |
2023-09-14 09:43:45,995 INFO sqlalchemy.engine.Engine SELECT job.id, job.state | |
FROM job ORDER BY job.id | |
2023-09-14 09:43:45,995 INFO sqlalchemy.engine.Engine [generated in 0.00005s] {} | |
2023-09-14 09:43:45,996 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_1 | |
2023-09-14 09:43:45,996 INFO sqlalchemy.engine.Engine [no key 0.00005s] {} | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine [generated in 0.00006s] {'state': 'updated', 'job_id': 13} | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_1 | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine [no key 0.00005s] {} | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_2 | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:43:45,997 INFO sqlalchemy.engine.Engine [cached since 0.000822s ago] {'state': 'updated', 'job_id': 14} | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_2 | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine [no key 0.00005s] {} | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_3 | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine [cached since 0.001418s ago] {'state': 'updated', 'job_id': 15} | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_3 | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_4 | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:43:45,998 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine [cached since 0.002s ago] {'state': 'updated', 'job_id': 16} | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_4 | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine SAVEPOINT sa_savepoint_5 | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine UPDATE job SET state=%(state)s WHERE job.id = %(job_id)s | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine [cached since 0.002572s ago] {'state': 'updated', 'job_id': 17} | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine RELEASE SAVEPOINT sa_savepoint_5 | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine [no key 0.00004s] {} | |
2023-09-14 09:43:45,999 INFO sqlalchemy.engine.Engine COMMIT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment