nodeのメモリー足らない問題をswapで回避する

AWSのt2.microだと、メモリーが1Gです。

webpackerを使用していると、普通にdeployで落ちてしまいます。

FATAL ERROR: JS Allocation failed - process out of memory
Aborted

こんな感じのエラーが出てにっちもさっちもいかなかったです。

memoryが足らないと言うことで、memoryを増やせばいいのですが、無料期間なので、グレードをあげて対処するなんてもったいないです。

どうしよう・・・っと思っていたところに、swap領域を作れば回避できるんじゃないですか?とアドバイスをいただいたので、swap領域を作成することにした。

AWSの上位インスタンスだと、最初からswapはあるようだけど、安いインスタンスだとなかった。

あと、inuxだと、swap領域を自分で設定できたとは知らなかった。

swapとは

HDDにメモリー領域を作成して、高速にアクセスできるようにします。 パソコンはメモリー上にある領域は高速にアクセスできるのですが、HDDに書き込みするのは遅いです。 ということで、memoryが足らない状態になると、HDDにメモリー領域を作成します。

今回は実際のメモリー上のアクセスしかしていなくて、メモリー領域が足らなくて落ちていました。

コマンド

sudo dd if=/dev/zero of=/swap bs=1M count=1024
sudo mkswap /swap
sudo swapon /swap

んで、freeコマンドで確認する。

Mem:       1022936     831780     191156       1592      80336     183480
-/+ buffers/cache:     567964     454972
Swap:      1048572      10064    1038508

無事にできている。

topコマンドでもきちんと作成されています。

top - 21:06:59 up 1 day, 21:51,  1 user,  load average: 0.00, 0.05, 0.14
Tasks:  64 total,   1 running,  63 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.7 us,  0.1 sy,  0.0 ni, 98.3 id,  0.9 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   1022936 total,   832076 used,   190860 free,    80336 buffers
KiB Swap:  1048572 total,    10064 used,  1038508 free.   183504 cached Mem

きちんとコンピューターの仕組みがわかれば大した問題じゃないのですが、解決できてよかった。

意外にサーバー面白い!

gitのリポジトリの中で、issueのURLを開くコマンドを作成した

いちいちgitを開けるのが面倒くさいってパターンです。

#!/usr/bin/env ruby

require 'open-uri'

url = "https://github.com/foo/bar/issues/#{ARGV.first}"

system "open #{url}"

ファイル名をbin/issueで登録する。

bin/issue 10

これで開きます。

もう少しいい書き方がありそう。

面倒くさいので、ターミナルからブラウザを開く行動までやっていきたいですね。

rubyでrakeの外部コマンドが失敗した時のエラーハンドリング

webpackerを使用していて、webpacker installって出ているのに、installは失敗していた。

これはムカつくからpullreq出してやろうと思ったら、すでにあった。

さすがだ。

fail hard when webpacker:install ./bin/yarn fails by sealocal · Pull Request #149 · rails/webpacker · GitHub

んで、実際のエラーハンドリング方法が思いつかなったので、これは参考になりました。

run './bin/yarn add webpack webpack-merge path-complete-extname babel-loader babel-core babel-preset-env coffee-loader coffee-script compression-webpack-plugin rails-erb-loader glob'
unless $?.success?
  puts set_color 'Failed to install webpack!', :red
  exit $?.exitstatus
end

$?はprocessの実行結果がいます。

これを見て、成功したかどうかを判定して、エラー内容を記述していました。

芸がこまかい。

そして、$?自体初めて知りました。

スレッドで最後の実行結果が入っているのですね。

$?自体がUNIXの実行結果の可否を調べるものなので、それと同じ動きをしていたってことですね。

% cat foo
=>cat: foo: No such file or directory

% echo $?
=>1

この辺はUNIX/Linux使いで、シェルとか作ってる人は分かるのでしょうが、自分は使ってないので、思いつかなかったので、これをきっかけに覚えておく。

variable $? (Ruby 2.4.0)

以上です。

nvmでnodeをインストールしているのに、deployしたら「/usr/bin/env: node: No such file or directory」が出る場合

なんでだよ!

node -v
=> v7.7.4

