まっしろけっけ

めもてきなやーつ

AWS ElastiCache for Memcached の AutoDiscovery についての調査

はじめに

ElastiCache for Memecached is これ
AutoDiscovery is これ

最近転職して SRE として生きているのですが、週末に ElastiCache for Memcached の空きメモリのアラートが上がっておりこれってスケールアップするのがいいのか node 増やすのがいいのか?という話になりました。
そこでそもそもどうやって書き込む node が決まっとるんだっけ?となったのが調査の発端。

前提条件

ElastiCache for Memcached を使っているアプリケーションは下記

Ruby on Rails
Dalli を使っている

Rails.cache の設定や Dalli::Client には AWS console で確認できる `設定エンドポイント` を渡している。

調査

僕の想像では設定エンドポイントを指定するとなんかいい感じに(key を元に?) node に書き込みデータを分散させて、取得の際もいい感じに(key を元に?) node からデータを取得してくれるのでは?もしくは node と言いつつ同じデータを書き込んだりしている?(でもそれって node として意味なくない?)と思っていた。

後者に関しては、その予想通りなら複数 node はメトリクス上同じような波形になる項目が多いはずだが実際にはバラバラなのでそうじゃないっぽいというのが理解できる。
その他にも転職したばかりなので cache 周りの構成や仕様などを把握出来ていないので色々見ながらふむふむ〜となっていたら同僚が下記の issue を見つけてくれた。

github.com


要するに書き込むデータを各 node に分散したければ Rails.cache の設定や Dalli::Client には複数の node の hostname を渡さないといけないのだけれど、node は増やしたくなったり減らしたくなったりするよね。そのために AutoDiscovery という機能を使って node の一覧を取得できるようになっているから Dalli を使う場合は dalli-elasticache を使って node 一覧を取ってきてからそれを Dalli::Client に渡してねという話

つまり、僕が想像していた設定エンドポイントを指定するとなんかいい感じにしてくれるのではなく、複数の memcached server の hostname を Dalli に渡すと Dalli がいい感じに分散してくれるようだ。(確かに Dalli::Client に複数の hostname を渡せるようになってるのなぜ?となっていた)

検証

$ bundle exec rails c

nodes = %w[0001-hostname 0002-hostname]

node01 = Dalli::Client.new(nodes[0])
node02 = Dalli::Client.new(nodes[1])
cluster = Dalli::Client.new('cluseter endpoint')

cluster.set("abc", "1")
Dalli::Server#connect cluseter-endpoint.apne1.cache.amazonaws.com:11211 # cluster endpoint に書き込まれたログ

cluster.set("bcde", "2")

node01.get("abc")
"1"
node02.get("abc")
nil
node01.get("bcde")
"2"
node02.get("bcde")
nil

# ここまででどうやら 0001 にのみ書き込みが行われていそうなことがわかる

100.times { |i| cluster.set("abc#{i}", i) }
100.times { |i| pp node01.get("abc#{i}") } # 1 ~ 100 まで取得できる

# 掃除
cluster.delete("abc")
cluster.delete("bcde")

100.times { |i| node01.delete("abc#{i}") }
100.times { |i| node02.delete("abc#{i}") }

自分の最初の想像だとこれで node01 と node02 にいい感じに ElastiCache 側が分散してくれている想定だったが 1 つの node にのみ書き込みが行われるというのがわかる

dalli-elasticache を使用した際の挙動確認

$ bundle exec rails c

nodes = %w[0001-hostname 0002-hostname]

node01 = Dalli::Client.new(nodes[0])
node02 = Dalli::Client.new(nodes[1])
elasticache = Dalli::ElastiCache.new('cluseter endpoint')
cluster = Dalli::Client.new(elasticache.servers)

cluster.set("abc", "1")
Dalli::Server#connect 0002-hostname.apne1.cache.amazonaws.com:11211 # 0002 に書き込みされたログ

cluster.set("bcde", "2")
Dalli::Server#connect 0001-hostname.apne1.cache.amazonaws.com:11211 # 0001 に書き込みされたログ

# 取得

cluster.get("abc")
"1"
cluster.get("bcde")
"2"

node01.get("bcde")
"2"
node02.get("bcde")
nil

node01.get("abc")
nil
node02.get("abc")
"1"

