家のインターネット環境を安定させた

自宅で作業するのが増えた結果、インターネットが遅くなったり、ルーターとの接続が切れたりするのに気づくことが増えた。ので設定を変更してなんとかしたぞというメモ

前提

  • niftyのv6プラスを利用済み
    • ルータの近くだと20~30Mbpsくらいでる
    • v6プラス以前は1Mbpsとか。ひどい有様だった…
  • 有線は部屋の配置の問題でケーブルをひくのが難しいので無線でやるしかない
  • PCは窓際なので、外の電波に影響を受けている可能性が高そう

やった(やっている)こと

  • 5GHz帯を使う
  • 利用チャンネルを動的に変更するように設定を変更
    • 時間によって接続が不安定になった&&ルーター近くだと安定していたので、外の電波と関係があるのではと推測
  • チャンネルを束ねて高速化する機能をオフにした
    • 有線でも20~30Mbpsしかでないのにチャンネルを束ねる必要はなかった
    • 束ねた結果、外の電波と干渉して接続を失ったのではないかと推測

Railsのbelongs_toによるバリデーションと外部キー制約って重複しているような気がする

表題のとおりなのですが。

class Post < ApplicationRecord
  belongs_to :user
end

↑のように書いたときに、Rails5からは自動でvalidation_presence_of :userとしたのと同じになります。postをバリデーションするときにuserの存在をチェックする必要があるので、userが未ロードであればクエリが発行されます。一度にたくさんのpostを保存するようなケースでN+1が発生して困った経験がある人もいるのではないでしょうか。

./bin/rails g model post user:references のようにした場合は、次のように外部キー制約とbelongs_toが両方定義された状態になりますね。

class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      t.references :user, null: false, foreign_key: true

      t.timestamps
    end
  end
end
class Post < ApplicationRecord
  belongs_to :user
end

しかし、PostからUserへ外部キー制約を貼っていればvalidation_presence_of :userは不要では?という気がします。むしろ無駄にクエリが発行される分マイナスなような気もする。

次のようにすればOKではあるけれど、実際は必須なのにoptional: trueと書かなければいけないなのが大変微妙。

class Post < ApplicationRecord
  belongs_to :user, optional: true
end

と、ここまで書いていて気がついたけど論理削除のようなしくみを採用していた場合は外部キー制約では不十分ですね…とりあえず今回のケースではそれは考慮しないとします…><

このへん、みんなどのように解決しているのでしょうか?コメントほしいです(\( ⁰⊖⁰)/)

  1. 外部キーを使っておらず、belongs_toによるバリデーションを利用している
  2. Railsが生成するデフォルトのように、外部キーとbelongs_toによるバリデーションを両方使っている
  3. 外部キーのみ使用しており、belongs_toによるバリデーションはオフにしている
  4. その他

Rails 6時代のCredentailsをどうやって書くのが一番スマートなのか

Rails6ではcredentials.yml.encが複数の環境で作れるようになりました*1。staging環境などでもCredentialsが使えてべんり!となったのだけどdevelopment環境やtest環境の秘密の文字列はどのようにするべきなのでしょうか。たぶんRails公式ではこういう書き方を想定しているんじゃないかな、と想像します。

secret_token = if Rails.env.development? || Rails.env.test?
                 '開発用の秘密の文字列'
               else
                 Rails.application.credentials.secret_token
               end

もちろんこれでも書けなくはないのだけど、こういうのが色んな場所にあったりするともっといい感じにできないもんかな…、という気持ちになりますね。

stagingと同様にconfig/credentials/development.yml.enc とconfig/credentials/test.yml.enc を作るとRails.application.credentials.secret_tokenだけですべての環境をカバーできるけど、たいていdevelopmentとtestは同じ秘密の文字列を指すのでこれはこれでなんだかな、となる。

そこでググってみたところRails 6 adds support for multi environment credentials – Saeloun Blog という記事では次のようにしているのを見つけました。

  • credentials.yml.enc を開発用(development, test)にする
  • config/credentials/production.yml.enc を別途作る

これだと前述の問題を解決できそう。Rails 5.2時代を経験していると「credentials.yml.encはproduction用途である」という思い込みがあるので初見で面食らう、という点を除けばこれが最適解なような気もします。

みなさんはどんな感じでCredentialsを管理しているか教えてほしいです(\( ⁰⊖⁰)/)。

*1:詳しく知りたい人はこちらを読んでみると良さそう https://gihyo.jp/book/2019/978-4-297-10869-4

実行時間が長いコマンドが終了したら教えてほしい

macの話。

実行時間が長いコマンドを走らせて待っている間に他のことを始めてしまい、いつのまにかコマンドを実行していることそのものを忘れてしまう、という事が多かったので、終わったら通知が来るといいな、と思ったのでした。

