SimpleDelegatorでSimpleなDecoratorを作る

そういえば、 ModelであんまりViewに関わるロジックを使わないほうがきれいにコード書けていいですよ~みたいな話をしたのですが、 その時に標準ライブラリにあるSimpleDelegatorを使うといいですよねなんて話をしたのですが使い方を復習するなどしていました。

SimpleDelegatorってなんだっけ…

るびまによると…

オブジェクトの機能を再利用する手法の一つとして、Ruby では言語仕様としてクラスの継承とモジュールの Mix-in を提供しています。これらは、元になるクラスやモジュールの実装までもをそのまま取り込んでしまいますが、他の手段で機能の再利用を実現する手法として、委譲があります。

委譲では、再利用したい機能を自分に取り込むのではなく、その機能を持つオブジェクトに処理を依頼します。

Ruby では特に言語仕様として委譲がサポートされているわけではありませんが、委譲を実現するためのライブラリとして forwardable と delegate が用意されています。具体的には、これらのライブラリを使用することによって、あるメソッド呼び出しを他のオブジェクトのメソッドにたらい回すということを簡単に記述することができます。

という感じです。 普通だと委譲を実現するのにいちいちメソッドを定義しないと行けないのですが、 SimpleDelegatorを使うと追加したいメソッドだけを追加するだけでいいので便利。

標準添付ライブラリ紹介 【第 6 回】 委譲

SimpleDelegatorで作ったSimpleなDecorator

# 委譲させたいClass
class User
  attr_reader :firstname, :lastname
  def initialize(firstname:, lastname:)
    @firstname = firstname
    @lastname = lastname
  end
end

# Decorator
class UserDecorator < SimpleDelegator
  def fullname
    "#{firstname} #{lastname}"
  end
end

user = User.new(firstname: "西尾", lastname: "拓也")
u = UserDecorator.new(user)
p u.fullname

そんな感じで委譲でよく使われる鉄板用途としてDecoratorパターンがありますが、それをSimpleDelegatorで作ってみた例になります。

12. Decorator パターン | TECHSCORE(テックスコア)

これの使い道としては、自前で簡単にViewロジックを実装するような、いわゆるHelperやDecoratorが欲しい時、 ActiveRecordに依存しないようなDecoratorがほしいときはこれでサクッと作ってしまうのがいい。 もし、ActiveRecordに依存するDecoratorならActiveDecoratorを使うのでもいいのではないでしょうか。

まとめ

  • 標準ライブラリ便利なので、他にもこういった便利ライブラリがあるのでどしどし使っていきたい。

大規模サービス技術入門読んだ

まとまりないけど、感想を簡単に。

RDBMSの分割方法の話から、大規模サービスでパフォーマンス改善するためにどうアルゴリズムを生かしていくかや、大規模サービスのWebインフラはこんな感じでやってるぞーということが書いてあってよかった。 

普通なら退屈なアルゴリズムの部分も、実戦ではこう使えるし、アルゴリズムの知識だけではダメで、それを応用してくことが大事ーということだったり、ハードやソフトウェアの進化でお安く富豪的に解決できる事もあるから、時にはナイーブな実装試して検証して、割り切ることも大事だぞーと話しててなるほどとなった。 

この本でも特に多くページを割いていた印象があるのだけど、大規模サービスではやっぱりAppサーバーよりもDBサーバーの方が大変でやることが多いのかーとなっていた。でも、n+1について言及がなくて、はてなの場合はそこまで悩む事はなかったのかと。もしくは基本的なところなので外したか?

サーバーの仮想化についても、わかりやすく説明していて、仮想化してあるとリソースを余す事なく使えていいですねーとなった。(さらに発展させるとDockerに)

大規模サービスとか運用したことないし、今後もしそういったサービスを運用する機会があった時にそもそも経験したことがなくて、この辺がめちゃくちゃコンプレックスみたいな感じだったのだけど、この本で幾分知識をつけれたので今後のインフラやミドルウェア選定、アプリケーション実装に前よりは少しは自信持てるかなぁ。

出版されて8年くらい経っているのだけど、陳腐化してる技術もそこまでなくて、割とコンピュータのベースにあるような技術やわりと今でも応用が効きそうな大規模サービスでのパフォーマンス改善のヒントがたくさん書いてあるので、もし本屋などで見かけたら読んで見るといいかもしれない。

さあ、次は何を読もうか。

WebAPI The Good Partsを再読した

そういえば昔に読んだな、 とかおもってまた引っ張り出して読んでいた。

Web API: The Good Parts

Web API: The Good Parts

改めて学びがあったトピックは

  • バージョン番号をどこに入れるといいんだっけ?
  • ページネーションの仕様について(相対参照・絶対参照)
  • オーケストレーション層(実質BFFと解釈している)についての話が簡単に
  • パブリックなAPIとして運用していく色々なTips(レートリミットなど)

このあたり。

オーケストレーションは昔のRebuildでも話したそうだけど、(今度また聴いてみる) やはりNexflixすごいよなぁとなった。

あとはセキュリティの話があったんだけど、だいたいこれはAPIにかかわらない内容でなるほどーとなっていた。

全体的に、パブリックにエンジニアにAPIが公開を前提として話を進めているためか、 イケてないURL設計とかレスポンス内容ってダサいから、特別な理由がない限り利用者の印象良くないからやめようぜよく言ってた気がした。

公開APIとしての運用面のノウハウ、エラーレスポンスをこうするとわかりやすいみたい話はあんまりネットなくて良い感じにまとまっているので、 APIを始めて設計したりする時に参考になるところはいくつかあるのでちょっとおすすめしたい。

