|
61 | 61 | <li class="toctree-l2"><a class="reference internal" href="#manually-close-the-transaction-and-session">Manually close the transaction and session</a> |
62 | 62 | </li> |
63 | 63 | <li class="toctree-l2"><a class="reference internal" href="#multiple-sessions-and-concurrent-execution">Multiple sessions and concurrent execution</a> |
64 | | - <ul> |
65 | | - <li class="toctree-l3"><a class="reference internal" href="#read-your-own-writes">Read Your Own Writes</a> |
66 | 64 | </li> |
67 | | - <li class="toctree-l3"><a class="reference internal" href="#rollback">Rollback</a> |
| 65 | + <li class="toctree-l2"><a class="reference internal" href="#rollback">Rollback</a> |
68 | 66 | </li> |
69 | | - </ul> |
| 67 | + <li class="toctree-l2"><a class="reference internal" href="#read-your-own-writes">Read Your Own Writes</a> |
70 | 68 | </li> |
71 | 69 | </ul> |
72 | 70 | </li> |
@@ -238,7 +236,7 @@ <h3 id="manually-close-the-transaction-and-session">Manually close the transacti |
238 | 236 | ) |
239 | 237 | await session.execute(stmt) |
240 | 238 | </code></pre> |
241 | | -<h2 id="multiple-sessions-and-concurrent-execution">Multiple sessions and concurrent execution</h2> |
| 239 | +<h3 id="multiple-sessions-and-concurrent-execution">Multiple sessions and concurrent execution</h3> |
242 | 240 | <pre><code class="language-python">import asyncio |
243 | 241 |
|
244 | 242 | from context_async_sqlalchemy import ( |
@@ -309,6 +307,73 @@ <h2 id="multiple-sessions-and-concurrent-execution">Multiple sessions and concur |
309 | 307 | await session.execute(stmt) |
310 | 308 | await session.commit() |
311 | 309 | </code></pre> |
| 310 | +<h3 id="rollback">Rollback</h3> |
| 311 | +<pre><code class="language-python">from context_async_sqlalchemy import db_session |
| 312 | +from sqlalchemy import insert |
| 313 | + |
| 314 | +from ..database import connection |
| 315 | +from ..models import ExampleTable |
| 316 | + |
| 317 | + |
| 318 | +async def handler_with_db_session_and_exception() -> None: |
| 319 | + """ |
| 320 | + let's imagine that an exception occurred. |
| 321 | + """ |
| 322 | + session = await db_session(connection) |
| 323 | + stmt = insert(ExampleTable).values(text="example_with_db_session") |
| 324 | + await session.execute(stmt) |
| 325 | + |
| 326 | + raise Exception("Some exception") |
| 327 | + # transaction automatically rolls back |
| 328 | +</code></pre> |
| 329 | +<pre><code class="language-python">from fastapi import HTTPException |
| 330 | + |
| 331 | +from context_async_sqlalchemy import db_session |
| 332 | +from sqlalchemy import insert |
| 333 | + |
| 334 | +from ..database import connection |
| 335 | +from ..models import ExampleTable |
| 336 | + |
| 337 | + |
| 338 | +async def handler_with_db_session_and_http_exception() -> None: |
| 339 | + """ |
| 340 | + let's imagine that an http exception occurred. |
| 341 | + """ |
| 342 | + session = await db_session(connection) |
| 343 | + stmt = insert(ExampleTable).values(text="example_with_db_session") |
| 344 | + await session.execute(stmt) |
| 345 | + |
| 346 | + raise HTTPException(status_code=500) |
| 347 | + # transaction rolls back automatically by status code |
| 348 | +</code></pre> |
| 349 | +<pre><code class="language-python">from context_async_sqlalchemy import db_session, rollback_db_session |
| 350 | +from sqlalchemy import insert |
| 351 | + |
| 352 | +from ..database import connection |
| 353 | +from ..models import ExampleTable |
| 354 | + |
| 355 | + |
| 356 | +async def handler_with_db_session_and_manual_rollback() -> None: |
| 357 | + """ |
| 358 | + An example of a handle that uses a rollback |
| 359 | + """ |
| 360 | + # it's convenient this way |
| 361 | + await _insert() |
| 362 | + await rollback_db_session(connection) |
| 363 | + |
| 364 | + # but it's possible this way too |
| 365 | + await _insert() |
| 366 | + session = await db_session(connection) |
| 367 | + await session.rollback() |
| 368 | + |
| 369 | + |
| 370 | +async def _insert() -> None: |
| 371 | + session = await db_session(connection) |
| 372 | + stmt = insert(ExampleTable).values( |
| 373 | + text="example_with_db_session_and_manual_close" |
| 374 | + ) |
| 375 | + await session.execute(stmt) |
| 376 | +</code></pre> |
312 | 377 | <h3 id="read-your-own-writes">Read Your Own Writes</h3> |
313 | 378 | <p>The "Read Your Own Writes" pattern ensures that after a write request, subsequent read requests |
314 | 379 | always see the data that was just written — even when reads are served by a replica.</p> |
@@ -389,74 +454,6 @@ <h3 id="read-your-own-writes">Read Your Own Writes</h3> |
389 | 454 | <p>The <a href="https://github.com/krylosov-aa/pg-status">pg-status</a> provides a ready-made helper |
390 | 455 | for exactly this: it lets you poll replicas and pick the first one whose replay LSN is at or |
391 | 456 | ahead of the required value, falling back to the primary if none qualifies.</p> |
392 | | -<hr /> |
393 | | -<h3 id="rollback">Rollback</h3> |
394 | | -<pre><code class="language-python">from context_async_sqlalchemy import db_session |
395 | | -from sqlalchemy import insert |
396 | | - |
397 | | -from ..database import connection |
398 | | -from ..models import ExampleTable |
399 | | - |
400 | | - |
401 | | -async def handler_with_db_session_and_exception() -> None: |
402 | | - """ |
403 | | - let's imagine that an exception occurred. |
404 | | - """ |
405 | | - session = await db_session(connection) |
406 | | - stmt = insert(ExampleTable).values(text="example_with_db_session") |
407 | | - await session.execute(stmt) |
408 | | - |
409 | | - raise Exception("Some exception") |
410 | | - # transaction automatically rolls back |
411 | | -</code></pre> |
412 | | -<pre><code class="language-python">from fastapi import HTTPException |
413 | | - |
414 | | -from context_async_sqlalchemy import db_session |
415 | | -from sqlalchemy import insert |
416 | | - |
417 | | -from ..database import connection |
418 | | -from ..models import ExampleTable |
419 | | - |
420 | | - |
421 | | -async def handler_with_db_session_and_http_exception() -> None: |
422 | | - """ |
423 | | - let's imagine that an http exception occurred. |
424 | | - """ |
425 | | - session = await db_session(connection) |
426 | | - stmt = insert(ExampleTable).values(text="example_with_db_session") |
427 | | - await session.execute(stmt) |
428 | | - |
429 | | - raise HTTPException(status_code=500) |
430 | | - # transaction rolls back automatically by status code |
431 | | -</code></pre> |
432 | | -<pre><code class="language-python">from context_async_sqlalchemy import db_session, rollback_db_session |
433 | | -from sqlalchemy import insert |
434 | | - |
435 | | -from ..database import connection |
436 | | -from ..models import ExampleTable |
437 | | - |
438 | | - |
439 | | -async def handler_with_db_session_and_manual_rollback() -> None: |
440 | | - """ |
441 | | - An example of a handle that uses a rollback |
442 | | - """ |
443 | | - # it's convenient this way |
444 | | - await _insert() |
445 | | - await rollback_db_session(connection) |
446 | | - |
447 | | - # but it's possible this way too |
448 | | - await _insert() |
449 | | - session = await db_session(connection) |
450 | | - await session.rollback() |
451 | | - |
452 | | - |
453 | | -async def _insert() -> None: |
454 | | - session = await db_session(connection) |
455 | | - stmt = insert(ExampleTable).values( |
456 | | - text="example_with_db_session_and_manual_close" |
457 | | - ) |
458 | | - await session.execute(stmt) |
459 | | -</code></pre> |
460 | 457 |
|
461 | 458 | </div> |
462 | 459 | </div><footer> |
|
0 commit comments