# ここまででどうやら key によって書き込み先が分散していることがわかる

100.times { |i| cluster.set("abc#{i}", i) }
100.times { |i| pp node01.get("abc#{i}") } # 1 ~ 100 の間で取得できない値が存在する
100.times { |i| pp cluster.get("abc#{i}") } # 1 ~ 100 まで取得できる

# 掃除
cluster.delete("abc")
cluster.delete("bcde")

100.times { |i| node01.delete("abc#{i}") }
100.times { |i| node02.delete("abc#{i}") }

key によって書き込む node が分散されていることがわかる

他にも下記などで memcached のデータを調査

$ echo "stats items" | nc 0001-hostname.apne1.cache.amazonaws.com 11211
$ echo "stats cachedump 5 100" | nc 0001-hostname.apne1.cache.amazonaws.com 11211

さいごに

ということでいい感じに node に書き込みを分散させたいなら dalli-elasticache などを使うと良さそう。
JavaPHP では公式の client があるみたいですよ

ElastiCache for Memcached をはじめて使っていた現場で既に設定エンドポイントを直接指定するコードになっていたので ElastiCache 側がなんかいい感じにしてるんや〜と想像していたがどうやら違ったようでちゃんと検証する機会が持てたのでよかったという感想

とブログを書いて前の職場に今も副業で関わっているのでコードを見てみたら普通に dalli-elasticache 使ってたわ〜となったオチ

Rails の Credentials で使用する yml.enc を変えたい

はじめに

Rails の Credentials の機能がどんなものなのか?は割愛しますが、デフォルトで読み込まれる yml.enc 以外を使いたい場合ってありませんか?
一般的には無いかもしれませんが自分の環境ではあったんですよ。その為に credentials で読み込まれる yml.enc がどのように決まるか?というあたりのコードを少し前に読んで理解〜となったのでメモを残す。

環境は Rails7.0

デフォルトで読み込まれる yml.enc に関して

まずは rails new した後の config 内はこんな感じ

$ ls -1 config
application.rb
boot.rb
cable.yml
credentials.yml.enc
database.yml
environment.rb
environments
importmap.rb
initializers
locales
master.key
puma.rb
routes.rb
storage.yml

# credentials.yml.enc を編集したい場合は下記のようにすればよい
$ EDITOR=vi bundle exec rails credentials:edit

# 下記で中身の確認ができる
$ bundle exec rails credentials:edit

次に environment 毎に yml.enc を分けたい場合はどうするか?というと下記のようにする

$ EDITOR=vi bundle exec rails credentials:edit --environment development
# 保存すると
Adding config/credentials/development.key to store the encryption key: xxxxxxxxx

Save this in a password manager your team can access.

If you lose the key, no one, including you, can access anything encrypted with it.

      create  config/credentials/development.key

Ignoring config/credentials/development.key so it won't end up in Git history:

      append  .gitignore

File encrypted and saved.
# production の場合は
$ EDITOR=vi bundle exec rails credentials:edit --environment production

$ ls -1 config
application.rb
boot.rb
cable.yml
credentials
credentials.yml.enc
database.yml
environment.rb
environments
importmap.rb
initializers
locales
master.key
puma.rb
routes.rb
storage.yml

$ ls -1 config/credentials
development.key
development.yml.enc
production.key
production.yml.enc

こんな感じになります。.key に関しては下記のように環境変数で指定する方が楽かなと個人的には思います。

$ RAILS_MASTER_KEY=xxxxx EDITOR=vi bundle exec rails credentials:edit --environment production

この状態だと RAILS_ENV が development/production の場合はそれぞれ config/credentials/development.yml.enc と config/credentials/production.yml.enc が読み込まれます。
では RAILS_ENV test だとどうなるかというと config/credentials.yml.enc が読み込まれます。

このようにどの yml.enc を使うか?を決めているのが下記のコード
難しいコードでは無いので読んでもらえればわかりますが config/credentials/#{Rails.env}.yml.enc が存在すればそちらを、存在しない場合は config/credentials.yml.enc を読み込むという感じになっています。

github.com

基本的な挙動はこれでおしまいです。

独自のルールで yml.enc を指定したい

ここからが本題
上記で基本的に読み込まれる yml.enc に関しては理解できましたが独自のルールで読み込まれる yml.enc を決めたいとなった場合にどうするか?というと下記のようにします。

