男女比はカレーと福神漬けと同じくらい

マサカリよろしくお願いします。

ModelとConcernに記述したcallbackの実行順

結論

Concern に記述された callback が先に実行される。
既存の callback を Model もしくは Concern に移すときは気をつける必要がある。


以下確認コード

class Hoge < ApplicationRecord
  include HogeConcern

  before_validation -> { puts "before_validation in Class" }
end

class HogeConcern
  extend ActiveSupport::Concern

  included do
    before_validation -> { puts "before_validation in Concern" }
  end
end
[2] pry(main)> Hoge.create
   (0.5ms)  BEGIN
before_validation in Concern
before_validation in Class
...

マイクロサービス他社事例

マイクロサービスの事例を調べた。 多少まとめた方がいいんだろうけど、一覧があるだけでも一定の価値はあると思ったので載せておく。


その他

更新系 Web API response 再考

更新系の Web API response をどうすればいいのか毎回悩むので良い感じのデザインパターンをパッと調べたけど見当たらなかったので友達と議論した。

どんな場合はどんな response を返すべき、という結論には至らなかったけど一通りのパターンは洗い出せたと思うのでメモしておく。

TL;DR

  • response bodyを返さないパターン
  • 更新したリソースの id だけ返すパターン
  • 更新したリソース全体を返すパターン
    • 更新したリソースだけを返すパターン
    • 関連する情報を含む更新したリソースだけを返すパターン
  • 更新したリソースのうちクライアント側が必要な情報だけを返すパターン
    • 柔軟なinterfaceである程度endpointを共通化するパターン
    • 固定のinterfaceで必要な情報のユースケース分のendpointを作るパターン

response bodyを返さないパターン

204 を返すパターン。 protocol buffer だと google.protobuf.Empty を返すパターン。

リソースの新規作成など、response として property を返さなくてもクライアント側でリソースの状態を知る術がある場合に使う印象がある。

更新したリソースの id だけ返すパターン

response body を返さないパターンと使う場面は大きく変わらない気がしている。

しかし、クライアント側が更新したリソースの id を知る術がない(request で複数のパラメータによってリソースを一意に特定するといった id 以外の情報で指定している場合)がクライアントが id を key として状態を管理しているため、id が知りたい場合などでは id だけ返すパターンの有用性が出てくる。

更新したリソース全体を返すパターン

これは backend 側の目線で返すリソースを決めるパターンで大別すると2つに分類できる。

  • 更新したリソースだけを返すパターン
  • 関連する情報を含む更新したリソースだけを返すパターン

どっちで返すかはパフォーマンス次第という印象がある。

更新したリソースのうちクライアント側が必要な情報だけを返すパターン

これは frontend 側の目線で返すリソースを決めるパターンで実装方針は2つに分類できる。

  • 柔軟なinterfaceである程度endpointを共通化するパターン
  • 固定のinterfaceで必要な情報のユースケース分のendpointを作るパターン

柔軟なinterfaceである程度endpointを共通化するパターン

サーバに対してクライアントが複数存在しユースケースがたくさんある場合、こっちの方が手早く開発できる印象がある。 GraphQL, Protocol Buffer の Field Mask, Rails active-model-serializer の fields パラメータなどはこれに該当する。

固定のinterfaceで必要な情報のユースケース分のendpointを作るパターン

サーバとクライアントが 1:1 対応しているような比較的小規模なアプリケーションではこっちの方が手早く開発できる印象がある。

Thanks to

DB共有しているRailsアプリケーションのテストを書くときに気をつけること

これで2時間くらい潰れたので供養のために書いておく。

TL;DR

DatabaseCleanerのstrategyがtransactionになってると、もう一方のRailsアプリケーションに反映されないから気をつけよう

遭遇したこと

2つのRailsアプリケーションで1つのDBを共有しているサービスで以下のようなテストを書いていた。

  1. Rails 2 がレコードを作成する
  2. Rails 2 がRails1に対して作成したレコードのidを渡しAPI callをする
  3. Rails 1 が受け取ったidのレコードを取得し、中身を書き換える
  4. Rails 2 で中身が書き換わったことを確かめる