deploy→/usr/bin/env: node: No such file or directory

確実にインストールはされている。

ということで、シンボリックリンクを貼りましょう。

sudo ln -s "$(which node)" /usr/bin/node

ちなみにdebianのversion

cat /etc/debian_version
→8.3

これより古いと、別のパターンもあるので、、適切なものを選びましょう。

【Node.js】【PM2】 PM2でさくらVPSにNode.jsのアプリケーションをデプロイ - 人生dat落ち

rails5からはcontrollerテスト書くぐらいなら、requestテストを書くのがいいっぽい

controllerテストいらねみたいな流れだったけど、requestテスト書けって話だったのか。

Rails 3 と 4 において、controller spec の assigns は慣用されてきました。 今回の RSpec 3.5 はマイナーリリースであり、私たちは SemVer に準拠する以上、 既存の controller spec を壊さないようにしています。 既存の Rails アプリケーションで assigns を多用しているものについては、 rails-controller-testing gem を Gemfile に追加することで assigns と assert_template を復活させることができます。 RSpec はこの gem とシームレスに連携するため、controller spec は問題なく動作し続けるはずです。

これから新しく作成する Rails アプリケーションについては、 rails-controller-testing gem を追加するのはおすすめしません。 Rails チームや RSpec コアチームとしては、代わりに request spec を書くことを推奨します。 Request spec は一つのコントローラーアクションにフォーカスしますが、 controller spec とは違い、ルーターミドルウェアスタック、Rack リクエストやレスポンスも関与します。 これによって、より現実に近い環境でテストを実行し、controller spec で発生しがちな多くの問題を避けることができます。

RSpec 3.5 がリリースされました!

今までもassingsを使用していたcontrolelrテストだと、下記を入れれば今まで通り使用できます。

GitHub - rails/rails-controller-testing: Brings back `assigns` and `assert_template` to your Rails tests

これを使うぐらいなら、requestテストを書けって流れみたいだ。

なお、requestテストはcontrollerテストよりも早くなっているらしいので、早速試した。

Rails 5 では、request spec が Rails 4までの request spec や controller spec よりもかなり高速になっています。 これは Rails チームの Eileen Uchitelle1 のおかげです。

rails5のrequest

Finished in 0.33271 seconds (files took 5.43 seconds to load)
1 example, 0 failures

controller

Finished in 0.277 seconds (files took 11 seconds to load)
1 example, 0 failures

controllerの方がまだ早いかな・・・

流れ的には、requestテストの方がリアルなデータが取れるので、そっちを使えってことなので、今後はrequestテストを書いていく。

rails5ではrender methodをいたるところで使えるようになっている

rails5にアップデートするときに変更されていて、手を加えて変更したので、備忘録として書いておきます。

rails4.2時代のrenderのやり方

下記のように、requestを強引に擬似るような形でやっていました。

class Renderer
  def renderer
    controller = ApplicationController.new
    controller.request = ActionDispatch::TestRequest.new
    ViewRenderer.new(Rails.root.join('app', 'views'), {}, controller)
  end
end

class ViewRenderer < ActionView::Base
  include Rails.application.routes.url_helpers
  include ApplicationHelper

  def default_url_options
    { host: Rails.application.routes.default_url_options[:host] }
  end
end

Renderer.new.render

こういうやり方ではなく、ActionController::Rendererというクラスメソッドが作られました。

コントローラのアクションの外部で任意のテンプレートでレンダリングするActionController::Rendererを追加。 (Pull Request)

rails/rendering.rb at master · rails/rails · GitHub

ということで、下記のようにすることでrenderをいたるところで呼べるようになりました。

ApplicationController.renderer.render partial: 'topics/image', locals: { url: url, host_name: host_name }

以上です。

vueのオブジェクトの並び順について

jsonで値を渡しているのに、並び順が違う!って状態に陥りました。

なんでだ?って思うと、どうもObject.keys()の並び順に整理されるようですね。

オブジェクトを反復処理するとき、順序は Object.keys() の列挙順のキーに基づいており、全ての JavaScript エンジンの実装で一貫性が保証されていません。

jp.vuejs.org

ということで、ソートした状態にするには、 methodを使ってやるしかないようです。