# config/application.rb などで

module Testapp
  class Application < Rails::Application
    config.credentials.content_path = Rails.root.join("config/credentials/#{ENV.fetch('CREDENTIALS_FILE')}.yml.enc")
    # key を RAILS_MASTER_KEY で指定せず file として読み込ませたい場合
    config.credentials.key_path = Rails.root.join("config/credentials/#{ENV.fetch('CREDENTIALS_FILE')}.key")
  end
end

もちろん file name を決める部分は環境変数である必要はなく独自のロジックを書いて決定しても問題ありません。

編集する場合は下記のようにします。

$ RAILS_MASTER_KEY=xxxxx EDITOR=vi bundle exec rails credentials:edit --environment hoge

ただし上記の場合若干罠があり file 名を dev や prod にしたい場合できないという問題が発生します。
dev にすると config/credentials/development.yml.enc が prod にすると config/credentials/production.yml.enc が編集されます。

これはなぜかというと下記のようなコードになっているからです。
environment で渡した値が env として渡ってくるのですが config/environments/ 内に該当の file が存在しない場合 production/development/test に前方一致で正規表現をかけられ match したものが採用されます。そのため dev を指定しても config/credentials/development.yml.enc が編集されるということになります。

github.com


ではこういう場合はどうするかというと下記のコマンドで編集が可能です。

$ RAILS_MASTER_KEY=xxxxxx EDITOR=vi  bundle exec rails encrypted:edit config/credentials/dev.yml.enc

最後に

config.credentials.content_path をいじったりしないでそのまま使えるのが一番ですよ

THE FIRST SLAM DUNK の感想

はじめに

ネタバレを含みます。

前提

筆者はスラムダンクを何度も読んでおり、バスケに関しては 20 年以上(現在も)プレイしています。

見る前の今回の映画の事前知識は下記

  • CG (これはちょっと残念だなと感じていた)
  • 声優が変わる
  • 山王戦っぽい(なんかの記事で得点差と赤木のフリースローで〜というのを観た)
  • 旧ルールで行われる(事前の動画でペイントエリアが台形なのが見て取れる)
  • 宮城リョータに焦点を合わせた話らしい

これらの知識を得ていて、もし新しい試合を描くなら(いるかは知らないが)スラムダンクを知らないで見る人々や子供達が混乱するだけだし
わざわざ旧ルールでやる必要ないし山王戦なんだろうな〜という気持ちでいた。

感想

CG

映画の入りが手書きのアニメーションから入るのですが、この手書きから CG に変わる演出がとても上手いなと思った。
この流れで CG に対する違和感がかなり減ったと感じた。

その後の試合開始後のミッチーの 3P が完全にミッチーのシュートフォームでスラムダンクじゃん!って一気にテンションが上がった。

CG に関していうとキャラがあまり動いていないシーンでは多少なりとも違和感はあるのだがバスケのプレイ中などのスピード感が出るシーンでは全く違和感がなくむしろこっちの方がいいと思えるくらいであった。

宮城の過去

原作ではほとんどキャラの掘り下げが行われていないので各キャラの掘り下げが行われるのはとてもありがたかった。

しかし、正直ちょっとくどいというか悲劇感出しすぎでは?という感じはあったし、長い気がして試合のテンポが悪く感じた部分はあった。
もし漫画を書いている時点でこの構想があったのであれば、(週刊連載というのもあり)試合のテンポを意識して漫画で入れなかったのか?だとしたら天才なのでは?という気持ちだがどうだったのかは知らん。

過去の湘北の話や実はミッチーとバスケをしていたという話などはとてもよかった。

声優

旧アニメもリアルタイムで見ていた世代ですが、違和感はほとんどなかったですね。
ただし、これは声優さんが悪いとかではなく完全に個人の好みレベルなのですが木村昴の声だけがずっと違和感を感じてしまった。
最近プレイしたスターオーシャン 6 の主人公のうちの 1 人を木村昴が担当しているのですが、こちらに関してもクリアしてもずっと声に違和感を感じたので筆者自身との相性が悪いのだと思う。

改変

(時間の都合上カットではなく)いくつか改変された部分もあって残念に思った点もありました。

  • 魚住の桂むきがなくなっていた
  • 他チームの会話が無い

