patorashのブログ

方向性はまだない

FiNC様に会社訪問してきた

仕事で東京出張があったので、目的の打ち合わせが終わった後にどこかに会社訪問してみたいなーと思ってtwitterで募集してみたのだが、突然だったからか、反応が悪かった。しょうがないから23時にラーメンを食べに行った。

日高屋、初めて行ったと思う。美味しかった。会社の昼食用チャットで話題になったことがあったので行ってみたかったところ。

それはともかく、twitterで募集してもあかん!と思ったときに、ふと、最近勢いのあるSlackのruby-jpの存在を思い出したので、そこで募集してみたところ、応答してくださって、FiNC様に訪問できることに!ありがたい~🙏Rubyコミュニティの温かさに触れた瞬間だった。

FiNCについて

FiNCはヘルステックの企業で、最近は中村アンさんを起用したCMを打っててよくテレビで見かけるようになりました。実は自分は今年のRubyKaigiで見かけるまで知りませんでした。

finc.com

RubyKaigiの懇親会でFiNCの方と話す機会があって、業務内容が色々やっているみたいだったから、会社訪問できることになって嬉しい。

案内していただいた

突然の訪問にも関わらず快く受け入れていただいて、社内を一通り案内していただけました。ちなみに私と後輩の2人で伺いました。 会社訪問に慣れてないもので、会社の入り口の写真を撮り忘れてしまったけれど、オシャレな受付でした。オフィスとフィットネスジムが併設されていて、フィットネスジムは一般の方が来るところだそうです。

そして、執務スペースめっちゃ広い。社員数240名くらいということで、開発・マーケティング・デザインと分かれているみたいで、外国人の方も多く働かれているようでした。

その後、開発部屋に案内していただきました。開発部屋のほうはかなり静かで、集中してコーディングする空気。空いているデスクにもディスプレイアーム付きのディスプレイが既に置いてあったのが印象的でした。本棚も技術系の本がビッシリと並んでいる、かと思いきや本棚の下の段にはボードゲームがビッシリとありました。みんなでボードゲームをしたりするそうです。いいなぁ~。

フィットネスジムのほうも軽く案内していただいたのですが、その途中で棚に置いてある健康食品とかサプリメントとかの話になって、フィットネスアプリとジムだけではなくて、ECもやっていて、コラボやタイアップした商品を販売しているということが聞けました。ジムの会員さんに有効な健康食品を案内したり販売したり、トータルで健康をサポートするからということで、多角的で面白そうだなと感じました。

https://mall.finc.com/

フロアを移動

「実はフロアがもうひとつあるんです」と言われ、エレベーターでフロアを移動。こんだけ広いのにまだあるのか!と思いながら移動すると、超広いフロアが…。イベント用スペースだそうです。

f:id:patorash:20190823140923j:plain
FiNC様のイベント用のスペース。めっちゃ広い!

フィットネス系のイベントや、IT系のイベント、会社の集会などをそこで行っているようです。まぁこれだけ広いと社員が240名いても入りそう。バーカウンターがあって、イベント後に飲んだりすることもできるとか。壁にはFiNCのイメージ動画がずっと流されていたのですが、ここでスマブラ大会をやったことがあるとか。めっちゃ楽しそう。

調理師が正社員!

奥のほうに部屋が2つあり、1つは広めのキッチン、もう1つは撮影用・配信用のスタジオでした。調理師を正社員として雇っていて、これから従業員のランチを作っていくために手続きや環境を整える準備を進めているということでした。すごーく羨ましい!!社員に健康的な食事を、ということで素晴らしいなと思います。

開発者募集中だそうです

案内していただいた方は入社3年目でRailsエンジニアの方だったので、お互いどういうことをやっているのか等、話しました。開発と保守のバランスの話とか。これだけ人数がいても足りないのか…と思ってしまうけれど、メルカリとかもtwitterで開発者足りないって言ってるし、どこも人材争奪戦ですね。

まとめ

勢いのある会社すごい。突然にも関わらず受け入れてくださったFiNCさんありがとうございました!

