行動すれば次の現実

ほどよくモダンなシステム開発を目指しています。メインテーマは生産性、Ruby、Javascriptです。

HerokuのR14エラーをScout APMで対策する方法 | Rails

Herokuではメモリ使用量がプランの上限を超えるとR14エラーが発生します。 R14はスワップメモリが発生していることを意味しており、発生するとアプリケーションの処理速度が著しく低下してしまいます。

このままメモリ使用量が200%を超えてしまうと、R15エラーが発生してdynoが強制再起動されてしまいます。 このような自体にならないためにもR14エラーが発生した場合は、早急に原因を特定して改善する必要があります。

Scout APMでRailsのパフォーマンスチューニング

スワップメモリを発生させないためにはメモリ使用状態をモニタリングする必要があります。 メトリクス監視サービスとしてNewRelicなどが有名所ですが、筆者はScout APMをオススメします。

NewRelicの有料プランを使用していたのですが、以下の点が不便であると感じていました。

  • 多言語をサポートしているためフレームワーク固有の情報が取得できない
  • 多機能すぎて使いこなすのが難しい
  • サーバー単位でのメトリクスは詳細に把握できるが、どこのURLのアクションが問題を起こしているのかが把握できない

その点、Scout APMはRailsに特化しているので上記の問題点がすべて解消されます。 さらに、Githubと連携することでコード単位での分析も可能となり、ピンポイントに原因を特定することが可能になります。

以下に、R14エラーが発生した際の原因特定からパフォーマンスチューニングまでの流れを説明します。 R14エラーに悩まされている方はぜひ参考にしてみてください。

STEP1. メモリ使用量が大きいアクションを特定する

R14エラーを検知したらScout APMのDashboardから原因のアクションを特定します。

下記画像(Scout APM Dashboard)のようにMEMORYチャートを表示させて、発生した時間帯にシークバーを設定します。 設定すると「Largest Memory Increase」というウィンドウにメモリ使用量の大きい順でアクション情報が出力されます。

StoreLayouts::PrintsController#indexが41MBのメモリを使用していることが確認できます。 このアクションが原因であることが確認できます。

f:id:furu07yu:20211002164610j:plain
Scout APM Dashboard

STEP2. トレース情報から問題のあるコードを特定する

StoreLayouts::PrintsController#indexの箇所をクリックすると、より詳細なトレース情報が表示されます。 下記画像(Transaction Traces)を参照。

f:id:furu07yu:20211002165901j:plain
Transaction Traces

Transaction Tracesの該当アクションをクリックすると、実行された処理がタイムライン形式で表示されます。 下記画像(Memory Allocation)を参照。

「Memory Allocation Breakdown」タブを選択するとメモリーアロケーションの回数などが表示されます。 また、N+1が発生した場合はその発生回数が表示されますので、問題のありそうなコードがおおよそ把握できます。

f:id:furu07yu:20211002193641j:plain
Memory Allocation Breakdown

backtraceというラベルをクリックするとGithub上のコードが表示されます。 下記画像(backtrace)を参照。

f:id:furu07yu:20211002171018j:plain
backtrace

N+1が多く発生している箇所などを中心に問題のある箇所がないかをチェックしていきます。

R14エラーの場合、N+1以外にも、ActiveRecordのfind時に無駄にincludesしていることで徐々にメモリリークになるケースも多いので、そのような箇所もチェックしていきます。

Scout APMで調査できることはここまでです。 後は上記の調査結果からソースコードの当たりをつけてパフォーマンスチューニングを実際に行っていきます。

STEP3. 実際にパフォーマンスチューニングを行う

ソースコードの当たりがついたらメモリを測定します。

RubyにはObjectSpace.memsize_of_allというメソッドがありますので、それを使用します。 application_controller.rbなどの共通参照できる箇所に以下のようなメソッドを定義しておきます。

def log_memory(label)
    puts '--------'
    puts "--------#{label}"
    puts "#{ObjectSpace.memsize_of_all * 0.001 * 0.001} MB"
    puts '--------'
    puts '--------'
  end

実行するとMB単位でメモリ使用量がコンソール出力されますので、メモリを多く使用していると思われる処理の前後に仕込んでおき、メモリ使用量を測定します。

log_memory('before method_a')
method_a
log_memory('after method_a')

メモリ使用量過多の箇所が特定できれば、後は適宜実装を改善した上で改めてメモリ使用量を測定します。

メモリの使用量が改善されればパフォーマンスチューニングは成功です。

実装しない勇気

私はWebサービス(SaaS)を開発・保守しています。