赤木は魚住の助言ではなく、チームメイトによって対河田脳から脱するわけですがその流れもいいんだけど魚住の桂むき見たかったよな...
また、海南のメンバーの会話や諸星の「はっきりいって自信なし 」も聴きたかった。一言二言のセリフで声優のギャラとか渋ったのか?とか邪推した...

  • 「おい……見てるか谷沢」

初見は谷沢って誰やねんってなるだろうけど、これは削っちゃダメだろ〜

  • 「大好きです 今度は嘘じゃないっす」

この映画だけだとあの場面でこのセリフ言ってもマジで意味わからんって感じだろうからまぁ仕方ないっちゃ仕方ないが...

その他

漫画でのイメージだと山王戦あたりだと桜木は素人なのにバスケの動きをある程度理解していて素人感がなくなっていると思っていたけれど、動きがまだまだ素人くさい部分が映画では表されていてやっぱりリアルはそうだよなぁとなってよかった。

モップの掛け方間違ってるよね?

流川だけ回想とかの出番なくてあれ?ってなった(実際には桜木も少ないんだけどシュート練の回想とかが原作と同じで入るから...)
宮城メインだから仕方ない部分もあるが...

「死守」って言ってるシーンがあっさりしてて思いの外少ない

試合終了後の皆んなが抱き合ってるシーンの後に、宮城が母親と会話するシーン -> 山王の「負けたことがある」というのがいつか大きな財産になるの流れが最後に時系列がちぐはぐになってしまっていてもう少しどうにかならんかったのか?と思った。

ラスト観た直後は沢北はわかるけど、そっちはそうはならんやろって(原作のその後からも)思ったけど(時期とかを言っていたか記憶にないので)高校卒業後とかならあり得なくは無いか?と納得した

最後に

山王戦は何度も読んでるから試合内容とかに対しての新規性とかは無いのでそれに対しての感想はほとんどないんだけど全体的にマジでよかったですね。
動いている山王戦を見れるだけで嬉しいんだなって見てる途中で思った。

山王戦で一番好きなシーンの「オヤジの黄金時代は〜」
山王戦で一番好きなプレイの「行くぞ流川」からのもう体力残っていないミッチーが走っているのをちゃんと見てパスを出す宮城、そこからの 4 点プレイ(このプレイは漫画のコマ割りも含めてめっちゃ好き)

上記 2 つが動いている状態で見れてとても満足しました。

Rails version up 作業手順メモ

はじめに

最近副業で Rails version up 業を行っています。(6.0 -> 6.1 -> 7.0)
関わっている全てのサービスの 6.1 化が終わりいくつかのサービスの 7.0 化が終わっています。

前回の記事Ruby に関しての経緯は書いていますが Rails に関してもほぼ同じなので割愛

何度か登壇で Rails の version up に関して喋ったり、前職でテックブログを書いたりしているのですが作業手順の詳細を書いたことはなかったので良い機会だしまとめておこうという感じ。

ちなみに Rails は 3.2 から使い始めて 7.0 までの version の version up は全て対応したことがあります。

前提条件

前提として以下の状態であること。

  • spec が十分に存在する
    • 足りない分は version up の機会に追加して良いのであればこの限りではない
  • Rails 以外の gem は適度に update されている
    • update が 1 年以上止まっているとかの場合は先に gem の version up を全て終わらせてからの方が作業が進みやすいと思われます
  • Rails の version が使っている Ruby の version に対応していること
    • 対応していないなら先に Ruby をあげる
  • load_defaults と現在使っている Rails の version が揃っていること
    • そろっていないならまずは揃えるところから(最後の方に作業内容は軽く載っています)

実際の作業

0. 方針を決める

事前に決めておいた方がいいのは deprecation warning をどうするか?という点です。
リリース前に対応するのか?リリース後に対応するのか?次の version にあげる際に対応するのか?あたりになると思います。
チームで話し合って決めるのが良いと思います。

僕はリリース前に対応するのが好みです。

1. Release Note を見る

例えば Rails 7.0 の場合は下記のページ
guides.rubyonrails.org

release note を眺めてざっとどんな変更があったか?を脳内に記憶しておくと脳内に index が作られて後々エラーなどが発生した場合に原因の追求が楽になります。
あとは新機能が知れて便利