# めちゃくちゃ雑な図解

             Rails 1                Rails 2
                |                     |
                |                 a = A.create!
                |                     |
          A.find(a.id)                |
      a.update!(foo: "bar")           |
                |                     |
                |          expect(a.foo).to eq "bar"
                |                     |

実行すると、A.find(a.id)がResourceNotFoundを返してテストが落ちていた。
テストコードを何回見直してもおかしい箇所は見当たらず、悲しい気持ちになっていたら database_cleanerが悪いことをしている気がしたので確認してみると、

RSpec.configure do |config|
  ...
  condig.before do |_|
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.start
  end
  ...
end

database_cleanerのstrategyがtransactionに設定されていた。
なので変更してもDB自体にはcommitされていなかったためにRails 1から作成したレコードは見えていなかった。

よく考えると当たり前なんだけど、問題に直面するとなかなか気づかなかったりするのでメモメモ。

新卒ISUCONで優勝しました

新卒ISUCON(SHISUCON)とは

弊社ではwebアプリケーション開発の基礎となる知識を実践的に吸収するため、新人研修の一環として新卒メンバー+CTOでISUCONを行うことが恒例になっています。
このISUCONをSHISUCON(SHinsotsu ga Iikanjini Speed Up Contest)と呼んでいます。(他の人は新卒ISUCONと呼んでいます)

ルールは以下の2点を除けば本来のISUCONと同じです。

  • 競技時間が11:00~17:00と本番より2時間短い
  • 13:00~13:00, 15:00~15:30の計2回、ISUCON上級者のメンターがアドバイスをしにきてくれる

↓去年、一昨年のSHISUCONの内容

www.wantedly.com

www.wantedly.com

SHISUCONの意義についてはこれがわかりやすい

speakerdeck.com

今回のSHISUCONではYahoo!Y!SUCONを利用しました。
Y!SUCONは1台のサーバで解くことが想定されている問題ですが、今回は3台のサーバ(c4.large)が与えられました。

新卒ISUCONで優勝しました

新卒ペア*3 + CTOおひとりさまの4チームで戦い、自分は @spring1018 とチームを組みました。(新卒ではないけど別業種からの転職&本人の強い参加希望があったので新卒枠としての参加になったらしい?)

自分達のチームは24,550点でフィニッシュしCTOをおさえて優勝することができました。 自分達は結局サーバを1台しか使っていないのでY!SUCONと同じ状況でのscoreとなっています。
今まで行われていたSHISUCONではCTOが毎回優勝していたのでCTOから優勝の座を奪えてよかった✌️

f:id:euglena1215:20190512003038p:plain
青いのが自分のチーム

f:id:euglena1215:20190512005606p:plain
負けて体調が悪化するCTOの図

戦略

まず、自分も@spring1018もインフラエンジニアではないため制限時間内でのmiddlewareの完璧なチューニングは難しいだろうと考えました。 そこで 「競技時間中はインフラのことをなるべく考えずにアプリケーションに集中する、そのために80%のクオリティのチューニングはコピペで終わるような秘伝のタレを作っておく」 と話し合って決めました。

事前準備

過去問を解く

@spring1018は初ISUCONだったということもあり、問題形式に慣れることを目的にISUCON7予選を本選出場ラインのscoreが出るまで解きました。
(ネットワーク帯域の再現はしていないので正確ではない & 本選出場のscore届くまでに2日かかった)

自分はISUCONは参加したことがあったのですが、アプリケーションしか触ってこなかったのでインフラ周りの知識が皆無でした。
そのため個人的にISUCON6予選、ISHOCON2*1を解いてインフラ周りのハマりポイントに一通りハマっておくよう心がけました。

wikiに諸々の情報をまとめる

Home · wantedly/shisucon2019-teppei-haruki Wiki · GitHub

競技開始前からリポジトリを作っておき、wikiを見ればほとんども問題が解決することを目指し充実させました。
普通に便利なのでもうちょっとちゃんとまとめてブログの記事にしたい.

作業フェーズをリマインダーに設定しておく

f:id:euglena1215:20190512181402p:plain:h300
こんな感じ

人間は焦ってしまうと予定を忘れる傾向にあるので複数台使うかどうかの最終判断/再起動試験チェックなど忘れたら困るやつは予めリマインダーに設定しておきました。 これで時間のことを気にせず安心して改善に取り組めました。 作業フェーズもwikiに書いていた。

