下級エンジニアの綴

新しく発見したことを綴っていこうと思っています。夢はでっかく上級エンジニアになることです。

rubyのcountについてメモ

countを使わなくて失敗したので戒めを含めメモ。

Active Recordのデータ(配列)に対して

sizeを使えば配列を全てオンメモリで扱って、結果を返す。

countはを使えばdbに投げて結果を返してくれる。

↑のことはわかっていたけど、物理的な部分を理解しているつもりで終わっていた。

例えば、100件程度のデータならsizeの方が良いと思うが、数十万件となるとcountの方が良いと思う。

理由としては

数十万件のデータを全てメモリに載せようとするとおそらくメモリが足りずに落ちる

からである。

blank?とかpresent?とかも同じことが言える。

配列を判定する時に値が存在する、存在しないという処理も配列のデータをオンメモリで扱おうとするので仮に数十万件データが入っていると落ちる可能性がある。

array.count > 0

という処理はpresent?を使えば良いと思っていたけど、時には必要なんだなと思った。

countが使えない場合はfind_eachとかを使って頑張るしか無いかなと思う。

その辺をわかっているつもりでわかっていなかったから、不具合を産んでしまった。次は絶対に同じミスをしない。

railsのparamsについて調べたのでメモ

今回はparamsのメソッドについてハマったので色々調べたメモです。