2. Rails の version を変更する

Gemfile で version を固定している場合はその部分の記述をあげようとしている version に変更します。
例えば下記のような感じ

- gem 'rails', '~> 6.1.0'
+ gem 'rails', '~> 7.0.0'

3. bundle update を行う

bundle update --conservative rails 

conservative option を使うことで他の gem の version が意図せず上がるというのを防げます。
これによってリリース時に問題が発生した際の原因特定がしやすくなります。

Bundler could not find compatible versions for gem "hoge":
  In snapshot (Gemfile.lock):
    hoge (= x.x.x)

実際には上記のような依存関係によるメッセージが出ることが多いので下記のようにエラーが出た gem を追加して行きます

bundle update --conservative rails hoge

ちなみにこの際に他の gem が "activerecord" < "7.0" などのように rails 関連の gem の version を制限していて update できない場合があります。(この場合 Rails 7 を使いたい場合に問題になる)
その際はその gem に PR を送るチャンスですので自信の fork した branch で gem の version 制限を変更して問題なく動作すれば PR を作成しましょう。
問題が起こる場合は問題を解消して PR を送りましょう。もしくは既に他の人が同様の PR を出している可能性もあるので PR の一覧を先に確認するのも良いと思います。

4. rails app:update を行う

rails app:update を実行することで config 内の設定を新しい Rails のものに置き換えを行うことができます。

$ bin/rails app:update
    conflict  config/boot.rb
Overwrite /rails/site/config/boot.rb? (enter "h" for help) [Ynaqdhm]

実際には上記のように confilict が発生するのでとりあえず d で diff をみてどうするか判断するなどしましょう。
僕はとりあえず a で全て置き換えて git diff で差分を眺めたりします。

5. rails が起動するか確認

まずは bundle exec rails c で rails console を立ち上げてみてまともに起動するか?というのをみます。(別に s で server 立ち上げでも問題ないです)
ここで立ち上がらない場合は表示されている error log から原因を探っていきます。原因に関しては個々の application や環境で異なってくるので対応方法は個別に判断しましょう

6. migration が全てうまくいくか?の確認

いったん開発環境の database を drop して migration や seed がちゃんと動くか?も確認しておくと良いでしょう。
CI で cache などを使って全 migration を実行していない場合は過去の migration や seed などは定期的に実行されることが無く、新しい人が入ってきたタイミングや PC 切り替え/開発環境が壊れたので作り直すなどの場合にしか実行することが無いので壊れていることに気づきにくいです。

また、db/schema.rb に変更がある場合もあるので実行しておくと良いでしょう。

7. CI を全て通す

6 の対応で CI 上で version up 後の Rails が動くようになるはずなのであとは落ちる test をひたすら修正していきます。

8. 不安であれば動作確認も行う

CI を全て通すことに成功した後にまだちゃんと動くか?というのが不安であれば開発環境にて動作確認を行うのも良いでしょう。
この際に最低限動作確認しておくべき機能リストなどを作成しチームで合意を取っておくと便利かもしれません。

9. version up 前の Rails でも問題ない変更のもしくは backport 可能なものの場合は事前にリリースを行う

こちら の記事に書いてあるような方法で行っていきます。
他にも rails 以外の gem も一緒に version up しないといけない場合に事前に version up 出来るようならそちらを先に対応しておくと良いでしょう。

メリットとしては下記のようなものがあります。

  • version up 時の Pull Request の差分を少なくできる
    • Rails の場合は変更行数が数百になることもよくある
    • レビュワーの負担軽減
  • version up 前に問題ないコードだという安心を得られる
  • version up 後にもし問題が起きた場合の原因特定が容易になる

10. version up の Pull Request を作成する

8 を行なったことで Pull Request の差分も最小のものになりレビューもスムーズに終わるはずです。

11. リリース

staging 環境などがある場合に動作確認をしたいなどのパターンはあると思いますのでチームの方針によって行なって貰いますが、残りはリリースするだけになります。

12. load_defaults を変更する

Rails の version 自体を上げて終わりでは無く Rails では load_defaults という設定がありこれと使っている Rails の version を揃えて初めて version up が完了します。