調べたらapple script経由で通知を出せるらしいという情報を得たので、とりあえずbundle installとyarn install用のエイリアスに仕込んでみました。

alias b="bundle install && osascript -e 'display notification \"finish!\" with Title \"bundle install\"'"
alias y="yarn install && osascript -e 'display notification \"finish!\" with Title \"yarn install\"'"

f:id:willnet:20191222165404p:plain

なかなかよさそう。

参考: terminal - How can I trigger a Notification Center notification from an AppleScript or shell script? - Ask Different

webpackerでデフォルト以外のenvironmentを使いたい場合

"environment"が指すものがRAILS_ENVとNODE_ENVの2つあり、かつwebpackerはその両方を利用するのでわかりづらい。

config/webpacker.yml

RAILS_ENVに対応したキーの設定を使う

config/webpack/*.js

NODE_ENVに対応したキーのファイルを使う。のだけど注意点がある。

例えばNODE_ENV=stagingのときにはconfig/webpack/staging.jsを使う。その中で次のようにしていたとする。このとき、変数environmentに入るのはwebpackerがproduction用に用意した設定になる。

const environment = require('./environment')

module.exports = environment.toWebpackConfig()

これは、webpackerが

  • test,development,production用のデフォルト設定を用意している
  • NODE_ENVがデフォルト設定のいずれかと同様であればそれを、どれでもなければproduction用のデフォルト設定を利用する

という仕様になっているから。このへんを見ると挙動がつかめるはず。

https://github.com/rails/webpacker/blob/d905149d8a33303a9c24002721bc872ef95a8b6f/package/env.js

NODE_ENVはどのように決まるか

./bin/webpackコマンドでコンパイルするときは、明示的にNODE_ENVを設定しないとdevelopmentになる

https://github.com/rails/webpacker/blob/d905149d8a33303a9c24002721bc872ef95a8b6f/lib/install/bin/webpack#L4

rails assets:precompilerails webpacker:compileを実行したときは、明示的にNODE_ENVを設定しないとproductionになる

https://github.com/rails/webpacker/blob/d905149d8a33303a9c24002721bc872ef95a8b6f/lib/tasks/webpacker/compile.rake#L29

前はRAILS_ENVも見てなかったっけ?

https://github.com/rails/webpacker/pull/1359 で今の挙動に変わったらしい

SentryやRollbarに通知する内容を2つに分けたい

  • Railsプロジェクトでエラーの管理をするとき、SentryかRollbarを使うことが多いはず(以下エラー管理システムと呼ぶ)
  • エラー管理システムには、システムでの想定外の挙動を通知させたい
  • 「システムでの想定外の挙動」を分けるとおおきく次の2つ
    • いわゆるバグ
    • 想定外の振る舞いをしている可能性があるもの
  • 後者は例えば「ActiveRecord::RecordNotFound例外で404ページを表示している箇所で、これ自体は想定内の挙動なのだけどサービス内に該当ページに遷移するリンクがあったときに気付けるように一応エラー管理システムに通知する」というもの*1
  • 後者の通知が多くて、対処が必要な前者が埋もれがちになる、という問題をよく見かける
  • 後者はエラー管理システムとは別で管理したい
    • ログとして情報をためておいて、時々参照したい
      • 単なるログだと流れてしまって見ない
    • 頻度やその時の状況、スタックトレースなどもほしい
    • あれ?でもこれってエラー管理システムの要件では?
  • なんかうまく管理する方法ないですかね…?

追記

@masa_iwasakiさんにアドバイスもらったので追記

*1:例が良くないかもしれないけど、御社のエラー管理システムのを眺めると「バグじゃないんだけど登録されているエラー」がたくさん見つかるはず

循環参照してるときのsaveにおけるActive Recordの挙動

class A < ApplicationRecord
  belongs_to :b
end

class B < ApplicationRecord
  belongs_to :a
end

a = A.new
b = B.new
a.b = b
b.a = a
a.save

みたいなときどのように動くのかを調べた。

↓このへんを参考にした

rails/autosave_association.rb at master · rails/rails

  • belongs_to関連を設定した場合、before_saveでsave_belongs_to_associationが実行される
  • a.saveしたときに、save_belongs_to_association経由で関連先bのsaveを実行する
  • bのbefore_saveでもsave_belongs_to_associationが実行され、先にaが保存される(このときaのsave_belongs_to_associationの二回目が実行されないような仕組みが入っている rails/autosave_association.rb at master · rails/rails )。
  • bにaの外部キーが設定されてbが保存される
  • aにbの外部キーが設定され、aが再度保存(update)される

という流れになるはず。