Skip to content

Commit 7242abd

Browse files
committed
update sqlalchemy in conccurent threads and tasks
1 parent 1809fbd commit 7242abd

1 file changed

Lines changed: 22 additions & 64 deletions

File tree

docs/posts/2019/2019-05-14-using-python-sqlalchemy-session-in-multithreading.md

Lines changed: 22 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,89 +4,47 @@ authors:
44
categories:
55
- python
66
- multithreading
7+
- asyncio
78
- sqlalchemy
89
comments: true
910
date:
1011
created: 2019-05-14
11-
description: Using Python SQLAlchemy session in multithreading by using contextmanager
12-
or scope_session.
12+
updated: 2025-05-14
13+
description: Using Python SQLAlchemy session in multithreading and asyncio by using context manager
14+
or scoped_session.
1315
---
1416

15-
# Using Python SQLAlchemy session in multithreading
17+
# Using Python SQLAlchemy session in concurrent threads or tasks
1618

17-
SQLAlchemy DB session is [not thread safe](https://docs.sqlalchemy.org/en/13/orm/session_basics.html#is-the-session-thread-safe). In this post, I will show you 2 ways to use it in a multithreading context.
19+
SQLAlchemy DB session is [not thread safe](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#is-the-session-thread-safe-is-asyncsession-safe-to-share-in-concurrent-tasks) for both sync or async session. [AsyncSession](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.AsyncSession) is only a thin proxy on top of a [Session](https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session)
1820

19-
<!-- more -->
20-
21-
## Way 1 - Using contextmanager to create a session per thread
22-
23-
Below is an example given by the official doc to show how to use the [contextmanager](https://docs.sqlalchemy.org/en/13/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it) to construct, commit and close a SQLAlchemy session.
24-
25-
```python
26-
### another way (but again *not the only way*) to do it ###
27-
28-
from contextlib import contextmanager
21+
> The concurrency model for SQLAlchemy's `Session` and `AsyncSession` is therefore Session per thread, AsyncSession per task.
22+
> The best way to ensure this use is by using the [standard context manager pattern](https://docs.sqlalchemy.org/en/20/orm/session_basics.html#session-getting) locally within the top level Python function that is inside the thread or task, which will ensure the lifespan of the `Session` or `AsyncSession` is maintained within a local scope.
23+
> For applications that benefit from having a "global" `Session` where it's not an option to pass the [Session](https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session) object to specific functions and methods which require it, the [scoped_session](https://docs.sqlalchemy.org/en/20/orm/contextual.html#sqlalchemy.orm.scoped_session) approach can provide for a "thread local" Session object; see the section [Contextual/Thread-local Sessions](https://docs.sqlalchemy.org/en/20/orm/contextual.html#unitofwork-contextual) for background. Within the asyncio context, the [async_scoped_session](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.async_scoped_session) object is the asyncio analogue for [scoped_session](https://docs.sqlalchemy.org/en/20/orm/contextual.html#sqlalchemy.orm.scoped_session), however is more challenging to configure as it requires a custom "context" function.
2924
25+
<!-- more -->
3026

31-
@contextmanager
32-
def session_scope():
33-
"""Provide a transactional scope around a series of operations."""
34-
session = Session()
35-
try:
36-
yield session
37-
session.commit()
38-
except:
39-
session.rollback()
40-
raise
41-
finally:
42-
session.close()
43-
44-
45-
def run_my_program():
46-
with session_scope() as session:
47-
ThingOne().go(session)
48-
ThingTwo().go(session)
49-
```
50-
51-
Suppose we have a function called `f1` which does something with the session. And we need to call `f1` in a multithreading context.
52-
All we need to do is to add the `session_scope()` around the `f1`:
27+
## Way 1 - Using context manager to create a session per thread or task
5328

5429
```python
55-
from contextlib import contextmanager
5630
from multiprocessing.dummy import Pool as ThreadPool
5731

58-
# db_utils is a python file that creats the Session by using the factory sessionmaker(),
59-
# not shown here.
60-
from db_utils import Session
61-
62-
63-
@contextmanager
64-
def session_scope():
65-
"""Provide a transactional scope around a series of operations."""
66-
session = Session()
67-
try:
68-
yield session
69-
session.commit()
70-
except:
71-
session.rollback()
72-
raise
73-
finally:
74-
session.close()
75-
32+
from sqlalchemy import create_engine
33+
from sqlalchemy.orm import sessionmaker
7634

77-
def f1(session, number):
78-
# do something around the session and the number...
35+
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")
36+
SessionFactory = sessionmaker(engine)
7937

8038

81-
def thread_worker(number):
82-
# We're using the session context here.
83-
with session_scope() as session:
84-
f1(session, number)
39+
def thread_worker(session_factory, number):
40+
with session_factory() as session:
41+
# do something around the session and the number...
42+
session.commit()
8543

8644

87-
def work_parallel(numbers, thread_number=4):
45+
def work_parallel(engine, numbers, thread_number=4):
8846
pool = ThreadPool(thread_number)
89-
results = pool.map(thread_worker, numbers)
47+
results = pool.map(thread_worker, SessionFactory, numbers)
9048
# If you don't care about the results, just comment the following 3 lines.
9149
# pool.close()
9250
# pool.join()
@@ -150,4 +108,4 @@ if __name__ == "__main__":
150108

151109
## Bonus - How the Python web frameworks work with SQLAlchemy thread local scope
152110

153-
[https://docs.sqlalchemy.org/en/13/orm/contextual.html#using-thread-local-scope-with-web-applications](https://docs.sqlalchemy.org/en/13/orm/contextual.html#using-thread-local-scope-with-web-applications)
111+
https://docs.sqlalchemy.org/en/20/orm/contextual.html#using-thread-local-scope-with-web-applications

0 commit comments

Comments
 (0)