サービス開発をしているとついつい「あの機能も必要かな?この考慮も必要かな?」と考えを巡らせます。 実装が終わってみると予定よりも多くの機能が出来上がり、大きな満足感を得ることが出来ます。

しかし、その機能は本当に必要だったのでしょうか。

そのような経緯で生まれた機能は、実際に開発して運用してみると使わなくなることが往々にしてあります。

私もこのような経験を数多くしてきました。 このような自体に陥らないために、実装するかどうかの私なりの判断基準を解説したいと思います。

サービス開発・保守を担当している方はぜひ一つの参考にしてください。

実装するのは誰でもできる

実装するという選択を安易に選んではいけません。

なぜなら「実装する」ということは、その機能を責任持って保守をし続けると確約することだからです。 (安易にペットを飼ってはいけないという感覚に近いと感じます)

筆者は「実装する」と選択するよりも、「実装しない」と選択するほうが難しいと感じます。

機能を実装する(作る)かどうかを判断する上で以下のカテゴリに分類されます。

  1. 作る必要がある機能
  2. 作る必要がない機能
  3. 作ったほうが良い機能

この内「3. 作ったほうが良い機能」を「作る」という選択を安易にしてしまうのは非常に危険です。 なぜならそのような機能を実装する理由が実装者の思い込みによるところが大きいためです。

この機能があったほうが便利だろう、という理由は実装者の感覚でしかなく何も根拠がないからです。 この機能は絶対使われると確信できないのであれば、いっそのこと作らないと選択したほうが良いです。

作ったが使われなかった場合、その作業に費やした工数。今後その機能をメンテナンスしなければならない工数は全て無駄になってしまいます。

それでも作るのであればそれは実装者のエゴか、保身としての選択になります。 ある意味「逃げの選択」と言い換えることもできます。

※ ただし、新育のためにあえて無駄な機能を作る(作らせる)のはありだと思います。教育という明確な目的があるためです。

実装しないという選択

では、どうすればよいかというと「作る必要がある機能」のみ作れば良いです。

「作る必要がある機能」のみを搭載した最低限の状態(MVP Minimum Viable Product)で顧客に使用してもらい、本当に必要な機能を見極めてもらいます。これはMVP戦略と呼ばれています。

MVPを使用した上で、顧客から意見をフィードバックしてもらいます。そのフィードバックから生まれた機能は「作る必要がある機能」として確信をもって実装することが出来ます。

この時、前提として「作ったほうが良い機能」については、初期開発では無駄な機能は開発しない旨をあらかじめ顧客に提案して合意もらっておく必要があります。

合意をもらうというアクションは極めて重要です。これをしなければ、なぜ作ってもらえないのかと顧客から反発を受ける可能性があるためです。

実装を捨てる勇気

すでに実装してしまった場合は最後まで責任持って保守し続けるしかないのか、というとそうではありません。 根拠を持って実装を捨てれば良いです。

いきなり捨てるのではなく、以下のようなアクションを経て捨てる根拠を見出します。

  1. 顧客からその機能を使っているかヒアリングする
  2. GoogleTagManagerやサーバーログからその機能の使用数を測定する
  3. ABテストでその機能を一時的に塞いで反響を見る

これらのいずれかの方法で捨てても良い根拠を見出だせたらその機能を捨てることが可能となります。

iPad ProとApple pencilでゼロ秒思考をやってみる

私は毎日ゼロ秒思考を実施しています。

ゼロ秒思考といえば、紙とバインダーとボールペンを使うのが一般的ですが、iPad ProとApple pencilで代替できないかと試してみたところ、全然これで十分、むしろ快適とすら思えてしまいます。

iPad ProとApple pencilでゼロ秒思考を検討している方はぜひ参考になれば幸いです。

紙からの開放は自由な発想を広げる

紙とボールペンを使用するスタイルだとどうしても大量の紙(ゴミ)が発生してしまいます。

紙を躊躇なくどんどん使っていける人ならそれで問題ないと思いますが、私の場合「ゴミがかさばるから」という理由で、多くても一日10枚くらいまでという制限を無意識に設定していました。

これでは思考にストッパーがかかり、自由な発想ができません。

iPadの場合、紙ではなく電子データなのでほぼ無限に書き続けることができます。 この「紙からの開放」こそがiPadを使用する一番のメリットだと感じています。

多彩な装飾により思考が整理される

紙とボールペン時代は基本的には色は黒のみを使用していました。 様々な色のボールペンを持ち歩くのは面倒だからという理由です。