railsのバージョンは4.1.8でrubyのversionは2.1.5です(古いバージョンで辛い・・・

内容

作成した成果物を一覧で表示する処理を書いており、スクロールを行うと自動で次の成果物を表示するロジックを書いていた時に発生しました。

ロジックとしては、一定以上の座標に到達した時、非表示にしているlinkタグを動的に叩かせる処理で、linkタグはget形式です。

原因

getのパラメータの引数が膨大な長さになっており、それが原因でエラーになっていました。

処理の流れとしては

paramsを送信 (ブラウザ)→paramsを加工(サーバ)→linkタグを生成(ブラウザ)

みたいな流れです。

なぜ急に発生したのかと調べているとparamsを加工する処理が一番の原因でした。

サーバ側で〇〇_id_in[1,2,3.....]みたいな形のparamsを生成しており、

↑のparamsを元にgetのpathを生成すると

〇〇_id_in=1&〇〇_id_in=2&〇〇_id_in=3...のような形式でpathが生成されました。

色々調べたところ、pathを作る時にparamsを元にしているっぽかったです。(配列で渡されたら↑のような形式になるのかな?未検証ですみません)

対応

  • paramsを直接書き換えないようにする事(加工する時はdupなどで複製した物を使う)
  • ransakを使うときは〇〇_id_inみたいなparamsをブラウザで生成しない事

まとめ

railsのparamsは直接触らずにdupなどで複製し、その複製したものに変更を加え流のが良いですね。あとransackの〇〇_in怖い・・・

参考リンク

stackoverflow.com

params (ActionController::StrongParameters) - APIdock

destroy_allの挙動が思ったのと違ったのでメモ。

railsでまとめてデータを削除したいときにdestroy_allを使用していたのですが、実際に動いているsql文を見て想定と違ったので調べたのでメモ。

内容としては 数十万件のレコードを舐める時に1000件単位で削除を行おうと思ってコードを書いており、1000件のオブジェクトごとにdestroy_allをかけていました。

想定していた挙動は 1000件まとめてレコードを削除するdelete文が叩かれる事 でしたが、 実際は1レコードずつ削除するdelete文 が叩かれていました。

気になったのでrailsのapidockを見たところdestroy_allの実際の処理は配列のオブジェクトをeachで回して1つずつdestroyを叩いていました。

# File activerecord/lib/active_record/relation.rb, line 398
    def destroy_all(conditions = nil)
      if conditions
        where(conditions).destroy_all
      else
        to_a.each {|object| object.destroy }.tap { reset }
      end
    end

勉強になりました。今回はこれで終わりです。

ssh先のサーバでrailsサーバを立ち上げるシェルを書いたのはいいけど、sshをkillするとrailsのプロセスが残ったままになったのでメモ

今回ハマった点ですが、タイトルの名の通りssh先のサーバでrailsサーバを立てるシェルを書いたのでが、ctrl + c で抜けるとsshはkillされてrailsのプロセスがssh先のサーバで残ったままだったので、解決方法を模索したメモになります。

ssh hostname 'rails s -b 0.0.0.0 -p 3005'

とコマンドを実行した時、ssh先のサーバでrailsサーバを立ち上げてテストを行おうと思っており、実際に問題なく立ち上げることには成功しました。しかし、ctrl + cで処理を終了させるとsshのプロセスだけ死んでrails sのプロセスだけずっと残ったままでした。

プロセスを確認すると↓のようになっており、

$ ps aux | grep ruby
508      17276  1.0  5.2 406732 155300 ?       Sl   12:24   0:05 bin/rails s -b 0.0.0.0 -p 3105
508      17587  0.0  0.0  81796   836 pts/0    S+   12:32   0:00 grep ruby

調べてみると、ttyとかptsの問題のようでした。 下記の記事でわかりやすく説明してくださってました。

qiita.com

簡潔に説明すると

ターミナルからアクセスしていると pts/ というものが表示され、 と表示されている場合はdemonで立ち上がっているよという目印みたいでした。

上記を解決する方法ですが、

ssh -t hostname 'rails s -b 0.0.0.0 -p 3005'

-t オプションを付ければ良いみたいでした。

LINE DEVELOPER DAY 2017に行って来ました

Line Developer Day に行って来ました f:id:yanterakun:20170929003922j:plainf:id:yanterakun:20170929003757j:plain 昨年も参加したのですが、ブログに書くのは今年が初めてになります。

受付

受付を完了するとお昼代としてヒカリエお食事券をいただきました。(2000円分) 写真を撮り忘れていたので、写真はありませんorz 受付を済ませオープニングセッションのホールへ向かう途中に f:id:yanterakun:20170929003820j:plain ホームスピーカーが展示されていました。(めっちゃ欲しかったです

午前のセッション

午前のセッションは2つだけでLineのオープニングとLineが最近力を入れているAI、clovaの2本立てでした

Opening Session

openingはLINEがどのようにしていきたいという話と、もっとossに力を入れていくので、エンジニアのみなさんも力を貸してくださいという内容でした。私自身は勿論OKでした。エンジニアに優しいLINEのお願いとあっては断れるはずありません。

www.slideshare.net

The Technologies in Clova

clovaのセッションではlineがclovaをどのように定義し、これから展開していくかというお話でした。

一番びっくりしたのが、clovaを搭載したwavaは1年前にプロジェクトが始まったらしいです。地固めをしっかりしているLINEさんだからこその開発スピードなのかなと思いました。

(Line Developer Dayのアンケートに回答するとwaveが50名に当たるそうなので、めっちゃ欲しいです。ブログ書くので当たらないかなーって思ってました。)

www.slideshare.net

LUNCH TIME

ランチはお寿司を頂いて来ました。とっても美味しかったです。 f:id:yanterakun:20170929010954j:plain ランチが終わると11階のカフェで休んでいました。f:id:yanterakun:20170929011251j:plain (これだけものが無料で提供されていたので、Lineの懐の深さに驚愕です。

午後のセッション

Gateboxのこれまでとこれから

Gateboxってなにかというとホームスピーカー1つですが、AmazonGoogle、Lineと違うところは端末にキャラクターが存在しており、感情豊かなキャラクターがいるということでした。

いわゆる俺の嫁みたいな感じらしく、コンセプトムービーを見ましたが、これは是非欲しいなと思いました。

clovaとgateboxの提携が決定しているらしく、どちらかに吸収されるという事ではなく、互いに良いところを取り入れて来たいねという形での提携されるそうです。

www.youtube.com

www.slideshare.net

Paying back technical debt - LINE Androidクライアントの事

リファクタリングをしやすい文化と技術を取り入れましょうという内容でした。 ボーイスカウトルールはチームの文化に是非取り入れていきたいなと思いました。(めっちゃ耳が痛いですが

www.slideshare.net

休憩

ここから1時間ほど元会社の上司の方とお話していました。

LINEにおけるBluetoothを活用した取り組みの紹介

主にline beaconの話でした。 本屋と連携しており、LINE漫画を読むことにより、限定特典などもらえるみたいでした。

Tappinessは自販機にline beaconを仕込んでおり、 lineを立ち上げて自販機に端末を近づけるとline payまたは現金で決済が出来、15ポイントでクーポンがもらえるみたいです。

なるほど、line beaconをlineの入った端末のネットを使ってサーバと連携するのか・・・(web buletoothを参考にしているらしいです。

line simple beaconはbuletoothの端末をline beaconに出来るソフトウェアらしく、ラズパイとか使って一回試して見たいなと思いました。

www.slideshare.net

Parallel Selenium Test With Docker

翻訳機を使っていたのですが、英語がかなり聞きやすかったので、英語聴きながら日本語を聞くと訳が分からなくなるので、途中から使うのをやめました。

内容はスライドを見ていただくと8割くらい理解できると思います。

docker+jenkins+seleniumでのテスト行う環境、良いなぁ。

www.slideshare.net

休憩

疲れたので次のセッションは参加せずに休憩してました。 フラフラしていると、上記セッションでお話をされていたTappinessの自販機を発見したので、利用させていただきました。

使い方はLineを起動すると自販機のline beaconが反応するので、Line Payまたは現金で決済を行うと買えました。

suicaと違ってドリンクポイントが貯まり、何回か利用するとお得になると思うと、あったら使うなと思いました。 f:id:yanterakun:20170929012919j:plain f:id:yanterakun:20170929012902j:plain

BOT and the new Comfortableness

最後の技術セッションは message api とline loginの連携が出来るようになったみたいです。

グループを作成した時にユーザーのIDを取得できるようになったので、 個別のユーザーに対してのリアクションが可能に。

rich menuのレイアウトが自由に構築できるようになるみたいです。 自由なデザインが可能になるので、これは遊ぶ楽しさが増えますね。

ただし、rich menuのapiの更新はcommig soonらしいです。 line bot studioも近いうちにリリースされるみたいです。 ( 非エンジニアの人がカルーセルとかで出てくるものを選択してline botが作れるみたいです。コード書けなくてもできるというのが凄いなと思いました。

clovaのAPIも近い内に公開されるみたいです。

結論、rich menuのapiの更新・line bot studio ・clova apiはまだなので、とりあえずmessage apiとline loginで遊んでみてくださいとのことでした。

line api expertが公開されるみたいで、要は一緒にline api作ってくれる人募集との事でした。

www.slideshare.net

Closing Session

クロージングでは本日のセッションの振り返りとLineのこれからどのように展開していくのかというお話でした。京都にも開発拠点は学生との交流がメインであったりと、次を見据えているLineは改めてすごいなと感じさせられました。

www.slideshare.net

最後に

本日のアンケートに回答するとノベルティがもらえました。 その時にclova waveの抽選もあったのですが、筆者は見事当選しました!!

(左上 : ステッカー、右上 : マグカップ、下 : クローバー栽培キット f:id:yanterakun:20170929021621j:plain (clova wave f:id:yanterakun:20170929021656j:plain これで当分頑張れそうな気がします。

本当Line Developer Day最高でした。

開催して下さった関係者のみなさんありがとうございました!

来年も開催されたら是非参加させて頂きたいなと思っています。

linux(centos)の公開鍵を置いているサーバーにsshが出来なくて嵌まったのでメモ。

今回は公開鍵を置いているサーバーにsshが出来なくて嵌まって調べたメモになります。

状況

  • .sshフォルダの権限は700で所有者はログインユーザー
  • authorized_keysの権限は600で所有者はログインユーザー
  • 公開鍵と秘密鍵は対になっている
  • タイポもしていない

この状況でsshが出来ませんでした。

理由

view /var/log/secure

ここに

User your_username not allowed because account is locked

と書かれていました。

↑のログはssh_configの設定でパスワードの設定をしてないユーザーがsshしようとする時に出るみたいです(アクセスを弾いちゃいます)。

view /etc/shadow

↑を見るとUSERNAME:の次に「!!」と書いて有る場合は lockされているみたいです。(パスワードが設定されていると「!!」ではなくランダムの文字列になっているみたいです。

解決

passwd user_name

↑のようにloginユーザーのpasswordを設定してあげるとsshが出来るようになりました。

参考URL

tkuchiki.hatenablog.com

Rspecでインスタンスメソッドのテストに苦労したのでメモ

今回はRspecでメソッドのテストをしたかったのですが、メソッド内で定義しているメソッド先の処理のテストは記述したくなかったので、それを解決したメモになります。

rubyのコード

# 今回テストしたかったのはTest1のexecuteメソッドになります
Class Test1
  def self.execute(test_params)
    Test2.execute(test_params)
    Test3.new(test_params).execute(test_params) # ←こいつが一番の曲者でした
  end
end

Class Test2
  def self.execute(test_params)
    p test_params + 'test2'
  end
end

Class Test3
  def initialize(test_params)
    @test = test_params + 'test3'
  end

  def execute(test_params)
    p @test + test_params + 'test3'
  end
end

テストコード

require 'rails_helper'

RSpec.describe Test1 do
  describe 'テスト1'
    it 'interfaceのテスト'
      test = double('Test3:00000000')
      allow(Test2).to receive(:execute).and_return(true)
      allow(Test3).to receive(:new).and_return(test)
      allow(Test3.new("")).to receive(:execute).and_return(true)
      Test1.execute(anything)
    end
  end
end

Test3のテストコードですが、。最初は下記のように記述していました。

allow(Test3).to receive(:new)
allow(Test3.new("")).to receive(:execute)

↑だとexecuteを呼ぶ時にno method errorがでます。

Test3で定義しているexecuteはクラスメソッドではなくインスタンスメソッドなので、インスタンスにメソッドを定義しないとエラーが出てしましました。

なので、

test = double('Test3:00000000')

↑でインスタンスを定義して

allow(Test3).to receive(:new).and_return(test)

↑でnewした時の戻り値にインスタンスを返えすようにします。

allow(Test3.new("")).to receive(:execute)

↑でインスタンスにexecuteメソッドを定義することによって

Class Test1
  def self.execute(test_params)
    Test2.execute(test_params)
    Test3.new(test_params).execute(test_params) # ←こいつのテストが通るようになる
  end
end

Test3のテストが通るようになります。(解決するまでにめっちゃ時間がかかりました。

参考URL

qiita.com