4 の rails app:update を行った段階で config/initializers/new_framework_defaults_x_y.rb という全てがコメントアウトされた file が生成されます。
これは以前使っていた version と今使っている version で設定が変更になるものが書いてあります。

一項目ずつ精査し反映とリリースを行った後に load_defaults を現在の version に揃えることで Rails の version up は終了になります。

最後に

Rails の version をあげる際に自分が行なっている作業を雑にまとめました。

7 ~ 10 の流れを少し補足すると僕は下記のような流れにすることが多いです。

1. とりあえず汚い commit でもいいので CI を通す
2. 通ったら commit を綺麗にした branch を用意する
3. 上記を眺めて version up 前の Rails でも問題ない変更を先にリリースする
4. version up の Pull Request を作成する

忘れてたことがあった場合は後で追記しておきます。

Ruby version up 作業手順メモ

はじめに

最近副業で Ruby version up 業を行いました。(2.7 -> 3.0 -> 3.1)
本業の方でも古い Ruby を使っており EOL が近づいたら version up を行うという方針だったのですが、
EOL が来たらあげるんじゃなくてこういうルールで上げていきましょうという日本語を書いて共有したらやっていくことになった。

それで、僕はもう version up 業ができるので本業の方でも僕が作業してもあまり得られるものがないので、
それならば別の誰かに作業して貰う方がその人の成長に寄与するのでは?と思ったので他の誰かに任せることにした。(予想以上に手を上げてくれる人がいた)

その為、これまで僕がどのように Ruby の version up 業を行なってきたか?をメモって公開することにした。(特に社内限定である意味がないので)

ちなみに Ruby は 1.9 から使い始めて 3.1 までの version の version up は全て対応したことがあります。

前提条件

前提として以下の状態であること。

  • Rails を使用した web application の Ruby の version をあげる際の話
    • gem の場合も大きくは外れないと思いますが
  • spec が十分に存在する
    • 足りない分は version up の機会に追加して良いのであればこの限りではない
  • Rails 以外の gem は適度に update されている
    • update が 1 年以上止まっているとかの場合は先に gem の version up を全て終わらせてからの方が作業が進みやすいと思われます
    • 結果的に Rails の upgrade を行う際も効率よく進められる

実際の作業

0. 方針を決める

事前に決めておいた方がいいのは deprecation warning をどうするか?という点です。
リリース前に対応するのか?リリース後に対応するのか?次の version にあげる際に対応するのか?あたりになると思います。
チームで話し合って決めるのが良いと思います。

僕はリリース前に対応するのが好みです。

1. Release Note を見る

例えば Ruby 3.0 の場合は下記のページ
www.ruby-lang.org

release note を眺めてざっとどんな変更があったか?を脳内に記憶しておくと脳内に index が作られて後々エラーなどが発生した場合に原因の追求が楽になります。

2. 開発環境の Ruby を version up 後の version にする

docker 上で動かしている場合は Dockerfile 等に書いてある Ruby の version を変えたり
local の PC 上で動かしている場合は rbenv install x.x.x や .ruby-version を変えて rbenv install したりしてあげようとしている version の Ruby を install します。

3. 必要な gem を入れる

Rails の application は Gemfile で管理されているはずなので bundle install を行うだけです。
この段階で gem によっては RUBY_VERSION を見ており install が失敗する可能性があります。その場合は該当の gem に対応 PR を送っておきましょう。

4. rails が起動するか確認

まずは bundle exec rails c で rails console を立ち上げてみてまともに起動するか?というのをみます。(別に s で server 立ち上げでも問題ないです)
ここで立ち上がらない場合は表示されている error log から原因を探っていきます。原因に関しては個々の application や環境で異なってくるので対応方法は個別に判断しましょう

5. CI で動いている Ruby version を変える

続いて CI を通すために CI 上で使っている Ruby の version を変更します。