iPadの場合、簡単に色付けしたり、太字にしたりできるので表現できる幅が格段に広がりました。

これはゼロ秒思考だけでなく、プログラムの設計で頭を整理したい時などにも効果を発揮します。 オブジェクトを移動させたり、コピーする機能などがあり、紙では出来ないような表現が可能で、デジタルとアナログの良いとこ取りだと感じています。

実際、それにより生まれた新機能はたくさんあり、それだけでも使っていてよかったと思えます。

iPad Airだとレイテンシーが気になる

実は初めはiPad AirとApple pencilでゼロ秒思考をしていました。

それでも革命的な衝撃を受けたのですが、走り書きしていると若干のレイテンシーが発生していました。

気にならない人は気にならないのかもしれませんが、神経質な私はそれが結構なストレスだと感じしていました。

家電量販店でiPad Proを使用してみたところほとんどレイテンシーが感じられず、ボールペンで書いているのと遜色ない感覚でした。

より違和感なく使用したい場合はiPad Proを選択することを強くおすすめします。

参考

実際に書いたゼロ秒思考

ゼロ秒思考sample
ゼロ秒思考sample

使用しているアイテム

JQueryでcheckbox操作 (チェックしたら○○する、チェック外したら○○する)

checkboxをチェックしたら何かをしたいときの条件式です。

$('#check').on('change', function (e) {
  if (e.target.checked) {
    //  check:on時の処理
  } else {
    //  check:off時の処理
  }
});

clickイベントで拾いたい場合はこう書きます

$('#check').on('click', function () {
  if ($(this).prop('checked')) {
    //  check:on時の処理
  } else {
    //  check:off時の処理
  }
});

時間がないから成果が上がらないのではなく、時間があるから成果が上がらない

レバレッジ・シンキングを読んでみたので簡単なまとめと書評。

自由な時間が多い人こそ必要なマインドだと思った。

読んだきっかけ

やりたいことが見つかる時間編集術を読んでプロダクティブタイムの効率化にアンテナを張っていたところAmazonのリコメンドで気になったので購入。

4つの資産を構築してレバレッジをかける

  • 圧倒的な成果をあげるにはパーソナルキャピタルを構築してレバレッジをかけることが必要
    • パーソナルキャピタルとは、労力資産、時間資産、知識資産、人脈資産のこと
  • パーソナルキャピタルを高めて、余った時間をさらにパーソナルキャピタルに投資していくことで複利的に資産を増やせる
  • パッシブではなくアクティブに行動せよ
    • 例:普通にテレビを見るのはパッシブ(受け身)、録画して気になるところだけ見るのはアクティブ

労力のレバレッジ

  • 労力のレバレッジをかけて「やる気に左右されたくない」「なるべく労力をかけずに近道したい」を取り除く
  • 仕組み化と習慣化がポイント
    • 仕組み化:チェックリスト、マニュアルで余計な労力を使わない
    • 習慣化:継続することで知識や経験が溜まっていくものを無意識化できると最強。小さく始めるのがポイント
  • KFSを見極める
    • KFSとは成功への鍵を握る要素=キー・サクセス・ファクター
    • 人の真似をするのが良い
      • 受験勉強の例:参考書を最初から最後まで読むのではなく、まずは「合格体験記」を読んで合格するための勉強方法を学ぶ

時間のレバレッジ

  • 時間がないから成果が上がらないのではなく、時間があるから成果が上がらない
    • 期限があるから人はそれを行う。期限がないと手を付けない(例:夏休みの宿題)
    • 時間にレバレッジをかけることで短い時間で成果を上げることができる
  • 俯瞰逆算(トップダウン)でスケジュールを組む
    • いきあたりばったりのスケジュールよりもゴールを達成するためのタスクを洗い出してスケジューリングするのが効果的
  • 時間割を活用する
    • アウトプット(タスク)、プライベート、インプットなどの単位で分割して一日のスケジュールを時間割することで時間制限を設けることができる
    • 「仕事が早く終わったら本を読もう」の発想だと、実際には仕事に本を読む時間が奪われてしまう
    • 時間があるから仕事はできない、ではなく、時間がないから仕事はできる
    • 区切りをつけることが重要。

知識のレバレッジ

  • 知っているかどうかで成果に大きな差がでる。知っていることでレバレッジをかけることができる。
  • 人の真似をする
    • 時間に限りがあるから近道を考える。人の真似をするのが一番効率的
    • 自分に似たタイプの人を真似する
      • 受験の例:受験まで1年間しか無いなら1年間で大学受験に合格した人の勉強法を参考にする
  • レバレッジ・リーディング
    • なぜその本を読むのか?目的意識をはっきりさせる
    • その本のカバー、著者のプロフィール、まえがき、目次、あとがきを眺める
    • その後本を読むことである程度予測できた状態で読書できる。普通に読むより効率が上がる。

