私は、それがそれのように見えなかったということを知りませんでした!HerokuとCancancanパフォーマンス


昨年の私は私たちのリモートチームのメンバーの仕事の結果を記録し、我々のクライアントのPDFレポートを生成する内部のWebアプリを構築している.このアプリはコンプライアンス志向産業に向けてターゲットとされているため、精度の高い必要があります.これは、監査機関や監視ログを必要としないので、金融機関やヘルスケアではないが、我々はめったにハードの削除を実行します.私たちがハード削除を好む1つの状況があると言われています、チームメンバーが間違ってコンポーネントを加えるとき、しかし、彼らがそのコンポーネントのどんな行動も実行する前に.私はこのテストのためのスコープを作成しました
class Component < ApplicationRecord
    has_many :test_records, as: :testable
    scope :untested, -> {
       left_outer_joins(:test_records).
       where(test_records: { testable_id: nil } ) 
    }
end
これが多型性の関連であることを無視し、データベース内のすべてのTestCountレコードをそのコンポーネントに結合し、TestableRound IDを持たないレコードのみを選択していることに着目し、TestCountレコードなしでそれらのコンポーネントを収集することを確認します.これはかなり高価なリクエストですが、コンポーネントを削除しようとしている場合にのみ発生します.この話の時点で53000のテストされていないコンポーネントがデータベースにありました.
ここで問題がどこに来る.私のCancancan能力で.Rbファイル私はすべてのフィールドの人員のために以下の行を追加しました
def certified:
    can :destroy, 
        Component, id: Component.untested.map{ |comp| comp.id }
end
Cancancan文書を見て、これはそうする方法でありません.なぜ私は呼んでいた.ここだけでなく、スコープを使用してマップ?なぜ、私はこのようにすべてのIDを集める必要さえありますか?なぜ、私はこれを以前に特定しませんでしたか?
もちろん、管理者役割は、私が定義した1つのルールだけです.私が管理特権の象牙の塔に座っていた間、私のユーザーは缶にあらゆる一回の呼び出しの不必要なデータベース要求に苦しんでいました?
私がアプリケーションにログインするとき、私は200 msへの時折のスパイクで200~500 msの応答時間を見ます.私はただ、アプリは、遅延のために予想されることができなかった遅延があったどのように動作したと思った.反省して、私はこれが問題を解決することを望む方法であると思います.
私がフィールドで私のすべての技術者のために1000 ms - 2000 msの応答時間を見ていたけれども、一旦私のログが始まったならば.数ヶ月間、その応答時間は時折タイムアウトで5000 msまで成長しました.私は、PDF生成が犯人であると思いました、それで、私は質問をできるだけ効率的にしている全体のコントローラを書き直しました.それでもなお問題は続いた.
最後に、私はログ内の技術者のイベントを見ていたとき、私はブレークスルーをしていたと私が作っていたリクエストと彼らが作っていたリクエストの違いに気づいた.ユーザーとロールテーブルから選択された両方のクエリが、技術者の要求は、同じ要求を実行したときに呼び出されていなかったコンポーネントテーブルのクエリを実行していました
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", xx], ["LIMIT", 1]] 
Role Load (0.6ms) SELECT "roles".* FROM "roles" WHERE "roles"."user_id" = $1 LIMIT $2 [["user_id", xx], ["LIMIT", 1]]
Component Load (280.9ms) SELECT "components".* FROM "components" LEFT OUTER JOIN "test_records" ON "test_records"."testable_id" = "components"."id" AND "test_records"."testable_type" = $1 WHERE "test_records"."testable_id" IS NULL [["testable_type", "Component"]]
ファントムクエリ.
この質問がどこから来ていて、手続き的にコードを通っているのかを見つけ出そうとしている当惑した時間を費やした後、私は最終的に、クエリテーブルの問い合わせの直後に、テーブルテーブルによって生成されたクエリの前にクエリが起こっていることに気づいた.これらのクエリ間でコードが実行された唯一の場所は、アビリティーテーブルで、以前のコードが見つかりました.
私はすぐに、コンポーネントコントローラに私のDestroyメソッドを変更し、0 TestSumレコードがあることを確認し、デバイスが削除できない場合にエラーに応答します.私は自分の能力からコードを削除しました.RBファイルと応答時間はすぐに500 ms以下に落ちました.ユーザーは喜んだ.

修正は午後5時近くの日の終わりに適用されました.ここでは、応答時間を示す最後の24時間です.
知らなかったこと
SQLHERE ERRORCHEART CODE = 28000致命的な数: PGRANT HBAはありません.ホストのIPHONEアドレスのためのconfエントリー、ユーザー「postgres」、データベース「postgres」、ssl offは私の悪いコードの徴候でした.
定数dynoロードは、悪いことが起こっている徴候です
大きなメモリのフットプリントは
Herokuの監視ツールはどんなものであってもよいでしょう.

dyno負荷を示す最後の24時間.

最後の24時間のメモリ使用量を示す.
私は長年の趣味の基礎と開発者レベルでHerokuを使用しているし、ログや分析は私に利用されたことがない.私は、私のアプリがどのように実行されるべきであるか全くわからなかった、そして、私は私がそこであなたの多くに少し馬鹿に見えると確信します、しかし、正直に、それは30秒かかりました.タイムアウトは、私は本当の危機を持っていたことを認識する.あなたのHeroku Metaicsが私のもののように見えるならば、あなたは知っていなければなりません...