元WEBディレクターのプログラミング奮闘日記

元WEBディレクター、今はなんちゃってエンジニア。思っていること、ハマったところ、なんでも書いていきます。

Google Cloud Next 2018 Extended App & Infra Day に参加してきた

掲題のイベントに参加してきましたのでまとめ。

gcpug-tokyo.connpass.com

Firestore :Datastore Mode & Datastore Automatic Upgrade to Firestore by sinmetal

資料自体は公開されているので、面白かった点や感じたことをつらつらと書いてみます。

本題はFirestoreについてなんですが、Firestoreの話にいくまでにgoogleが今まで提供してきた BigTable, datastore, Spanner について時系列で歴史が語られており、その話が非常に面白かった。

個人的な印象としてgoogleのデータベースの歴史はKVSの進化の歴史なんだなぁと感じた。 割とプレーンなKVSとしてBigTableが存在し、そこから便利な機能を付け加えていって結果としてRDBっぽい機能を兼ね備えたSpannerが出来上がった、という流れ。

そしてそのSpannerをバックエンドとして、datastoreの互換を持ったサービスがFirestore datastore、ということらしい。(昔はBigTableが後ろにいた) Spannerは基本的には数TB以上のデータを格納しないとあまり旨味がないデータベースで(まあ最低限の1ノードでも8万/月くらいかかるしね。。。)そのSpannerのインスタンスを裏で共有してdatastoreの機能を提供するって感じっぽい。

最近Spannerと戯れることが多く、非常に好きなプロダクトなのだが、価格的にもスモールスタートには向かないよなぁと思っていたので、Firestore datastoreには非常に注目している。(別に従量課金のSpanner、というわけではないが)

Firestore Native Modeについての話は私がmbaas(というかNative App)に馴染みがなさすぎてあまり消化できなかった。

Application and Infra by kuma

GCP Next報告会ということもあって、当日の様子のレポートが冒頭にあり、後半はマイクロサービスの話だった。

NextのセッションはほぼYoutubeで公開されているとのこと。 www.youtube.com

kumaさんの発表で考えさせられたのは、マイクロサービスやkubernetes、istioなどの新しく流行っている技術に飛びつくのではなく 費用対効果と、それに適応できるチーム・組織かどうかを考える必要がある、と言っていた点。

もちろん技術は適材適所ではあるので、費用対効果は考えなければならない。(話の中でも個人用ブログにkubernetesは使わないよね、って例があった) が、それとは別に、そもそもこの辺のインフラの技術やNextで発表されたgoogle services platformはSREの考え方が前提になってたりするのもあるので ただ導入するだけだとチームや組織が適用できないケースがあるよ、って話だった。

次はSpanner Dayでインターリーブについて発表してきます。

gcpug-tokyo.connpass.com

それでは。

railsでネストされた小モデルに対してバリデーションしようと思った時に苦しんだお話

掲題のようなことをやろうとして大いにハマったので、解決方法を書いておく(ちなみに、やりたいことは実現できたというだけなので、こうすると楽だよ、的なご指摘あればコメントしていただけると大変嬉しいです。)

まず今回やりたかったこと(機能的には投稿の一時保存的なものをつくろうとしていました)

accepts_nested_attributes_for :fugafugaのようにネストされた小モデルのバリデーションをしたい

・あるアクション(僕の場合は一時保存アクション)から呼ばれた場合のみ、presence: trueだけ無視したい(その他にもlengthのチェックなどをしている)

1. on: :hogehoge の逆が簡単にできない

特定のアクションの場合のみバリデーションをしたいときは on: :hogehoge のようにvalidation_cotextをonオプションで指定してやってやるのが便利なんだけど、今回は特定のアクション以外の場合という条件分岐をしたかったので結構手こずった。

一時保存以外のアクションの場合(今回はcreateとupdate)にonオプションをつけても実現できるのだが、

with_options on: [:create, :update] do |hogehoge|
  hogehoge.validates :fuga, presence: true
end

上記のようにしてしまうと、今後このモデルを扱うアクションが増えた時なんかにメンテナンスがしににくなりそう(というか本質的にやりたいことではないのでなんか気持ち悪い)なので、なんとか一時保存のアクションでなければという条件分岐を作ってやりたかった。

そしたらちょうど同じことで悩んでいる方の記事を発見

http://labs.timedia.co.jp/2014/07/rails-validation-not-on-context.html

こちらにならい、ブロックをunlessに渡してやることにより一旦この問題は解決

with_options unless: -> { [:draft].include?(validation_context) } do |offer|
  offer.validates :position, presence: true
end

こんな感じ

2. ネストされた小モデルにvalidation_contextが渡らない

親モデルは上の方法でうまくいったのだが、accepts_nested_attributes_for :fugafugaのようにネストされた小モデルに対してはうまく行かなかった。 いろいろ調べてみると、どうやらvalidation_contextがうまく渡っていないご様子。一時保存のアクションからmodel.saveをしても親モデルには渡っているが、ネストされた小モデルのvalidation_contextにはupdate or createが渡ってしまっていた。

どのようにcontextを渡しているのかを調べてみると、以下のようにcontextがnilの場合は、新規レコードかどうかを見てcreateかupdateをつっこんでいるらしい。

#activerecord/lib/active_record/validations.rb

def valid?(context = nil)
  context ||= (new_record? ? :create : :update)
  output = super(context)
  errors.empty? && output
end

親モデルに対してcontextを指定してあげてもaccepts_nested_attributes_forでネストした子モデルにはnilが渡っているっぽい。

結構いろいろ試したんだけど、どうも子モデルのバリデーションにcontextを渡すことが出来ず。

以下のように親モデルの中で無理やりバリデーションさせることで対応した。

with_options unless: -> { [:draft].include?(validation_context) } do |offer|
  offer.validates :position, presence: true
  # 追加部分
  offer.validates :fugafuga_name, presence: true
end

private 
def fugafuga_name
  fugafuga.name
end

うーん、一応やりたいことは実現できたのだが、、、

そもそもRailsが用意していないことをやろうとしている時点で、何かがおかしいですかね、、、