Podcastを聴くようになった

スマホを新しくしてからというもの、小さなライフ チェンジングを色々としていて、それの1つがPodcastを聴くようになったこと。仕事しながら聴いたり、家事しながら聴いたり、通勤しながら聴いたりしている。車のオーディオと接続がよくなったので、気軽にできるようになった😆

大体はエンジニアがやってるやつを流し聞きしてるんだけれど、仕事しながら聴いてるとあんまり頭には入ってこない。まぁでもラジオの代わりみたいな感じで時々「わかる!」とか「その発想はなかった」みたいな話とか出てくるので継続していこうかなと思う。

そもそもPodcastを聴かなかったのは、よいアプリを知らなかったのだけれど、スマホを機種変更したタイミングでアプリを入れ直してたらGoogle PodcastというGoogleが作ったアプリがあったので入れてみたら思いの外よかった。再生速度も細かく変えられるし。

ただ、エンジニアのPodcastを速く聴くと、エンジニア特有の語り口が、早口になってなんとなくヤバイ人感が増したので、あんまり速くするのは微妙だった😥

 

文化を育んでいきたい

あんまり思っていることをブログに書いたりはしていなかったのだけれど、考えていることを文章として残しておくことは、後々の振り返りに使えるかなとも思うので、今後は書いていこうかなと思う。

数ヶ月前から、社内でフロントエンド技術勉強会と称して、勉強会を開催するようにした。ちょうど新入社員も入って2ヶ月目あたりなので、バリバリの技術情報というよりはHTML、CSSの基礎的なところを踏まえていくという辺りを焦点とした。すでに対象の本は読み終えて、今は皆で課題に取り組んでいるところである。

これは私なりの意図があって、新入社員の人たちのスキルアップもあるが、それ以上に「就業時間中に勉強会を開催してもよい」という認識をしてほしかったというのがある。就業時間中に仕事以外のことをするのは良くない、という認識はいいのだが(個人的にはよくないけど)、スキルアップに取り組むことも充分仕事の内に入ることなのだ」、と思ってほしい。

投資は必ずよい結果が返ってくるものではない

仕事なので投資した分の結果(見返り)を求められるのは、そりゃそうだ、とも思うのだが、投資に条件が付きすぎると段々億劫になっていく。必ずよい結果を出すとか、ちゃんとまとめて形にしなければならないとか、プレッシャーがあると伸び伸びとチャレンジできなくなっていく。自分は性善説寄りの思考をしているので、いうても完全に仕事と全く接点のない遊びを業務時間中にはやらんだろうから、自由にさせて、成果が出たら知見を共有していく、という風土にしていきたいと考えている。全てのチャレンジで成功しなくてもよくて、見返りを求められなくてもよくて、試行回数を増やしていく方が重要かなと思う。

認知を変える

就業時間中に読書会、というのは、なかなか新入社員からは提案できないことだろうし、そもそも思いつきにくいだろうと思う。そこで、先輩である自分たちがそれを行うことで「こういうのやってもいい会社なんだな」とまず思ってもらうことが大事。すると、参加者は今後、「次はこういう勉強会しませんか?」と提案しやすくなるだろう、という思惑がある。

主体性を持ってもらう

仕事をする上でずっと受け身でいたい人というのもなかなかいないと思うので、読書会をある程度こなしてきたら、参加者に読みたい本を選んでもらおうと考えている。小さな粒度で主体性をもって決断する機会を設けていく。まぁ大した効果があるかどうかはわからないけれど、何事も慣れなので、そういう場を社内で作ることはいいことかなと思う。また、読みたい本を選ぶ=自分にとって必要な領域は何かを見つめる時間になる、と思う。

継続する

社内勉強会・読書会は基本的には緩くてもいいからずっと継続していきたい。読書会でなくなってもいいから、なんらかの形でスキルアップする会として残していけるようにしたい。そして、それが普通になっていくようにというか、むしろ増やしていきたい。

組織にとって継続は風土・文化になる