Docker HubのSource Repositoryを変更したい場合

Docker HubでGithubあたりの自動ビルドを有効にしている時に、Source RepositoryのRename時に参照するGithubリポジトリ変更したい場合がある。 この変更を忘れると、Github上でエイリアスを設定していてもDocker Hubには適用されず、自動でビルドされない状態になる。

ではどうすればいいか

直接設定を変更する解決方法はない。

取るとすれば下記の2つが有力。

1. 潔くDocker Hub上のリポジトリを削除して、再設定する。

削除されることが許容できればこれが一番手っ取り早い。 削除されることのデメリットは、Docker Hub上のStar数がリセットされるとかそのくらいだとはと思う。

2. Docker Cloudでビルド先を設定して、Docker Hubではなく、Docker Cloudでビルドする

この辺を参照してみてほしいんだけど、面倒だし、Docker Cloudという別のサービス使わないと行けないのがとにかく良くないので、 特にこだわりがなければ、この対応よりも、前者のほうが簡単でラクでいいんでは。と思っている。

No way to change source project for automated build · Issue #313 · docker/hub-feedback · GitHub

なんでこれ調べていたか

ngx_mrubyのDockerイメージのベースイメージを新しいものに変更して、 特に問題なくMergeしてもらったのだけど、この時に1年くらい前から自動ビルドがされてないですねー。ということがわかって少し調べていた。

1年くらい前にタイミングでngx_mrubyはAuthor名が変わって、Github上のリポジトリ名が変更されていたのが原因だったらしい。

今回は面倒なので、2.を提案して対応していただいた。(その節はありがとうございました!🙏)

あまり日の目を見ないPassengerにスポットを当てる

Phusion Passengerとは

非公式にはmod_railsとmod_rackとも呼ばれている。 役割としても、Apacheにインストールして、そのまま動かす当たりはやはり、mod_phpとかと被ってる。

イマドキだと、PumaやUnicornRubyのAppサーバーとしての主流だけど、こっちは「nginx + php-fpm」に近いイメージ。

今だからというのはあるけど、PassengerはDockerが無かったりした時代ではある程度メジャーな手法だったんだろうし、何より当時としては簡単なインストール方法だったのだ。

メリット

ApacheやNginxのモジュールとして稼働する

これのメリットとしては、特別にRailsアプリケーション用のコマンドを用意する必要がない、 すなわちsudo service apache2 (nginx) restartでだけで再起動できるし、色々できるというわけだ。

複数アプリケーションを立てたときのメモリの有効活用

これは具体的に言うと、Passengerでは一定期間リクエストがないインスタンスに関しては終了させ、 またリクエストがあったときにインスタンスを生成するという仕組みを取っているので、トラフィックが少ないサイトを沢山まとめて1つのサーバーで管理したい時に重宝する。 この終了までの期間に関してはPassengerの設定で変更することも可能である。

定期的な終了による小規模アプリケーションの安定稼働

Unicorn当たりでよくあるパターンは、ActiveRecordなどのCacheをどんどん溜めてしまってメモリが溜まってしまうというものだ。 Pumaは本番で余り触ったことがないので、よくわからないのだが、Unicornの場合は、unicorn-woker-killer によって、 一定回数のリクエストや、プロセス単体のメモリ容量によってプロセスの再起動を行う対策を取るのが一般的ではある。

Passengerではリクエストが無いときにはインスタンスを終了させるので、小規模なアプリケーションの場合は定期的に再起動を行っていることになり、 メモリ解放について気を使うことは、小規模なアプリケーションに限って言えば他のアプリケーションサーバーに比べると少ないのかもしれない。

デメリット

Apacheに同梱されているので、静的ファイルを返すときでもApache + Passengerが動く

結構痛い。 なるべく余計な処理をさせないようにするには、 静的ファイルを返すときにはアプリケーションサーバー(Passenger)は動いてほしくないものである。 静的ファイルが多めのサイトを運用する場合は避けたほうがいいかもしれない。

一応対策もあるが、それは別途Nginxを立ててReverseProxyする方法。 静的ファイルはNginxから、動的に処理しないといけないところをApacheでという方法。 でもそれだったら、Unicornとか、Puma使えば良くないですかということになる…

ビジネス向けでないとDeploy時・再起動時にダウンタイムが発生する

コレはPhusion Passengerの機能制限。有料のビジネス版を利用しないとリスタート時にダウンタイムが発生する。 すなわちgraceful startができない。 どのくらいダウンタイムが発生して、どのくらいの時間かは未検証だけど、止まるということは頭に入れておいてほしい。

どういうときに利用するのか

止むにやまれない事情で、少メモリ環境で、複数アプリケーションを立ち上げる案件

インフラの費用がなかったり、管理上1つのVMインスタンスで管理したいという時。あんまりないねー。

小規模なRedmineを自前で立てる必要がある場合

いや、もうDocker使えよ…という話なんだろうけど、複数のRedimineアプリケーションを1つのサーバーに立てる時に何かと重宝する。 あと、単純に資料が多いというの公式のインストールマニュアルがあるしね。

積極的に使う理由が余り見当たらない…

普通にアプリケーション作っているんだったら、やっぱり、PumaかUnicornが無難。 元々Passenger使っていたとか、複数のRailsアプリケーションを1つのサーバーに同居させて管理する場合のみ、Passengerを使うべきであるというのが自分の出した答えである。