人脈のレバレッジ

  • 自分一人では限界がある。人の力を使ってレバレッジをかけることで圧倒的な成果を生み出せる
  • 短期投資ではなく長期投資を意識
  • 基本はギブ(コントリビューション)
    • 相手に有益なバリューを提供しなければ相手にされない
    • ただし去るものは追わない
  • パーソナルブランディングも重要な要素
  • マインドの高い集団に属することで自分のマインドを高める
    • 高いマインドの人は成功しやすい
    • ランチは人と食べることで人脈と食事の二毛作ができる

感想

「時間がないから成果が上がらないのではなく、時間があるから成果が上がらない」という言葉に出会えただけでもこの本を読んだ価値があった。 カフェや新幹線で作業すると、やたらと生産性が高いと感じる理由はこれなのかもしれないと腑に落ちた。 スケジューリングする際も時間割を決めて制限時間を意識して作業することで、生産性を意識せざるを得ない状況を生み出すことができる。 それを習慣化させて効率化を図っていきたいと思う。

プロダクティブタイムとアンプロダクティブタイム

やりたいことが見つかる時間編集」を読んでの書評。

今年読んだ本の中でベストな内容だと思う。

やりたいことがありませんは普通

  • 著書は「やりたいことがある方が異常」と言っている。
  • 世の中的に「やりたいことをやろう」「好きなことで稼ごう」というスローガンが流布されているが、それは成功者の言葉であり全体の1%
  • やりたいことを見つけるには人生の4つの資産を増やすこと
  • 時間→能力→人脈→お金
  • 時間の使い方が全ての基礎である

時間はお金で買おう

  • お金は増やせないが買うことができる
  • ものを買う際は一番安いものではなく、一番早いものを選ぶ
  • たとえば移動手段の場合はバスよりも新幹線
  • 格安飛行機で乗り継ぎするよりも直行便
  • 新しいサービス、モノは未来を体験できる、クリエイティビティが上がる
  • 迷ったら未知のモノを選んでクリエイティビティを上げる

プロダクティブタイムの意識

  • いわゆる時間術的な話
  • プロダクティブタイムは「時間のコスパ」を徹底的に高めることを目指す
  • 1.5倍速を意識する(3時間の仕事を2時間で終わらせると1時間自由な時間ができる)
  • 何をするにも、そのリターンが未来につながっているかを考える
  • ただし生産性を求め続けると疲弊して視野が狭くなる

アンプロダクティブタイムが最も重要

  • 何もしない時間、余白が重要
  • 余白は偶然をたくさんもたらす
  • たくさんの情報が入り、そこに行ってみたり、調べたりすると結果的に視野が広がる。ユニークな存在になれる
  • やりたいことがない人の多くは知識が足りない、世界が狭い
  • 興味をもったことを何も考えずにやると世界が広がる

おまけ

  • 感情的になったら一呼吸おいて、感情に反応するかどうか自分で選ぶ
  • 仕事における人間関係構築のコツはコミュニケーションコストを低くすること
    • スケジュールをどんどんこなしていくようになるだけで結果が全く変わる
    • 周りの人は安心して信頼してくれる

感想

プロダクティブタイム、アンプロダクティブタイムの存在が知れたことは自分の人生の中で大きな影響を与えると思う。 生産性重視でいつも疲弊気味だった自分が、アンプロダクティブタイムという最も重要な余白を意識することでこの先どう変わっていくのか楽しみである。

そのためにはプロダクティブタイムで圧倒的な生産性を実現する必要があるので、今までマインドは活かせれつつ、アンプロダクティブという束の間の休息を取れるのは非常にありがたい考え方である。

ActiveModel valid?でバリデーション例外を発生させる | Rails

ActiveModelのvalid?はモデルのバリデーションを実行して真偽値を返却するメソッドですが、バリデーションエラー時に例外を発生させたいことがあったので調べてみました。

通常ならsave!やcreate!で対応できますが、Activerecord-importのBulkインサートの永続化前にバリデーションチェックしたい場合などは使えるかもしれません。

実装

unless product.valid?
  raise ActiveRecord::RecordInvalid, product
end

valid?が真の場合は、対象オブジェクトをActiveRecord::RecordInvalidの例外クラスに詰めてあげれば永続化することなく例外を発生することが可能です。