本番(時系列)

自分のやったことを時系列で書いていく、ペアの@spring1018がやったことは↓に記事のリンクが貼られるはず

spring1018.hatenablog.com

10:50ごろ 競技開始
真っ先にbenchを走らせて初期scoreを確認、確か1500点くらいだった気がする

10:58 ~/をgit管理下に

11:17 nginx, mysql, sysctlをgit管理下に移動

11:28 kataribe, myprofilerが動く状態になる
@spring1018がDB schemaの調査を終わらせてくれていたのでkataribe, myprofilerを見つつbenchを回した。
kataribeはこれ、myprofilerはこれ
kataribeから「この時点では静的ファイルもアプリケーションを介して配信していたのでnginxで配信した方がいいよね、ついでに304つけちゃおうか、ついでにnginxの秘伝のタレ流し込んじゃおうか」と判断。
myprofilerから「tweets系が明らかに遅い、一旦created_atにindex貼ってお茶を濁そう」という判断をした。(確かあんまりscore変わらなかったはず)

11:50 nginxの秘伝のタレを流し込む hiden nginx by euglena1215 · Pull Request #6 · wantedly/shisucon2019-teppei-haruki · GitHub
scoreはほぼ変化なかったけどkataribeががらっと変わって GET /が遅すぎるということがわかった
ISUCON序盤のはずなのに全然CPUを使ってなかったのでおもむろにunicornのworkerを1→2に増やしたら1500→1700になってDBのCPUがボトルネックであることがわかるようになった。

12:00すぎ アプリケーションの概要をきちんと把握する
ようやくボトルネックの箇所がわかるようになってきたので二人でアプリケーションの概要の整理をした

  • isuwitterとisutomoが2つに分かれている(DBも別)
  • tweetsのオーダーが10万なので重そう、GET /ではフォロワーのツイートだけでいいのに全員のツイートを取得している。無駄っぽい。
  • GET /でusers.nameを取得するためにN+1が発生している、直したい

12:30ごろ DBを1つにくっつける
初期実装ではisuwitterとisutomoの2つにDBが分かれていたのをisuwitter DBにfriendsテーブルをもってくることによって1つにくっつけた。
理由は以下の通り。

  • 今後2つのサービス(isuwitterとisutomo)をくっつける可能性がある、そうなったときには2つのDBとのconnectionを張る必要がでてきて2倍のconnectionが必要になる
  • 後半にテーブルの移し替えを行うとindexを貼り忘れるといった可能性がでてくる、なら何も手をつけていない今のうちにやっちゃえばよさそう

結局http通信による遅延がボトルネックになるところまでチューニングが進まなかったのでほとんど意味はなかった気がする。

15:03 GET /でフォロワーの最新50件のツイートだけを取得するようにする 1700→3800 where friend ids by euglena1215 · Pull Request #14 · wantedly/shisucon2019-teppei-haruki · GitHub
この修正に2時間かけてしまい、申し訳ねえ申し訳ねえと呻きながら修正したら3800に点数が上がってホッとした。

kataribeを見るとこんな感じにGET /が若干早くなってGET /hashtagGET /searchと同じくらいになった。
なので次はGET /のN+1改善とdef searchで実行しているクエリを絞るのをやっていこうという話になった。

↓は申し訳ねえ申し訳ねえと呻きながらbinding.pryしているときに送られてきた自分の部署のリーダーからのDM。絶対に許さない。

f:id:euglena1215:20190513011018p:plain
自分のscoreが伸びないことを喜ぶチームリーダーの図, 一番下が自分のチーム

15:28 GET /のN+1改善 3800→3941 global user hash by euglena1215 · Pull Request #16 · wantedly/shisucon2019-teppei-haruki · GitHub
全然伸びなくて :thinking_face: という感じだった。SHISUCON終了後にキャッシュをインスタンス変数からThread.currentに持ち替えたら50000→59000とガッと上がったので最初からThread.currentで保存するようにしていたらもっと伸びていたかもしれない。

