Upgrade to Pro — share decks privately, control downloads, hide ads and more …

cls-hooked による実行コンテキストの保存と利用

cls-hooked による実行コンテキストの保存と利用

NestJS meetup #5

Hiroaki KARASAWA

March 31, 2023
Tweet

More Decks by Hiroaki KARASAWA

Other Decks in Programming

Transcript

  1. cls-hooked による実行コンテキストの保存と利用
    Hiroaki KARASAWA from dinii, inc
    NestJS meetup online #5
    2023/03/31

    View Slide

  2. 2
    自己紹介
    ● 氏名
    ○ 唐澤弘明 or @karszawa
    ● 所属
    ○ 株式会社 dinii
    ○ 飲食店の POS システムやモバイルオーダーを作っています
    ○ 【顧客情報 x 喫食情報】で飲食業界で初めて CRM を実現しています
    ● 興味
    ○ TypeScript, React, GraphQL
    ○ NestJS 歴 ⇒ 3年(ダイニーに入社してから)

    View Slide

  3. 飲食店の All in One SaaS
    “ダイニー”

    View Slide

  4. dinii と NestJS
    4
    ● ダイニーのバックエンドは NestJS によるモノリスです
    ○ 最近 Modular Monolith っぽく分割し始めた
    ユーザー数【450万人】到達! 🎉🎉🎉

    View Slide

  5. 【本題】
    ダイニーにおける cls-hooked の活用
    5

    View Slide

  6. cls-hooked is …
    6
    Continuation-Local Storage ( Hooked )
    ● Async hooks の便利なラッパー
    Async hooks is …
    ● Node.js の API で非同期処理のライフサイクルを追跡できる 🤨
    ● その関数がどういうコンテキストで呼び出されたかを記録できる 🤔
    ● 関数の呼び出しごとに呼び出しの粒度で情報を保持できる 🧐

    View Slide

  7. 関数の呼び出しごとに呼び出しの粒度で情報を保持できる
    7

    ● Async hooks を使わないならこんな感じ
    ● もちろん main がたくさん呼び出されると上手く行かない

    View Slide

  8. cls-hooked を利用するとこうなる
    8

    `context` が cls-hooked によって作成されている

    View Slide

  9. 便利ですよね?
    9

    View Slide

  10. ダイニーでの活用例を紹介します
    10

    View Slide

  11. 実用例 ① リクエストごとの id を保存してロガーで出力
    11
    ヘッダーから x-request-id を抽出
    事前に定義した context にセット
    context を作成(グローバルで OK)
    AppModule にて利用
    ※ Express.js の app.use と同じ
    NestMiddleware として実装できる

    View Slide

  12. 実用例 ① リクエストごとの id を保存してロガーで出力
    12
    先程の context から値を取得
    logger で出力
    ※ ちなみに winston を使っています
    ※ ちなみに winston の出力先はこんな感じ
    開発環境 ⇒ Human readable format
    本番環境 ⇒ JSON で標準出力 + Sentry

    View Slide

  13. 実用例 ② NestJS x TypeORM
    13
    データベースのトランザクションか否かのコンテキストを保存してトランザクション中での利用であればトランザクション用のコ
    ネクションを利用する
    ● その関数がトランザクション内で実行されていれば失敗時はロールバックして欲しい
    ● トランザクションではないなら不要な処理はしないで欲しい
    ● それぞれ書き分けるのは面倒くさい
    というときありませんか?

    View Slide

  14. 実用例 ② トランザクションのコンテキスト
    14
    odavid/typeorm-transactional-cls-hooked というライブラリの実装にて cls-hooked が活用されています

    View Slide

  15. 実用例 ② トランザクションのコンテキスト
    15
    おもむろに runOnTransactionalCommit という関数を
    呼び出すとトランザクションコミット時に処理を実行してくれ

    (これも呼び出しのコンテキストをストレージとして利用して
    いる)

    View Slide

  16. 実用例 ② トランザクションのコンテキスト
    16
    NestJS での TypeORM の実践に関してはこちらの記事もご覧ください!

    View Slide

  17. 実用例 ③ Transactional を魔改造
    17
    OLAP 的なメソッドにおいてリードレプリカの利用を強制する
    ● 重い分析系のクエリを間違ってもプライマリのデータベースに向けたくない
    ● あるメソッド以下では絶対に特定のコネクション(レプリカ向け)を利用させる

    View Slide

  18. 実用例 ③ Transactional を魔改造
    18
    デフォルトのデータソース
    レプリカのデータソース
    Analytical のアノテーションが付いているかどうかを
    cls-hooked で保持し、付いていればレプリカの方の
    DataSource を使うよう Repository にパッチをあてる

    View Slide

  19. まとめ
    19

    View Slide

  20. まとめ
    20
    ● cls-hooked について説明しました
    ● アイデア次第でいろいろなことができる
    ● 実現不可能と思っていた難しい課題があっさり解決してしまうことも
    ● 実は 500 行ぐらいしかないライブラリなので読んでみて下さい

    View Slide