成長するユーザーテーブルを飼いならす方法
クライアントはどのようにしてそこにたどり着きましたか?
詳細に入る前に、アプリがこの状態になる可能性があることを理解してみましょう。簡単なusers
から始めます テーブル。数週間後、最後のサインイン時刻を特定できるようにする必要があるため、users.last_sign_in_at
を追加します。 。次に、ユーザーの名前を知る必要があります。 first_name
を追加します およびlast_name
。 Twitterのハンドル?別の列。 GitHubプロファイル?電話番号?数か月後、テーブルは気が遠くなるようなものになります。
これの何が問題になっていますか?
大きな表はいくつかの問題を示しています:
User
複数の無関係な責任があります。これにより、理解、変更、テストがより困難になります。- アプリとデータベース間でデータを交換するには、追加の帯域幅が必要です。
- かさばるモデルを保存するには、アプリにさらに多くのメモリが必要です。
アプリがUser
を取得しました 認証と承認の目的ですべてのリクエストに対応しますが、通常はほんの一握りの列しか使用しませんでした。問題を修正すると、設計とパフォーマンスの両方が向上します。
テーブルの抽出
めったに使用されない列を新しいテーブルに抽出することで問題を解決できます 。たとえば、プロファイル情報を抽出できます(first_name
など)をprofiles
に追加します 次の手順で:
profiles
を作成するusers
のプロファイル関連の列を複製する列 。-
profile_id
を追加しますUser
へ 。NULL
に設定します 今のところ。 User
の各行 、profiles
に行を挿入します プロファイル関連の列を複製します。- ポイント
profile_id
users
の対応する行の 3に挿入された行に。 - しないでください
users.profile_id
を作成しますNULL
以外 。アプリはまだその存在を認識していないため、壊れてしまいます。
users.first_name
への参照を置き換える必要があります profiles.first_name
を使用 等々。少数の参照を含む少数の列のみを抽出する場合は、手動で行うことをお勧めします。しかし、私たちが「ああ、いや、これは史上最悪の仕事だ!」と考えていることに気づいたらすぐに 代替案を探す必要があります。
問題をおろそかにしないでください。 誰もが避けているコードの一部はさらに劣化し、さらに大きな不注意に苦しむでしょう 。悪循環を断ち切る最も簡単な方法は、小さく始めることです。
クライアントが問題をどのように解決したかを知りたい場合は、読んでください。
一度に1行ずつコードを修正する
最も段階的なアプローチは、一度に1つの古い列への参照を修正することです。 first_name
の移動に焦点を当てましょう users
から profiles
へ 。
まず、Profile
を作成します と:
rails generate model Profile first_name:string
次に、users
からの参照を追加します profiles
へ users.first_name
をコピーします profiles
へ :
class ExtractUsersFirstNameToProfiles < ActiveRecord::Migration
# Redefine the models to break dependency on production code. We need
# vanilla models without callbacks, etc. Also, removing a model in the future
# might break the migration.
class User < ActiveRecord::Base; end
class Profile < ActiveRecord::Base; end
def up
add_reference :users, :profile, index: true, unique: true, foreign_key: true
User.find_each do |user|
profile = Profile.create!(first_name: user.first_name)
user.update!(profile_id: profile.id)
end
change_column_null :users, :profile_id, false
end
def down
remove_reference :users, :profile
end
end
各ユーザーに1つのプロファイルを強制するため、users
からの参照 profiles
へ 反対の参照よりも望ましいです。
データベース構造が整ったら、first_name
を委任できます User
から profiles
へ 。私のクライアントにはいくつかの要件がありました:
- アクセサは、関連する
profiles
を使用する必要があります 。また、非推奨のアクセサーが呼び出された場所もログに記録する必要があります。 User
を保存するprofiles
を自動的に保存する必要があります 非推奨のアクセサーを使用してコードを壊さないようにするため。-
User#first_name_changed?
およびその他のActiveModel::Dirty
メソッドは引き続き機能するはずです。
これはUser
を意味します 次のようになります:
class User < ActiveRecord::Base
# We need autosave as the client code might be unaware of
# Profile#first_name and still reference User#first_name.
belongs_to :profile, autosave: true
def first_name
log_backtrace(:first_name)
profile.first_name
end
def first_name=(new_first_name)
log_backtrace(:first_name)
# Call super so that User#first_name_changed? and similar still work as
# expected.
super
profile.first_name = new_first_name
end
private
def log_backtrace(name)
filtered_backtrace = caller.select do |item|
item.start_with?(Rails.root.to_s)
end
Rails.logger.warn(<<-END)
A reference to an obsolete attribute #{name} at:
#{filtered_backtrace.join("\n")}
END
end
end
これらの変更後、アプリは同じように動作しますが、Profile
への追加の参照のため、少し遅くなる可能性があります (パフォーマンスが問題になる場合は、AppSignalなどのツールを使用してください)。コードは、複製できない属性も含め、レガシー属性へのすべての参照をログに記録します(例:user[attr] = ...
またはuser.send("#{attr}=", ...)
)したがって、grep
の場合でも、それらすべてを見つけることができます。 役に立たない。
このインフラストラクチャが整ったら、users.first_name
への1つの参照を修正することを約束できます。 定期的なスケジュールで、例えば毎朝(クイックウィンで一日を始めるため)または正午頃(集中した朝の後にもっと簡単なことに取り組むため)。 私たちの目標は問題を解決するための精神的な障壁を減らすことであるため、この取り組みは不可欠です 。上記のコードを何もせずにそのままにしておくと、アプリがさらに貧弱になります。
非推奨の参照をすべて削除した後(およびgrep
で確認した後 およびログ)最終的にusers.first_name
を削除できます :
class RemoveUsersFirstName < ActiveRecord::Migration
def change
remove_column :users, :first_name, :string
end
end
User
に追加されたコードも削除する必要があります 不要になったので。
制限
この方法はあなたのケースに当てはまるかもしれませんが、その制限のいくつかに注意してください:
-
User.update_all
のような一括クエリは処理しません 。 - 生のSQLクエリは処理しません。
- モンキーパッチが壊れる可能性があります(依存関係によってもモンキーパッチが導入される可能性があることに注意してください)。
User
およびprofiles
profiles.first_name
の場合、同期がとれていない可能性があります 更新されますが、users.first_name
そうではありません。
あなたはそれらのいくつかを克服することができるかもしれません。たとえば、モデルをサービスオブジェクトまたはProfile
のコールバックと同期させておくことができます。 。または、PostgreSQLを使用している場合は、暫定的にマテリアライズドビューを使用することを検討してください。
以上です!
この記事の最も重要な教訓は、においのするコードを避けず、代わりに真正面から取り組むことです。 。タスクが圧倒的である場合は、定期的なスケジュールで繰り返し作業します。提示された記事a テーブルを抽出するときに考慮する方法は困難です。あなたがそれを適用することができないならば、それから何か他のものを探してください。方法がわからない場合は、私に連絡してください。お手伝いさせていただきます。ビットを腐らせないでください。
-
Windows 10 PC を高速化する方法
Windows 10 コンピューターを高速化する方法を探しているなら、あなたは正しい場所にいます。今日に至るまで、動作が遅く、新しい PC でも高速であると思われる Windows 10 コンピューターをいくつか見て、修正してきました。私の経験によると、システムのパフォーマンスに影響を与えるいくつかの理由により、Windows の動作が遅くなる場合があります。 Windows (すべてのバージョン) の動作が遅くなる最も一般的な理由は次のとおりです: マルウェア感染。 Windows の起動時に実行される大量のプログラム ページング ファイル (仮想メモリ) の設定が無効です。 破損
-
Facebook はどのように信頼を取り戻そうとしていますか?
Facebook と Mark Zuckerberg は、最近の大失敗により、最近ニュースになっています。すべては、ケンブリッジ アナリティカのデータ プライバシー スキャンダルから始まり、彼の辞任の憶測につながりました。噂によると、Facebook を辞めた後、マークはスキンケア ラインを推奨する予定ですが、それはエイプリル フールのジョークであることが判明しました。 しかし問題は、マークが彼の将来のために何をしようとしているかではなく、ソーシャル メディアの巨人である Facebook.Inc がユーザーの失った信頼をどのように取り戻すかです。状況について憶測や異なる意見を聞くことはよく