ヒトリ歩き

愚痴とかいろいろ書きます

非同期処理を試す:SQLAlchemyを使った非同期操作

FastAPI絡みで非同期処理をやるためにDB操作も非同期処理が必要なので、SQLAlchemyを使った非同期処理を試してみる。

準備

非同期処理をするために、非同期に対応したドライバのインストールが必要。 psycogp2よりも3倍早いらしい、asyncpgをインストールする。

pip install asyncpg

github.com

これをやる

SQLAlchemyのページにある非同期のサンプルを動かす。

docs.sqlalchemy.org

エンジンの生成

非同期の場合、エンジンインスタンスを生成する際は、create_async_engineを実行する。 また、ドライバはasyncpgを使用する。

    url = "postgresql+asyncpg://postgres:example@localhost:5432/postgres"
    engine = create_async_engine(url, echo=True)

コネクションを非同期コンテキストマネージャを使って実行

async withを使用して、DBとのコネクションを生成する。実行もawaitを使用する。

    async with engine.connect() as conn:
        result = await conn.execute(select(t1).where(t1.c.name == "some name 1"))
        print(result.fetchall())

非同期でのORM

func.now()が効かない??

SQLAlchemyに載っているORMのサンプルを写経してみたが、func.now()がうまく動いてないみたい?

docs.sqlalchemy.org

下記のエラーが発生している。

  File "asyncpg/protocol/protocol.pyx", line 207, in bind_execute
asyncpg.exceptions.NotNullViolationError: null value in column "create_date" of relation "a" violates not-null constraint
DETAIL:  Failing row contains (9, a1, null).
from sqlalchemy import func

応急処置として、create_dateに現在時刻を設定するように変更したら、データを登録して検索もできた。

with async_session()だけを実行して、SQLの発行をしている箇所とwith async_session()の後にwith session.begin()を実行している箇所がある。
これはwith句を抜けた後に、自動でコミット/ロールバックするかしないかの違いになる。
with session.begin()は、トランザクションが開始されるので自動でコミット/ロールバックが実行される。

  • beginあり
    async with async_session() as session:
        async with session.begin():
  • beginなし
    async with async_session() as session:
        stmt = select(A).order_by(A.id).options(selectinload(A.bs))
        result = await session.execute(stmt)

with句を2段で書かずに、以下のようにできる。

    async with async_session.begin() as session:

最後に

SQLAlchemyでの非同期処理がなんとなく分かったので、次はFastAPI+SQLAlchemyをやってみたいと思う。

参考

qiita.com