GitHub Actions なら .github/workflows/*.yml
CircleCi なら .circleci/config.yml

上記あたりで使用している Ruby の image の version を変更しましょう。

僕が担当していたサービスでは CI 上で Ruby 2.7 を使っているつもりだったのに実は CI 上は Ruby 2.6 が動いていたという問題があったために下記のように Ruby version をチェックする spec を書いていたりしました。

describe RUBY_VERSION do
  it { expect(RUBY_VERSION).to eq('2.7.0') }
end

6. CI を全て通す

5 の対応で CI 上で version up 後の Ruby が動くようになるはずなのであとは落ちる test をひたすら修正していきます。

7. 不安であれば動作確認も行う

CI を全て通すことに成功したのちまだちゃんと動くか?というのが不安であれば開発環境にて動作確認を行うのも良いでしょう。

8. version up 前の Ruby でも問題ない変更の場合は事前にリリースを行う

例えば Ruby 3.0 では keyword arguments の非互換性な変更があります。
しかしこれらの変更に関しては Ruby 2.7 の段階で対応していても何ら問題ない場合がほとんどになるので事前に対応して Ruby2.7 の段階でリリースを済ませておくと良いでしょう。

メリットとしては下記のようなものがあります。

  • version up 時の Pull Request の差分を少なくできる
    • レビュワーの負担軽減
  • version up 前に問題ないコードだという安心を得られる
  • version up 後にもし問題が起きた場合の原因特定が容易になる

9. version up の Pull Request を作成する

8 を行なったことで Pull Request の差分も最小のものになりレビューもスムーズに終わるはずです。

10. 本番などの環境の Ruby を管理している file なども変更する

これは個々の環境よりますが、開発環境と同じ Dockerfile を使って動いている場合は変更しなくても 9 のものをリリースすれば勝手に変更になります。
本番などが別の Dockerfile なりプロビジョニングツールなりで管理されていた場合はそちらに変更の PR を送るなどを行なってください。

11. リリース

staging 環境などがある場合に動作確認をしたいなどのパターンはあると思いますのでチームの方針によって行なって貰いますが、残りはリリースするだけになります。

エラーが発生しやすいパターン

上記のように作業進めていく中でいくつかエラーが発生しやすいパターンがあるので軽く触れていきます

default gem になるなどして使用していた gem がなくなる

Ruby 本体に含まれなくなっただけで、実際には無くなっておらず Gemfile に追記して gem を引き続き使用することが可能です

非互換性な変更によるエラー

先で紹介した keyword arguments の変更など非互換性な変更によりエラーが発生することが稀に存在します。
Release Note を読んだりすればどのような変更を行えばエラーを解消できるか?は書いてあります。

gem が対応していない

Ruby 3.0 のように非互換性の変更がある場合は特に多いのですが、使っている gem が使いたい version の Ruby にまだ対応していないことがあります。
その場合はその gem に対応するための Pull Request を送ってみましょう。
既にその gem の更新が止まってしまっており Pull Request が取り込まれなさそうな場合は、代替えの gem を探す/fork したものをメンテする/独自に実装する/軽微なものなら monkey patch を当てる等の方法を取る必要があります。どの方針を選ぶかはチームで話し合って決めるのが良いでしょう。

最後に

Ruby の version をあげる際に自分が行なっている作業を雑にまとめました。
忘れてたことがあった場合は後で追記しておきます。

次回は Rails 編を書きます。

Rails に contribute する実績を解除した記念

はじめに

Rails を使い始めて 8 ~ 9 年くらい?経つのだけれど、なかなか機会が無く contribute 出来ていなかったがやっと出来たので記念に残しておく

内容について

github.com

こちらの PR なのですが、このバグを踏んだ経緯から説明します。

副業でお仕事をしているのですが、dalli の version up を行った時のことです。
dalli は v3.0 から大幅な変更が加えられており、独自で実装していた DalliStore という session store から Rails 標準の MemCacheStore 経由で Dalli::Client が呼ばれるという方式に変わりました。

PR をみてもらえればわかりますが、このバグは Dalli を v2.x から v3.x にあげたときに DaliStore(dalli v2.x) で memcached に格納されたデータを MemCacheStore 経由で read_multi を使って取得するときに発生するものになります。副業の方では read_multi で指定している key が 2 つしかなかったことから fetch で取得する方法に変更してこのバグを回避したのでお仕事としてはこれで終わりだったのですが、どうしてもこの解決方法に納得できず昼くらいに解決したけど夜までモヤモヤが残っていました。

モヤモヤの原因は Rails と Dalli どちらを修正するべきなのか?の判断が難しいものだったからで判断しかねていたので MemCacheStore の過去の PR で Dalli 関連のものってないのかな?と思い調べました。

それで辿り着いたのがこの PR でこの PR が取り込まれているということは Rails でもある程度 Dalli を利用した際の挙動について担保しておくということなのでこの PR を参考に test を書いて、該当箇所を修正したという感じ

最後に

最近は OSS 活動バグがあったら PR 出す程度しかしてないけれど Rails へ contribute できて嬉しかったので記念カキコ

最終出社

はじめに

本日 12/28 日が 6 年 6 ヶ月勤めた GMO ペパボの最終出社になります。とかいてますがこの記事を書いてるのは 27 日で公開したのは 28 日 0 時なのでまだ最終出社してません。1,2 月は有給消化になり 3 月から次の会社で働きます。

ペパボでの 6 年半の話

やったことなど

EC の API を開発するところから始まり 2 ヶ月ほどで minne に異動してきた後は、カオスだった API 開発に秩序をもたらしたり Solr から Elasticsearch への移行を行なった。その後検索周りの改善などもしていたら Apple Pay のローンチパートナーになったのでそれ関連の開発をしていた。

Apple Pay が落ち着いたあたりで EM に任命され EM 業をやりながらマイクロサービス化や k8s への移行、その他にもインフラとバックエンドを軸に様々な改善をしつつフロント周りも少し開発したりなど楽しく開発してきた。(正直多すぎて覚えていない...)
minne のメインとなるリポジトリの追加行数より削除行数の方が 1.4 倍くらい多かったのは割と気に入っている。

やってきたことはこのブログや SlideShare / Speaker Deck にも書いてあるので詳細はそちらを

おもしろイベントなど

(URL は載せませんが)企画でお見合いをしたりジョージアの web CM に出たりと色々なおもしろ体験をさせて貰った。
また、上でも書いたが Apple Pay のローンチパートナーになったおかげ(?)でちょっと特殊な状況で開発したり Apple の製品ページで minne の画面を継続的に使って貰えたりなどもあった。
普通に生きてたら(?)体験できないことだったので良かった。

評価など

(今は知りませんが)前職が新卒超優遇評価だったので、フラット且つオープンな評価制度で良かった。
入社する時は(誰にも言ってなかったが) 1 年でシニアエンジニアになれなかったら辞めるという強い気持ちで入社し、ありがたいことに評価していただき 1 年でシニアエンジニアになることができた。
その後も半年後には CTL となり、等級と名称が変わり SEL になるなどちゃんと評価されてきたと思う。数年前に評価制度が変わってからは(相対評価ではないものの)密かに SEL の中で一番いい評価を取り続けるんや!という気持ちを持ちながら仕事をしていたし実際に辞めるまでそういう評価を貰った。(バリューを出せばちゃんと評価してくれるので同じ職位の中で一番バリューを出すというような意味で)

今年 5 月くらいからはシニア・プリンシパルとして活動していた。

お金など

評価の項目にも書いた通り、割と順調に職位を上げてきたのと数年前に 200 万アップの件もあり入社当初から比べると倍以上になった。

総合的に

総合的な話をすると、ペパボで働いて正解だったなと思う。優秀で尊敬できるエンジニアも多いし、某ライバルのエンジニアとは同い年ということもあってか切磋琢磨してこれたのではないかなと思う。また既にやめてしまっているが同じ部署で CTL を一緒にやっていたエンジニアと一緒に仕事をしていた時期が僕は一番楽しかった + 色々な面で学びが多かったと感じれる時だった。

居るだけで成長できる環境をうたっていますが(今もうたっているっけ?)正確には居るだけで(皆に平等にチャンスがあり、それを掴みにいき成果を出せれば)成長できる環境があるっていうのが正解なのかな?ってのが個人的な気持ち。なのでちゃんとチャンスで手を挙げる勇気と覚悟を持て、それを放り出さずに周りの力も使いながら成果を出す自信がある人にはオススメかなって思います。


それじゃ、なんで辞めるんや?という話ですが特に面白い話でもないので直接聞いてくれれば答えます。

さいごに

はじめにも書きましたが、3 月から次の会社になります。どこか?は働き出して少ししたら書くと思います(が給料は 2 割くらい上がりますとだけ書いておきます)

1,2 月は有休消化で暇なので最後に例のリストを貼っておきます。

Amazon.co.jp