1. tanaken on Rails
  2. #025: shard_keys, assert_dif..
2024-06-23 22:49

#025: shard_keys, assert_difference

Summary

今週のtanaken on Railsポッドキャストエピソードでは、アクティブレコードにおける水平シャーディングやプレリクエストの紹介が行われています。アサーションメソッド「assert_difference」や「shard_keys」について説明し、アサーションディファレンスメソッドの使い方についても説明します。

00:00
こんにちは、第25回目のtanaken on Railsです。今週もプレリクエストを紹介していきます。
今週は二つですね、紹介します。一つ目が、add.shardkeys.sharded and .connectedToAllShardsMethodsToARModelsというプレリクエストです。
アクティブレコードにおける水平シャーディングの紹介
こちらは、アクティブレコードに関する変更です。まず、アクティブレコードにおける水平シャーディングについて簡単に触れようと思います。
水平シャーディングは、RailsGuard、RailsGuideのアクティブレコードマルチデータベースというところに記載されております。
水平シャーディングに関しては、引用しますね。水平シャーディングとは、データベースを分割して、各データベースサーバーの行数を減らしながら、
シャード、シャード、SHARD、全体で同じスキーマを維持することですと、データベースの行を減らして分割するということですね。
中略で モデルはSharsキーを介して Connect to APIに接続されますと そんな
ことが書いてあります 例として 次のような実装を紹介してみます
まず ShardedBaseというクラスを定義 してみましょう これは名前は何
でもいいんですけど ShardedBase シャーディングするモデルで使う
ようのベースクラスですね この クラスはアクティブレコードコロンコロン
ベースを継承したクラスとして 定義しましょう クラスの中身の
実装はself.abstract Underscoreクラス イコールトゥルー Connect toで引数
にシャーズを渡してます シャーズ の中身がハッシュで ハッシュの
キーがShard1とShard2という二つの キーを持つハッシュを渡しています
Shard1 Shard2 それぞれの値もハッシュ で そのハッシュはWritingというキー
を持つハッシュです Shard1のほう はWriting Shard1 Shard2のほうはWriting
Shard2というハッシュを渡してますね どのシャードにつなぐかという
設定だと思ってもらえればいい と思います Shard1 2はそれぞれデータ
ベースYAMLのほうに定義するという 感じですね 今 ShardedBaseというクラス
を定義しました このクラスをシャーディング するモデルで継承します 例えば
ユーザーモデル クラスユーザー 継承 ShardedBaseという形でクラス
ユーザークラスを定義すると そんな 形で 例えばController ユーザーズ
コントローラーでそれぞれのシャード につないで 例えばユーザーを
作るという処理を書いてみましょう と ユーザーズコントローラー
を定義します ビフォーアクション でSetShardというのをやってみましょう
SetShardというメソッドはビフォー アクションで指定したメソッド
で これの中身は 別にこのメソッド名 何でもいいんですけど Shardを
セットするよというのでSetShard というメソッド名にしておいて
Shardは例えば ユーザーの 今から 登録しているユーザーの 例えば
カンパニーIDというものがあれば そのカンパニーIDの偶数奇数で
Shard1につなぐかShard2につなぐか というのを決めるというような
感じで実装してみようということ が例えばできます ShardをカンパニーID
パラメーターからカンパニーID 取ってきて Shardどっちかなと 今回
は偶数だったらShard1 奇数だったら Shard2にしようという分岐を書いて
どっちかのShardになるわけですね Shard1かShard2に 実際にShardをセット
するときはuser.connected2でキーワード 引数にShard 値としてはShard1かShard2
のどっちかを渡すで括弧閉じ これでuser.connected2という形でどっち
のShardにつながるかというのが ここで指定されたわけですね その
connected2のブロックとしてYieldを 渡すというような形ですね そんな
形でやると 例えばuser.createって やったときにセットShardしてる
ので これbeforeアクションじゃなくて aroundアクションかな そのコネクション
アクション中はそのShardに接続 した状態で処理を実行するという
ようなことができますよという 感じですね そんな感じでございます
といった形で何らかのロジック でどっちのShardにつなぐかという
のを決めた上でレコードを登録 するというようなことが例えば
できるよねという感じですね 今回のプールリクエストでは何を
してるのかというと このシャーディング に関して三つの新しいメソッド
を追加しています 一つ目がShardKeysというメソッド
です これはShardのキーの一覧を取得 するメソッドですね さっきの例
でいうとShard1とShard2ってやつ これを取得するメソッドです
プレリクエストの紹介
これまではいいメソッドがなかった らしいんですよね ちょっとあんまり
僕理解が追いついてないんですけど モデルの接続をループする方法
以外では簡単にShardのキーの一覧 を取得する方法はなかったという
ふうに書いてありました モデル の接続をループするというのが
いまいちピンときてないんですけど どっちかの複数 例えば2個のシャード
があったらどっちかに接続する わけですよね どっちに接続する
かっていうのを何らかの方法で 決めてると思うんですけど どういう
ことなんだろうな これどのモデル はどっちのシャードに接続する
とかがあるのかもしれなくて その 辺りでキーを取得するという方法
がこのメソッドの実装前はあった のかな なんかいまいちピンときて
ないけど なかったらしいです 今までは 簡単にShardキーズっていう
形でキーを取得する方法がなかった みたいですね 近しいメソッドが
なかったんかなと思って調べて みたんですけど DBレイヤーでシャード
の名前を取得する方法っぽいのは 何か見つかりました シャードネイム
っていうメソッドがありまして それは APIには載ってなかったんですけど
Railsの中身をRailsのリポジトリ で検索したら Active Record Connection
AdaptersPoolManagerというクラスがあって その中にシャードネイムズっていう
のがあったんですよ これは違う のかなと思って見てたんですけど
多分 これはさっきのシャードベース シャーデッドベースとかって Active
Recordベースを継承して作ったクラス の中で シャード1 シャード2とかって
キーを定義したんですけど そいつ のことじゃ多分なくて シャーディング
しているDBの方のネームなのかな っていうのが取れるのかなという
ふうに読みました ちょっと分かんない けれども なんで シャードネイム
というメソッドはあったものの 何か違うもの 今回のRailsの実装
で実装者が定義したシャードの キーを取得する方法とはまた
違うものだったみたいですね 多分 のように読みました ちょっと分
かんない ちょっと確信はないんです けどという感じなんで 一応 その
メソッドのリンクも貼っておこう かしらという感じですね という
ところで なんか便利なシャード キーというメソッドが増えました
よというのが1個目 2個目のメソッド はsharded?というメソッドですね
これはモデルが複数のシャード に接続し得るモデルなのか そう
じゃないのかというのを判断して True Falseを返すというメソッド
ですね さっきの例で言うと ユーザー クラスはshardedベースを継承してる
んで Trueが返ってくるというような 感じですね 逆にshardedクラスを
継承してない 普通にアクティブレコード ベースを継承しただけのクラス
は Falseが返ってくるという感じ かなと思いますね
3つ目 Connected to all shards これはこの メソッドに渡したブロックの中の
処理を全てのシャードに対して 全てのシャードに接続して実行
するというメソッドです だから シャードが2つあれば2つ 10個あれば
10個の全てのシャードに接続して それぞれ接続した上でブロック
内の処理を実行してくれるよと そんなメソッドですね 逆に今まで
そういうのなかったんじゃなという 感じですね 僕はRailsで水平シャーディング
やった経験が今のところないので ほーんという感じですが もう
ちょっと手元でサンプルアプリケーション とか作ってやってみると そういうこと
かというのが分かるのかなという 感じがしてますが 今のところそんな
感じにはピンときてないですが ちょっと便利になったよという
お話でございました 続いて2つ目のプレリクエストです
アクティブサポートの変更
タイトルがRails Regenerate Assertion Failure Messagesというタイトルです
こちらはアクティブサポートに関する 変更です このプレリクエストなんですけ
れども 前提として2週間ほど前 6月7日に作られたプレリクエスト
が関連しているので まずはそちら から説明します そっちのプレリクエスト
のタイトルはImprove Error Message When Passing a Proc to AssertDifference or AssertChanges
というタイトルです アサーションメソッド いくつか たくさんあるうち アサート
ディファレンスメソッド または アサートチェンジメソッドを使う
メソッドのエラーメッセージが 変更されているということです
ここで僕 アサートディファレンス メソッドについて知らなかった
ので まずアサートディファレンス メソッドについても説明していき
たいと思います アサートディファレンス メソッド これ引数が何パターン
かいろんな形式で渡せるんです けど 主要な使い方をピックアップ
してお伝えすると このメソッド は第一引数は評価される式 第二
引数が期待される変化の値 デフォルト は1です 第二引数は このメソッド
に渡すブロックが実行されるコード っていう感じですね 何を言ってる
かというと ブロック内のコード を実行して その実行前後で第一
引数に渡した式の評価結果がどの ように変化するかと 変化の量が
第二引数に渡した変化の量と一致 してるかどうかっていうのをチェック
するというようなことですね 例えば 次のようなコードです アサート
ディファレンスというメソッド に第一引数で文字列でアーティクル
.カウントというのを渡します 第二引数に 例えば2という数字を渡します
ブロックの中にポストクリエイト パラムズホゲホゲという形でクリエイト
メソッドを使ってポストリクエスト を送るという処理をブロックの中で
2回やると これ何をやってるかという と 第一引数に渡した文字列のアーティクル
.カウント 大文字アーティクル .カウントですね つまりアーティクル
モデルのカウントメソッド アーティクル のレコード数を評価するという形
ですね ブロック内のポストクリエイト を2回実行することによって 正しく
実行されてれば 正常に処理が終わ ってれば アーティクルの件数が
2個増えてるはずだよねという ので 第二引数はその期待値ですね
2個変化するはずというのを渡す と テストの結果として ちゃんと
処理が実装されてれば レコード は2件増えるはずなので アーティクル
.カウントが前後で2件増えたね と そういうテストがかかっている
よと そういうメソッドになっています 今紹介した例では 第一引数に文字
列を渡していたんですけども これは文字列じゃなくて プロック
オブジェクトを渡すこともできます プロック入してもいいし ラムダ
式を渡しても別にいいんですけど 例えばアサートディファレンス
でラムダの括弧内でアーティクル.カウント で 第二引数は2 先ほどと同じですね
2という形で 中身は一緒です ブロック もポストクリエイトを2回やると
本当に第一引数だけ さっき文字列 だったところが プロックオブジェクト
になったよと そういう書き方も できますよということです
これがアサートディファレンスという メソッドの紹介でしたと
エラーメッセージの改善
プルリクエストの話に戻します その2週間前のプルリクエストでは
このアサートディファレンスメソッド のエラーメッセージを変更したという
話をしてました 例に示したとおり アサートディファレンスメソッド
の第一引数には文字列 ストリング も渡せるし プロックオブジェクト
も渡せると そのプルリクエスト の方の著者さんの主張では いろいろ
議論はあるが第一引数はストリング よりもプロックオブジェクトを渡した
ほうがいいというふうに私たち のチームは考えていますということ
を筆者さんは言っています その 理由としては プロックオブジェクト
であれば プロック内のコードが 純粋なRubyのコード 純粋なという
か普通にRubyのコードになるので 例えばRubocopとかLintツールでLint
することもできるし Rubyコード としての解析ができるので 例えば
何かエラーがあれば IDEとかで 気づきやすいしというので 文字
列として第一引数で 例えばさっき のarticle.countみたいなのを渡す
よりも プロックオブジェクトで article.countっていうのを書いて渡
したほうが分かりやすいよねという ようなことを言っていました
だから基本的にはプロックオブジェクト を第一引数で渡すようにしたいん
だよねと言っていて ただ プロックオブジェクトを渡した
場合にアサンションが失敗した つまり期待値通りじゃなかった
ときに出てくるエラーメッセージ がすごい分かりにくいんだけど
ということを指摘しています どんなふうに分かりにくかった
かというと エラーメッセージが プロックオブジェクトを.inspect
というインスペクトメソッドを 読んだものを表示しているという
感じになっていました なのでエラーメッセージを見る
と シャープ 何ていうのこれ 中 括弧 何括弧って言うんだろう
シャープ括弧 プロックコロン 0xなんちゃらかんちゃらみたいな
そのハッシュ値になってどのファイル どの場所のみたいな このプロック
オブジェクトが期待値2に対して 実際は変化してませんでした
とかそういうエラーになっている と これだと何が何やら分からん
と プロックオブジェクトの何らか でエラーになったのは分かるけど
そのプロックオブジェクトの中身 が何なのか分からないからエラーメッセージ
がすごい分かりにくいと そんな ことを言っているわけなんですね
なので そのプルリクエストでは Ruby VMコロンコロンアブストラクト
シンタックスツリーAPI いわゆる ASTってよく呼ばれてるやつですね
アブストラクトシンタックスツリー ASTを利用して プロックのソースコード
を出力することでエラーメッセージ が分かりやすくなるように変更
しているっていうのが そのプルリクエスト の中身です
先ほどプロックオブジェクトの インスペクトが表示されてたような
エラーメッセージのところが プロック の中身のソースコードが出力される
ようになると こういうプロック の中身のソースコードのものが
期待値と違う 例えばチェンジ倍 2件チェンジ 変更があって欲しかった
のにゼロでしたよとか そういう 結果が出てくると 実装の中身が
分かるようになったので分かり やすくなりましたっていう そういう
プルリクエストだったんですね ここまでで2週間前のプルリクエスト
の話ができましたと 今日紹介している プルリクエストは簡単に言うと
パフォーマンスの改善をしている プルリクエストです どんなこと
かというと 2週間前のプルリクエスト ではASTを使ってプロックオブジェクト
のソースコードを出力するよう になりましたという紹介をしました
ただASTを使うとそれなりに処理 コストがかかるんですよね 多分
解析をしてプロックオブジェクト の中身のメソッドの実装を書き
出すみたいなことを多分やっている んで 時間がかかるでしょう 2週間
前のプルリクを出す前は ただプロック オブジェクトのインスペクトメソッド
を読んだ結果を表示してただけ なのに それに比べると もちろん
ASTでソースコードを出力するって なると時間がかかります そういう
のを毎回やるとテスト全体が遅 くなったりしますよね シュール
があるよねっていうことを言って て エラーメッセージを遅延評価
遅延呼び込みできるようにエラーメッセージ のメッセージ自体をプロックオブジェクト
として実装し直すっていうことを やっています もともとミニテスト
ではエラーメッセージは文字列 を渡せるし プロックオブジェクト
も渡せるようになってたみたいで プロックオブジェクトが渡って
きた場合は渡ってきた後に.コール メソッドを呼んで評価をするっていう
形の実装になっているみたいですね なのでプロックオブジェクト渡
せるんだから メッセージ自体は 毎回は評価されずに メッセージ
を表示する必要が出てきたとき だけ中身を評価して つまりさっき
の説明した通りだとASTを使って 例えばソースコードの中身を出力
するみたいな処理が本当にエラーメッセージ を出したいときだけ呼ばれるよう
になったんで 無駄な処理がなく なりますよねっていうような感じ
ですね なので今回紹介するプロジェクト ではそのエラーメッセージを単純
なストリングじゃなくてプロック オブジェクトにしているという
ような実装になっておりました という感じです 二つのプロジェクト
の紹介ですが それぞれ前提となる 知識とかの紹介があったので それ
なりの時間になってしまいました が こんなところで今週の田中オン
リリースを終わりにしたいと思います ではまた来週バイバイ
22:49

Comments

Scroll