文化はある日突然持って来て適用できるものではない。長い時間かけて、守りながら育てていくものだろうと思っている。よっぽどその文化にフィットする人達だけが集まっていたらすぐに浸透するかもしれないけれど、そういうことは稀だ。

そういう文化があるところに転職する方が早い、みたいなことを言う人もいるだろうけれど、その文化も先人の誰かが育てたものなわけで、ならば自分は今の組織で文化を育んでいく側となっていきたいと考えている。

複数やっていく

技術寄りの勉強会と、技術ではなく組織論・思考法・設計なりの勉強会の両輪を回していこうと考えている。これはどちらも仕事する上では大事なこと。技術系が好きなエンジニアは後者寄りの本を自分で読む機会は少ないだろうから、そういう機会を準備してみることで、知見が補完されていく。また、後者のものは他者と意見交換しながら読み進めると新たな気づきが多い。他人の考えを輸入する機会を作って、視野を広げていってもらいたい。

まぁなんだかんだ言っても、自分のためでもあるだけれど、そういうことができる組織って素敵やんと思うので、理想を求めて文化を育んでいきます。

コスパ重視でGalaxy A30に機種変更した

以前まではArrows M04を使っていましたが、使い始めて2年が経過したのでそろそろ機種変を…と思い、後継機種を探すも、ない!!Arrows M05まだ出てない!!! 

仕方ないので他のブランドで探すことに。

Arrows M04の総括

悪かった点

M04は基本的によかったのたけれど、不満点も多々ありました。

  • 容量が16GBしかない
  • メモリが2GBしかない
  • カメラのピントがなかなか合わない

等々。

良かった点

無論、良かった点もたくさんあります。

  • 見た目は普通ながらも、タフで壊れにくい
  • 防水である。洗剤で洗える
  • おサイフケータイである

やはり日本仕様に慣れているため、今後機種変更するにしても、防水とおサイフケータイは外したくありません。

Galaxy A30に決定!

候補は色々ありました。本当に欲しかったのはAquos zeroでしたが、10万円位したため、手が出せず…😣容量128GBとメモリ6GBあるし、他の点でも優秀なので高いのは仕方ないかなとは思います。

mineoユーザーなので、当初はmineoから販売している端末で探してたのですが、いわゆる日本仕様を満たしていて、かつ不満点を解消した手頃な価格の端末があんまりありませんでした。

そこで、mineoからの直販以外で探して見つかったのが、Galaxy A30でした。

仕様に不満点がなかった

Galaxy A30はau, UQ Mobile等から発売されています。mineoでも使えることをmineoのサイトで確認しました(au simであること!)。

ミドルレンジのスマホとしては完璧😃✌️

価格がお手頃

気になるのはお値段なのですが、これだけの仕様を満たしながら、なんと3万円くらい。私はラクマUQ Mobile版を購入しましたが、それくらいでした。

Galaxy A30の使用感

以前の端末がロースペックだったのであれですが、めちゃめちゃ快適です。サクサクです。カメラ性能も私的には充分よいです。おサイフケータイ使えるのは本当に便利。

指紋認証が便利

最初は指紋認証はオマケみたいなもん、という気持ちだったのですが、めちゃくちゃ便利です。パスワード管理アプリが指紋認証に対応しているので、マスターパスワードを入力しなくても指紋認証だけで済むのが最高です。あと、楽天銀行の認証も指紋認証でできました✌️こんなに便利なら指紋認証のあるスマホにもっと早く変更しておけばよかった😂

容量が全然余裕

私は容量16GBの壁に飼い慣らされた、よく訓練されたユーザーなので、ポケモンGOとかすぐ削除していたのですが、全く余裕です。現時点でまだ40GB余っていて、「使いきれるのか…?」という気分です。

まとめ

コスパ重視なら、かなりアリなスマホです!!

mutationでバルク処理をする場合のアプローチ

