Skip to content

Commit ebe5a53

Browse files
add docs for routing session
1 parent df12d71 commit ebe5a53

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

docs/markdown/routing-session.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Routing Session
2+
## Introduction
3+
This feature allow `session`s issued by `SQLAlchemy` to target at runtime a specific `bind` for execution by providing it's `bind_key`.
4+
5+
SQLAlchemy supports the configuration of multiple binds, but by default only supports multiple binds for the use case of defining models that are specific to only one of those binds, expressed through class `__bind_key__` attribute of the model, which defaults to (None).
6+
7+
In order to support more interesting use cases for binds, such as read-write masters, read-only replicas, etc, a solution was devised where you could very easily select an alternative bind for an already existing bound session.
8+
9+
## Use cases
10+
* read-write master
11+
* read-only replica
12+
* geo-distributed replication / isolation for replication or enforcing strict data residency compliance measures.
13+
* write-through, write-beind, write-before, caching implementations
14+
* Create a new bind from an existing bind but with different engine, execution, and connection arguments such as: autocommit, autobegin, and transaction_isolation level. While this would require defining the bind before hand, you can also achieve this behavior at runtime by using another new feature called [Bind Contexts](bind-contexts.md).
15+
*
16+
17+
## Usage
18+
Below is a brief example of how to configure SQLAlchemy for multiple binds. The example below utilizes sqlite uri strings to define a few seperate binds that all share the same in-memory virtual database, so changes made in the default (None) bind will be available in the others To be clear, no files will be created, this is a virtual construct available only within the sqlite dialect.
19+
20+
```python
21+
config = {
22+
"SQLALCHEMY_BINDS": {
23+
None: dict(
24+
url="sqlite:///file:mem.db?mode=memory&cache=shared&uri=true",
25+
connect_args=dict(check_same_thread=False),
26+
),
27+
"replica": dict(
28+
url="sqlite:///file:mem.db?mode=memory&cache=shared&uri=true",
29+
connect_args=dict(check_same_thread=False),
30+
),
31+
},
32+
}
33+
34+
app = Quart(__name__)
35+
app.config.from_mapping(config)
36+
db = SQLAlchemy(app)
37+
38+
39+
class Todo(db.Model):
40+
Mapped[int] = sa.orm.mapped_column(primary_key=True)
41+
42+
43+
async with app.app_context():
44+
db.create_all(None)
45+
```
46+
47+
Adding a single Todo to the default bind.
48+
```python
49+
async with app.app_context():
50+
db.session.add(Todo())
51+
db.session.commit()
52+
assert len(db.session.scalars(select(Todo)).all()) == 1
53+
```
54+
55+
Using routing session we can execute the same operations on a specific bind by using the method `RoutingSession.using_bind`. Note that by design, `using_bind` should be the first call in the the `Session` call chain to set the appropriate context as early as possible. Using it later in the call chain is not a tested or supported use case.
56+
57+
58+
```python
59+
async with app.app_context():
60+
assert len(db.session.using_bind("read-replica").scalars(select(Todo)).all()) == 1
61+
```

0 commit comments

Comments
 (0)