15:32 def searchで実行しているクエリの取得数を絞る by @spring1018 3941→13311 change get_all_tweets by spring1018 · Pull Request #17 · wantedly/shisucon2019-teppei-haruki · GitHub
これが効いてかなり点数が伸びた。
kataribeを見るとこんな感じにまたGET /ボトルネックが戻ってきた。

この辺りから複数台構成を意識し始めて@spring1018に2,3台目のセットアップをお願いした気がする。

16:00 GET /でisutomoにアクセスしていたのを直接実行するように変更した 13311→14865 join GET / by euglena1215 · Pull Request #11 · wantedly/shisucon2019-teppei-haruki · GitHub

この辺りで@spring1018が2台目のサーバの/etcを全消去するというアクシデントが発生し、2台目は復旧不能になってめちゃくちゃ面白かった。
一応3台目もセットアップするようお願いしたけど、さすがに時間が間に合わず最後まで1台のサーバで動かすことになった。

16:15 tweets tableにuser_id, created_atの複合indexを貼った 14865→23914 myprofilerでスロークエリを確認するとSELECT * FROM tweets WHERE user_id = ? ORDER BY created_at DESCが一番多かったのでindexを貼るとすごい点数が上がった。わーい。

16:36 erbをerubisに変更 23914→24951 add erubis by euglena1215 · Pull Request #20 · wantedly/shisucon2019-teppei-haruki · GitHub 定番のやつ

この辺で「もうこれ以上触ると壊れそうだし何もしない方がいいよね*2」ということで片付けて撤収した。

その後結果発表で優勝したと聞いてよかったねーとなった。

感想

今まで参加していたISUCONではインフラ周りを全く触っていなかったのでとても良い勉強になってよかった。
本番のISUCONも本選出場できるよう頑張りたいです。


延長戦

競技終了後、雑談をしているとその間に他のチームが47000点を叩き出していて悔しかったのでもう少し頑張ることにした。

最終的に65000点で終了した

*1:本来はSHISUCONではこの問題を使う予定だったけど自分が解いていることを知って急遽SHISUCON2日前に問題変更したらしい。ごめんなさい

*2:と言いつつも競技終了5分前に「とはいえSELECT *くらいは消せるのでは?」と修正してしまいエラーがめちゃくちゃ出て死ぬかと思った。触っちゃダメ絶対

心理的安全性について

バイト先の人に「うちで働く可能性ってある?可能性が低いとしたら何があれば高くなる?」という質問をされて考えたことを書きます。

人は皆「心理的安全性」の高いところで働きたい

心理的安全性を「自分がそこに所属していてもよいと思えるかどうか」と定義したとき、大きく分けて3つの心理的安全性が存在するのではないかと考えました。

  1. 精神的な心理的安全性
  2. 金銭的な心理的安全性
  3. 成長に対する心理的安全性

そして、それぞれの心理的安全性に対する係数を自分の中で持っていて、係数を掛け合わせた3つの和を各企業で比較して「どこで働くか」を決定しているのではないかと考えました。

a + b + c = 1; 0 ≦ a,b,c ≦ 1