mutationでデータを更新するGraphQL APIを作りました。しかしそのAPIが時々しか呼ばれないならいいのですが、頻繁に何度も呼ばれるケースだとAPIのへのアクセスが複数回になり無駄が多いので、バルクアップデートみたいなことはできないか?と指摘を受けました。個人的には、「うーん、そうはいってもAPIってそういうもんでしょ…。」と思っていたのですが、他のサービスのケースとかを参考にしてみますと回答して、調査開始。

1度のmutationで同じAPIを使い回す

ググると、バルク処理用のAPIを作れみたいな記事が見つかってコレジャナイ感があったのですが、遂に以下の記事を見つけました。

blog.grandstack.io

path名をつけることで、同じmutationを複数回使うという方法です。 以下のように使います。

mutation {
  path1: updateUserName(input: { userId: 1, name: "foo" }) {
    user {
      id
      name
    }
  },
  path2: updateUserName(input: { userId: 2, name: "bar" }) {
    user {
      id
      name
    }
  },
  path3: updateUserName(input: { userId: 3, name: "baz" }) {
    user {
      id
      name
    }
  }
}

すると結果は以下のように返ってきます。

{
  "data": {
    "path1": {
      "user": {
        "id": "1",
        "name": "foo"
      }
    },
    "path2": {
      "user": {
        "id": "2",
        "name": "bar"
      }
    },
    "path3": {
      "user": {
        "id": "3",
        "name": "baz"
      }
    }
  }
}

おおお、1回のアクセスで3つ更新できた🎉

バルク処理のエラー検知

でもこれってエラーになったらどう検知するのよ?と思い、無理やりエラーが起きるようにしてみました。上記でいえば、存在しないuser_idを渡すとかです。

mutation {
  path1: updateUserName(input: { userId: 1, name: "foo" }) {
    user {
      id
      name
    }
  },
  path2: updateUserName(input: { userId: 2, name: "bar" }) {
    user {
      id
      name
    }
  },
  path3: updateUserName(input: { userId: -1, name: "baz" }) { # そんなユーザはいない!
    user {
      id
      name
    }
  }
}

そうすると、以下のような感じで返ってきました(実装次第ではありますが…)。

{
  "data": {
    "path1": {
      "user": {
        "id": "1",
        "name": "foo"
      }
    },
    "path2": {
      "user": {
        "id": "2",
        "name": "bar"
      }
    },
    "path3": null
  },
  "errors": [
    {
      "message": "ユーザが見つかりません",
      "locations": [
        # 略
      ],
      "path": [
        "path3"
      ]
    }
  ]
}

errorsの配列の中のpathの値を見たら、どのpathでエラーが起きているかが分かるので、対処しやすくなるかと思います。

所感

1つのデータ更新用のAPIと、バルク更新用のAPIをわざわざ作らないといけないのかなー?🤔と考えていたところだったのですが、これならば実装1つでいけるので、大変やりやすいです🥳ただし、トランザクションが必要になるケースの場合は、バルク更新用のAPIを作らないといけないでしょう。

Mojaveでrailsを起動するとpgでセグメンテーション違反になる場合がある

追記

Macの不具合が原因らしいことを突き止めたので色々やったら直ったぽいのでリンクを貼っておきます。

patorash.hatenablog.com

以下、元々の記事

開発マシンを新しいMBPに変えたと同時にMojaveになっていたのですが、そこで開発していると、時々Railsが全く起動しなくなりました。rails consoleとかは問題ないのですが、rails serverすると落ちる。しかもエラーメッセージがあまりにも長いので、標準エラー出力をファイルに出力して確認してみました。

$ bin/rails s 2> log/error.log

すると、以下のような結果に。

/Users/******/.anyenv/envs/rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pg-1.1.4/lib/pg.rb:56: [BUG] Segmentation fault at 0x0000000104994a3a ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18]

-- Crash Report log information -------------------------------------------- See Crash Report log file under the one of following: * ~/Library/Logs/DiagnosticReports * /Library/Logs/DiagnosticReports for more details. Don't forget to include the above Crash Report log file in bug reports.