以上です。

mysqlのorder by fieldで指定のID順に並べる

どういった時に使うんだよwwwって思いましたが、使う感じになりました。

select *
  from icons
 where id in(4, 5, 6)
 order by field(id, 5, 4, 6)

=>
mysql> select * from icons where id in(4, 5, 6) order by field(id, 5, 4, 6);
+----+------------------+-----------------+--------------------+-----------------+---------------------+---------------------+---------------------+
| id | icon_category_id | image_file_name | image_content_type | image_file_size | image_updated_at    | created_at          | updated_at          |
+----+------------------+-----------------+--------------------+-----------------+---------------------+---------------------+---------------------+
|  5 |                1 | 1f606.png       | image/png          |            2277 | 2017-03-13 15:09:17 | 2017-03-13 15:09:17 | 2017-03-19 00:50:29 |
|  4 |                1 | 1f601.png       | image/png          |            2235 | 2017-03-13 15:09:17 | 2017-03-13 15:09:17 | 2017-03-19 00:50:29 |
|  6 |                1 | 1f605.png       | image/png          |            2685 | 2017-03-13 15:09:17 | 2017-03-13 15:09:17 | 2017-03-19 00:50:29 |
+----+------------------+-----------------+--------------------+-----------------+---------------------+---------------------+---------------------+

確かに順番通りに返ってくる。

https://dev.mysql.com/doc/refman/5.6/ja/string-functions.html#function_field

FIELD(str,str1,str2,str3,…)

str1、str2、str3、… リスト内で str のインデックス (位置) を返します。str が見つからない場合は、0 を返します。

FIELD() へのすべての引数が文字列の場合は、すべての引数が文字列として比較されます。すべての引数が数値の場合は、数字として比較されます。それ以外の場合は、引数が倍精度として比較されます。

NULL ではどの値との等価比較にも失敗するため、str が NULL である場合は、戻り値が 0 になります。FIELD() は ELT() の補数です。

FIELD関数を使用しているってことなのね。

railsで使う場合

ids = [5, 4, 6]
Icon.where(id: ids).order("field(id, #{ids.join(',')})")

orderの中で文字列展開をして、使用しているってことですね。

rails5からはキチンとしたmethodがあるようです。

以上です。

参考

qiita.com

yoku0825.blogspot.jp

railsで複合uniqueのvalidationをつけてやる

mysqlrailsの両方に念のためつけます。

複合uniqueなんて、使うことあるのか?って思っていたけど、必要になりました。

忘れないために書いておきます。

まずは、migrationの書き方からです。

class CreateReactions < ActiveRecord::Migration
  def change
    create_table :reactions do |t|
      t.references :icon, index: true, foreign_key: true, null: false
      t.integer :reactionable_id, null: false
      t.string :reactionable_type, null: false
      t.string :user_cookie_value, null: false

      t.timestamps null: false
    end

    add_index :reactions, [:reactionable_id, :reactionable_type]
    add_index :reactions,←ここから
      [:user_cookie_value, :icon_id, :reactionable_id, :reactionable_type],
      unique: true,
      name: :index_reactions_on_uniq_cookie_and_ids
  end
end

そして、rails

class Reaction < ActiveRecord::Base
  belongs_to :icon
  belongs_to :reactionable, polymorphic: true

  validates_uniqueness_of :reactionable_id, scope: [:icon_id, :user_cookie_value, :reactionable_type]←scopeで判定する
end

scopeで値を絞ります。

参考

ActiveRecord::Validations::ClassMethods

以上です。

es2015の分割代入は、rubyのキーワード引数のイメージ

わかりづれー!ってなっていました。

{}←これ何?って感じで意味不明に陥りやすかったです。

function foo({x, y}) {
  return x + y
}

console.log(foo({x: 1, y: 2}));
=>3

x, yのvalueが代入されている。

これってrubyのキーワード引数と同じっぽく感じる。

def foo(x:, y:)
  x + y
end

foo(x: 1, y: 2)
=>3

それだけです。

新しい記法は慣れるのに時間がかかる。

なおかつ、それをほとんど使用しない身としては、とても覚えづらいけど、似た様なものと関連付ければ覚えやすくもなる。