働きたさ = a * 精神的な(ry + b * 金銭的な(ry + c * 成長に対する(ry

式で表すとこんな感じです。

精神的な心理的安全性

本来の意味での心理的安全性です。 「精神的な」と「心理的」は意味が重複している気もしましたが、明確に分けて使いたかったのでこのように表現しました。

基準として挙げるなら

  • チーム内で安心して発言できるか
  • チームにおいて自身が必要な存在だと思えるか

とかだと思います。

精神的な心理的安全性を見極める方法は

くらいしか思いつかないです。

しかし、残り2つの心理的安全性と比べチームで大きく上下する気がします。
入社するまで部署が決定しないような大きめの会社だとギャンブルですね、、、

金銭的な心理的安全性

ざっくりと言えば「お金がいっぱい・安定してもらえるかどうか」です。いくら精神的に肯定されているとしても求める生活水準に達していないと働き続けるのは難しいですよね。

会社が潰れにくいかどうかもこの安全性に入るのではないでしょうか。
大きめの会社は金銭的な心理的安全性が高く、小さいベンチャーのような会社は金銭的な心理的安全性が低い傾向にあると言えます。

基準として挙げるなら

  • 満足した水準の生活が送れるか
  • 突然辞めさせられてもXヶ月無職で生きていける程度の貯蓄が可能か
  • そもそも辞めさせられる可能性が高いかどうか

とかだと思います。

金銭的な心理的安全性を見極める方法は

  • 提示された年収を見る
  • 会社の業績をチェックする
  • 働いている人のTwitterとかをチェックして生活レベルを把握する*1

くらいだと思います。

成長に対する心理的安全性

ざっくり言うと「自身の市場価値が上げやすい環境か」、もっと言うと「何も考えなくても自然と市場価値が上がっていく環境か」です。

精神的な心理的安全性は「自分がそこ(そのチーム/企業)に所属してもよいと思えるか」だとすると、成長に対する心理的安全性は「自分がそこ(その業界)に所属してもよいと思えるか」に対応すると考えています。

もちろん、何も考えなくても自然と市場価値が上がっていく環境に属しているから自身は何も努力しなくていい、という話ではないです。
毎日5時間勉強するのが当たり前の高校に通っている人と毎日カラオケ・ゲーセンで遊び呆けているのが当たり前の高校に通っている人*2を大学受験の入試難易度という物差しで比較すると有意な差は存在するよね、という話です。

そもそも学び続ける必要があるのかという議論があると思いますが、自分の所属するIT業界の中のWebサービス開発の分野においては「学び続ける必要がある」と考えています。
理由は、利用している技術の代謝が早いため、今主流となっている技術が1,2年後も主流であるとは限らないからです。 この辺りは散々議論されてるのでここでは割愛します。

成長に対する心理的安全性を見極める方法は

  • 技術発信をしている
  • 技術的に難しいとされる領域に取り組んでいる
  • 勉強会の会場になっている
  • 強い人がたくさんいる

とかになると思います。

 


 

人生のフェーズによってどの心理的安全性を重視したいかは変わってくるだろうし*3、会社によってもどの心理的安全性を重視しているかは違うと思います。*4

なので、現時点での自分の志向性に合った心理的安全性を重視している会社を選ぶのが良さそうです。

*1:一応書いておくと自分はこれやったことないです

*2:それはそれで楽しそう

*3:所帯を持つと「一定ラインの金銭的な心理的安全性は必須」とかなりそう

*4:働きやすさを推す会社もあるし、高給を推す会社もあるし、圧倒的成長を推す会社もある

引っ越し備忘録

筑波大学に3年次編入してからもうすぐ2年になります。 卒業に必要な単位は取得し、卒業論文も提出し、あとは卒業研究の発表と細々した課題を出せばOKという状態になりました。

ありがたいことに就職先も決まっています。
オフィスは都内なのでつくばからは通えません。(ぜっっっっったい通いたくない)
なので引っ越し先を探して引っ越すことにしました。

ちなみに情報科学類に3年次編入した同期で院進せずに就職したのは自分だけなはず。
なぜ院進せずに2年で卒業する判断をしたのかはまた今度書きます(多分)。

2年前に編入でつくばに来た時に引っ越しを経験したはずなのに全然覚えてなかったため、忘備録としてまとめておくことにします。

いつ・どうやって物件を探すか

まず悩んだのが「いつ・どうやって物件を探すか」でした。
これは自分なりの答えを持っていて、それは「ちょい遅めに不動産屋さんに行く」です。

「ちょい遅め」とは4月入社の場合、1月第2週くらいを指します。 成人の日前後辺りでしょうか。

Q. なぜちょい遅めなのか

Ans. たくさんの候補の中から探すのが面倒だから。

自分は物件の条件の下限が高専の寮になってしまっていました。
そのため、ほとんどの物件はパスし「まあ住めないことはないよなあ」と全然絞れませんでした。
なので他の人がある程度選んだ状態から物件決めようと考えました。

加えて、都内の物件だと契約後2,3週間以内から家賃が発生するケースが多く、住んでいないのに家賃を払うのはもったいないな、という気持ちもありました。

Q. なぜ不動産屋さんなのか

Ans. いい感じにチョイスして提案してくれるから。

これに尽きます。web上のだと迷ってしまうので。

物件の検索条件をどうするか

先ほど述べた通り、ほとんど物件に対する必須の条件がありませんでした。

強いて挙げるなら

  • オフィスから1.5km圏内(社の住宅補助の範囲)
  • 1Kで7畳以上
  • コンビニかスーパーがそれなりに近い
  • 自転車の置けるスペースが欲しい
  • 家にお風呂がついていてほしい

くらいだと思います。 住宅補助圏内以外の条件は無理なら無理でまあ対応できるかなと考えてました。

結果どうやって決めたかと言うと、不動産屋さんが提案してくれたものの中からなんとなく良い気がする物件を選択しました。

次引っ越すときはもっと主体的に決めようと思います。

初期費用で全財産溶ける

初期費用は家賃の4~5ヶ月分だから早めに貯金しておこうね、という話です。

初期費用は敷金、礼金、仲介手数料、初月家賃、保険、鍵交換代などによって構成されています。 敷金・礼金がそれぞれ1ヶ月分、初月家賃はもちろん1ヶ月分、仲介手数料1ヶ月分、保険・鍵交換代その他諸々合わせて1ヶ月分、合計して5ヶ月分という感じでしょうか。

ざっと計算すると

家賃 8万 → 初期費用 32~40万
家賃 10万 → 初期費用 40~50万
家賃 12万 → 初期費用 48~60万

くらいになるかと思います。

都内のイケイケIT企業のオフィスへ自転車通勤できる物件となると1K7畳で10万前後するのは普通みたいです。

つくばの家賃は3~4万くらいで初期費用もそれほどだったというのもあり、あまり準備してなかったので全財産が溶けました。

引っ越しタイミングでバロンチェアを買う計画を立てていたのですが、一瞬で消滅しました。

引っ越し運送費は時期によって値段めっちゃ変動する

サカイ引っ越しセンターは実際に家まで来てくれて荷物の量から見積もりを出してくれます。
自分は何もしなくていいのでありがたい。 ついでにお米2kgくれました。

で、見積もり結果は

  • つくば〜東京
  • 3月上旬
  • 家具一式+段ボール20箱分(よくある一人暮らし分程度)
  • 荷造りと荷解きは自分で行う

で8万ちょいでした。
荷造りと荷解きもやってもらうプランにすると+3,4万くらいになるとのこと。
5~10万くらいなのかなと思っていたので想定内です。

見積もりに来てくれた人に話を聞くと

  • ヤマト運輸が引っ越し事業から撤退したせいでまじヤバイ。
  • もし引っ越し時期が3月下旬だったら3,4倍の見積もり金額を提示してる。
  • 3月下旬に北海道〜東京、一人暮らしサイズで100万近い額を提示したことがある。客からの怒られが発生した。が、他の引っ越し業者は見積もりを出すことさえできなかったので客は100万近い額で引っ越しを行った。

など色々面白い話が聞けました。

なぜ繁忙期はそこまで値段が跳ね上がるのかという質問をしたところ、
「自社で持っている運送能力を超える量の注文を受けると運送能力を外注して注文を遂行する、という仕組みになっている。外注はオークションのような仕組みになっていて、競合他社と文字通り競り合った金額分引っ越し費用に上乗せされてしまう」
との回答が得られました。 なるほど。

運送能力の入札の最適化とトラックへの積み込みの最適化(1つの大きなトラックに複数人分の荷物を載せることがある)の2つを同時に解いているらしく、なかなか面白そうな領域だなと思いました。

ライフライン・住民票といった事務処理

電気

1週間以上前の申し込みであればインターネット申し込みができる。
最悪前日でも電話すればいける。 立会いの必要なし。

オール電化のところに引っ越す場合、料金プランの関係で契約当日はお湯が沸かせないので注意が必要。

ガス

立会いの予約さえ取れれば前日でもいける。 立会いの必要あり。

水道

前日でもいける。 立会いの必要なし。

役所関係

転出・転入の申請ができるのは平日だけなので適当なタイミングで行く。

住所変更

各種サービスで入力している住所を変更する。

多分色々と忘れているものがあるので引っ越し先への郵便の転送サービスの申請した。


 

次に引っ越すときは東京の波に揉まれて調子乗った物件の条件を提示してたりするのでしょうか。
自分の成長が楽しみです。