かみぽわーる

kamipo's blog

Rails 6.0の複数DBでリードレプリカのテストするのたぶん大変

Rails 6.0の複数DBのレビューしてるときに気づいたことなんですけど、たぶんリードレプリカからデータを読むテストをするのたぶん大変だと思われます。

うちの業務のアプリでActive Recordが更新を検知できない方法でデータが更新されるとテストがコケるという問題が以前にあり、これと同じ構造の問題がマスターのコネクションで更新したときマスターのコネクションのクエリキャッシュはクリアされるけどリードレプリカのコネクションのクエリキャッシュは残ったままというのがあるよね、というのをテストコードで示そうと思ったときのことである。

github.com

通常RailsアプリでDBつかったテストをするとき、テストの中で変更されたデータを毎回初期状態に戻すのにフィクスチャーをロードし直すのは時間がかかって効率がわるいので、テストケースに入る前にトランザクションを開始しといてテストケース終わったらロールバックして変更をなかったことにすることで初期状態に戻し、時間のかかるフィクスチャーロードし直しを避けるということが効率のために行われています。

この、効率のためにテストケース終わったらロールバックして変更をなかったことにするためにテスト内のトランザクションはネストしたトランザクションになっていて実際にコミットは起きないということが複数DBのリードレプリカと相性がとてもわるい。実際にコミットが起きないことにはリードレプリカに更新がレプリケートされてこないのでずっと時が止まった状態なのである。

あたかもマスターで更新が起きたっぽいときにリードレプリカにも更新が伝搬しているかのように見せかけるため、Active Recordはテストのときデフォルトですべてのコネクションプールをマスターのコネクションプールにすり替えるということで対処している。コミットが起きないんだったら全部マスターに接続してテストすればいいじゃないってやつです。しかしこれをされるとマスターではなくリードレプリカからデータを読んでることをテストしたいときにめっちゃこまるという話である。

github.com

コード読んだ限りではフィクスチャー毎回読み直しモードのときは setup_shared_connection_pool は呼ばれないから大丈夫そうに見えるけど、自分が試したときにはフィクスチャー毎回読み直しモードのときでも setup_shared_connection_pool の効果を自力で打ち消さないとまったくリードレプリカに接続できなくてめっちゃ苦労したので、先人の記憶としてここに書き記しておきます。同じ問題にぶち当たったりぶち当たらなかったりなんか分かった人いて教えてくれたらここに追記します。

あと、リードレプリカでクエリキャッシュ残る問題はがんばってテスト書いて修正される運びとなったのでこれで更新の多い日も安心ですね。

github.com