-- Control frame information ----------------------------------------------- c:0058 p:---- s:0433 e:000432 CFUNC :initialize c:0057 p:---- s:0430 e:000429 CFUNC :new c:0056 p:0016 s:0425 e:000424 METHOD /Users/toko/.anyenv/envs/rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/pg-1.1.4/lib/pg.rb:56 ...略

pgでSegmentation fault

pgでSegmentation faultが発生してます。解決法がわからず、ググってみると、pgを入れ直してみるのがよいとあったので、このエラーが発生するたびに入れ直してました。

https://bitbucket.org/ged/ruby-pg/issues/291/crashbitbucket.org

とりあえず、入れ直すと動くようになりました。

$ bundle pristine pg

しかし、本当にpgのバグなんかなー…🤔自分の設定がどこかおかしいんじゃないか?と疑いつつも騙し騙しの運用をしてました。

pumaを疑う

そして、遂にpgを入れ直しても発生するようになってしまい、どうしたもんか?これは自分の環境だけのことなのか?と思い、周囲に聞いてみるも「その現象はまだ起きてない」と言われるので、再びググることに。rails consoleでは起きずにrails serverだけで起きるため、pumaとかが怪しいのでは?と薄々思っていたところで、そういう情報がヒット。

stackoverflow.com

worker数を複数にするのをやめたら起きないという情報が…。 puma.rbでworker数を指定するところを、developmentの場合は除外するようにしてみます。

workers ENV.fetch("WEB_CONCURRENCY") { 2 } unless Rails.env.development?

半信半疑でやってみたところ、確かに起きなくなりました。

解決?

ひとまず、これでMojaveで開発を継続できるようにはなったのですが、解決したわけではないし、むしろなぜworkerを複数起動するだけでこうなるのか?という疑問が残ります。プロセスをforkするタイミングでPostgreSQLへの接続がおかしくなるのだろうか?とりあえずこの件については時々調査してみようと思います。

Herokuでpuma_worker_killerを適切に設定する

昨年、こんな記事を書いていました。

patorash.hatenablog.com

これで設定できていたと思ったのですが、アクセスが集中したときにワーカーの再起動が起きず、スワップが発生してR14(メモリ関連のエラー)が頻発していました。このときに行った設定はどうも一定時間経過したらpumaのワーカーを再起動するという設定だけで、メモリ使用量が増えたらワーカーを再起動させる設定をしていなかったようでした(完全にミス…)。

puma_worker_killerの設定を行う

puma_worker_killerの設定はREADMEに書いてあったので、今回はそれをちゃんと行いました。

github.com

まず、 config/puma.rb に以下を追加しました。

before_fork do
  PumaWorkerKiller.config do |config|
    config.ram           = 1024 # 単位はMB。デフォルトは512MB
    config.frequency     = 10    # 単位は秒
    config.percent_usage = 0.90 # ramを90%以上を使用したらワーカー再起動
    config.rolling_restart_frequency = 6 * 3600 # 6時間
  end
  PumaWorkerKiller.start
end

その後、bin/rails sをしてみます。

動作確認

puma_worker_killerが起動していたら、以下のようなログが出ます。

$ bin/rails s
=> Booting Puma
=> Rails 5.2.3 application starting in development
=> Run `rails server -h` for more startup options
[37580] Puma starting in cluster mode...
[37580] * Version 4.0.1 (ruby 2.6.3-p62), codename: 4 Fast 4 Furious
[37580] * Min threads: 5, max threads: 5
[37580] * Environment: development
[37580] * Process workers: 2
[37580] * Preloading application
[37580] * Listening on tcp://localhost:3000
[37580] Use Ctrl-C to stop
[37580] - Worker 0 (pid: 37906) booted, phase: 0
[37580] - Worker 1 (pid: 37907) booted, phase: 0
[37580] PumaWorkerKiller: Consuming 764.96484375 mb with master and 2 workers.
[37580] PumaWorkerKiller: Consuming 764.96484375 mb with master and 2 workers.

PumaWorkerKillerが、pumaが使っているメモリの全体量をログに出します。これでいえば、約765MB使ってるということです。

ワーカーが再起動するかチェック

今度は敢えてたくさんのワーカーを起動させて、多くのメモリを使わせてみます。ワーカー数の設定は環境変数WEB_CONCURRENCYにしているので、今回はそれを3に指定してみます。

$ env WEB_CONCURRENCY=3 bin/rails s
=> Booting Puma
=> Rails 5.2.3 application starting in development
=> Run `rails server -h` for more startup options
[38214] Puma starting in cluster mode...
[38214] * Version 4.0.1 (ruby 2.6.3-p62), codename: 4 Fast 4 Furious
[38214] * Min threads: 5, max threads: 5
[38214] * Environment: development
[38214] * Process workers: 3
[38214] * Preloading application
[38214] * Listening on tcp://localhost:3000
[38214] Use Ctrl-C to stop
[38214] - Worker 0 (pid: 38523) booted, phase: 0
[38214] - Worker 1 (pid: 38524) booted, phase: 0
[38214] - Worker 2 (pid: 38525) booted, phase: 0
[38214] PumaWorkerKiller: Out of memory. 3 workers consuming total: 1010.109375 mb out of max: 921.6 mb. Sending TERM to pid 38525 consuming 252.52734375 mb.
[38214] - Worker 2 (pid: 38534) booted, phase: 0
[38214] PumaWorkerKiller: Out of memory. 3 workers consuming total: 1010.12109375 mb out of max: 921.6 mb. Sending TERM to pid 38534 consuming 252.53125 mb.
[38214] - Worker 2 (pid: 38543) booted, phase: 0

921MB以上を使用し(1010MB)、Out of memoryになったため、再起動させているのが確認できました。

Heroku環境ではどう設定すればいいか?

ここで私が気になったのは、config.ramの指定です。上記では1024と固定値にしており、プロダクション環境でもStandard-2X Dynoを使っているので(2X Dynoのメモリは1024MB)これでも問題ないのですが、もしかしたらお試しの環境を準備する際にStandard-1Xを使うこともあり得るかもしれません。もしくは、performance-mとかを使う可能性だってあり得ます。その時に、わざわざこの値を編集したくありません。

Dynoのメモリ量を取得する方法がないかと思い、bashで接続してfreeコマンドを打ってみたら、メモリが60GBと表示されたので(おそらくホストのメモリ量)、軽く絶望していたのですが、なんとなく環境変数を確認したところ、Dynoのメモリ量が取得できました!MEMORY_AVAILABLEで取得可能です。一応、試してみました。

Standard-1X Dyno

$ heroku run --size=Standard-1X bash --app hoge 
Running bash on ⬢ hoge... up, run.9315 (Standard-1X)
~ $ printenv
略…
MEMORY_AVAILABLE=512
略…

Standard-2X Dyno

$ heroku run --size=Standard-2X bash --app hoge 
Running bash on ⬢ hoge... up, run.6999 (Standard-2X)
~ $ printenv
略…
MEMORY_AVAILABLE=1024
略…

Performance-M Dyno

$ heroku run --size=Performance-M bash --app hoge 
Running bash on ⬢ hoge... up, run.2701 (Performance-M)
~ $ printenv
略…
MEMORY_AVAILABLE=2560
略…

puma_worker_killerに設定する

これらを元に設定します。puma_worker_killerのデフォルト値が512MBなので、そうしておきます。

before_fork do
  PumaWorkerKiller.config do |config|
    config.ram           = Integer(ENV.fetch('MEMORY_AVAILABLE', 512))
    # その他は省略
  end
  PumaWorkerKiller.start
end

これで、どのWeb Dynoにも対応したpuma_worker_killerの設定が完了しました🎉

ローカルでワーカーの再起動を起こさないようにする

このままだと、ローカルでrailsを起動すると、環境変数MEMORY_AVAILABLEがないため、512MBになってしまい、ワーカーが複数起動する設定だと、ワーカーの再起動を繰り返すだけになってしまいます。dotenvを入れていたら、この環境変数を追加しておきましょう。

MEMORY_AVAILABLE=1024