Upgrade to Pro — share decks privately, control downloads, hide ads and more …

アクティレコードだけで戦うには、この世界は複雑過ぎる #ginzarails

アクティレコードだけで戦うには、この世界は複雑過ぎる #ginzarails

銀座Rails #2 slide

Tomohiro Hashidate

October 17, 2018
Tweet

More Decks by Tomohiro Hashidate

Other Decks in Technology

Transcript

  1. Example c l a s s S e s s

    i o n M e s s a g e c l a s s < < s e l f d e f d e q u e ( & b l o c k ) p o l l e r . p o l l ( s k i p _ d e l e t e : t r u e ) d o | m s g | d a t a = O j . l o a d ( m s g . b o d y ) b e g i n y i e l d n e w ( d a t a ) p o l l e r . d e l e t e _ m e s s a g e ( m s g ) r e s c u e = > e R a i l s . l o g g e r . w a r n ( e ) e n d e n d e n d p r i v a t e d e f p o l l e r # o m i t e n d e n d e n d
  2. Struct JSON ­> Hash の一歩先へ k e y w o

    r d _ i n i t : t r u e でめっちゃ使い易くなった Rails に依存しない 処理が軽い C レベルで定義が完了するため、普通にクラス定義するよ り1.2 から1.5 倍ぐらい早い バリデーションやデフォルト値の定義先を明確にできる 単体テストが簡単
  3. ActiveModel::Attributes ついにRails 公式で、ActiveModel にリッチアトリビュートが 型変換 デフォルト値 (proc 対応) カスタムタイプキャスター対応 値オブジェクトとの親和性

    同一性の検証 透過的なシリアライズ/ デシリアライズ 雑にJSON からマッピングしても、型やデフォルト値を上手くマッ ピングしてくれる ちなみにRails5.2 より前に、自分でタイプキャストしてStruct にマ ッピングしているコードが結構ある
  4. 中間まとめ モデルといってもデータソースは様々 SQS とかKinesis とかKafka とかだったり シリアライズされたものからオブジェクトにマッピングする 複雑な構造のデータは単純にHash に割り当てずに、ちゃんと クラスを定義する

    タイプキャストをネストすれば、入れ子構造にも対応できる 複雑なモデルはツリー構造になる オブジェクトマッピングの処理とドメインロジックをごっち ゃにしない データソースとのやり取り、値変換、バリデーション、メイ ンロジックが独立してテストできる様に、クラスやメソッド を分割する
  5. select をちゃんと使う AR のメソッドだけでもある程度の集計をSQL にやらせて結果を受 け取ることができる。 r e s u

    l t = U s e r . l e f t _ o u t e r _ j o i n s ( p o s t s : : r a t e s ) . s e l e c t ( " C O U N T ( D I S T I N C T r a t e s . u s e r _ i d ) A S r a t e d _ u s e r s " , " I F ( r a t e s . u s e r _ i d I S N U L L , 0 , M I N ( r a t e s . r e v i e w ) ) A S m i n _ r e v i e w " , " M A X ( r a t e s . r e v i e w ) A S m a x _ r e v i e w " , " A V G ( r a t e s . r e v i e w ) A S a v g _ r e v i e w " , ) . g r o u p ( : i d ) . t a k e ! p r e s u l t . c l a s s # = > U s e r p r e s u l t . r a t e d _ u s e r s # = > レーティングした人の数 ただ、簡易なものに留めておくのが無難。 そして、出来るだけSQL そのものの構造に似せて書いた方が後々 剥がす時に楽。
  6. find_by_sql でのINLINE 埋め込み r e s u l t =

    U s e r . f i n d _ b y _ s q l ( [ < < ~ S Q L , a u t h o r i z e d : 1 ] ) S E L E C T C O U N T ( D I S T I N C T C A S E W H E N r a t e s . a u t h o r i z e d = : a u t h o r i z e d T H E N r a t e s . u s e r _ i d E L S E N U L L E N D ) A S r a t e d _ u s e r s , M I N ( r a t e s . r e v i e w ) A S m i n _ r e v i e w , M A X ( r a t e s . r e v i e w ) A S m a x _ r e v i e w , A V G ( r a t e s . r e v i e w ) A S a v g _ r e v i e w F R O M u s e r s L E F T O U T E R J O I N p o s t s O N u s e r s . i d = p o s t s . u s e r _ i d L E F T O U T E R J O I N r a t e s O N p o s t s . i d = r a t e s . p o s t _ i d S Q L
  7. execute with ERB I N S E R T I

    N T O a g g r e g a t e d _ s e s s i o n s S E L E C T D I S T I N C T i n s i g h t _ i d , u n i t , s t a r t e d _ a t , c u s t o m _ e v e n t _ i d , S U M ( C A S E W H E N p l a t f o r m I N ( ' i o s ' , ' a n d r o i d ' , ' w e b ' ) T H E N , S U M ( C A S E W H E N p l a t f o r m = ' i o s ' T H E N f r e q u e n c y E L S E 0 E N D , S U M ( C A S E W H E N p l a t f o r m = ' a n d r o i d ' T H E N f r e q u e n c y E L S E , S U M ( C A S E W H E N p l a t f o r m = ' w e b ' T H E N f r e q u e n c y E L S E 0 E N D F R O M ` a g g r e g a t e d _ s e s s i o n s _ < % = u n i t % > ` W H E R E d t > = ' < % = f r o m . s t r f t i m e ( " % Y % m % d " ) % > ' A N D d t < ' < % = t o . s t r f t i m e ( " % Y % m % d " ) % > ' A N D f i r s t _ a c c e s s = f a l s e G R O U P B Y i n s i g h t _ i d , u n i t , c o n v e r s i o n _ s t a r t e d _ a t , c u s t o m _ e v e n t _ i d
  8. c l a s s Q u e r y

    R e n d e r e r < O p e n S t r u c t d e f s e l f . r e n d e r ( t e m p l a t e _ f i l e , * * v a r i a b l e s ) n e w ( t e m p l a t e _ f i l e : t e m p l a t e _ f i l e , * * v a r i a b l e s ) . r e n d e r e n d d e f r e n d e r E R B . n e w ( F i l e . r e a d ( t e m p l a t e _ f i l e ) , n i l , " ­ " ) . r e s u l t ( b i n d i n g ) e n d e n d r e s u l t = A c t i v e R e c o r d : : B a s e . c o n n e c t i o n . e x e c u t e ( Q u e r y R e n d e r e r . r e n d e r ( t e m p l a t e _ f i l e , { u n i t : " d a y " , f r o m : 1 . d a y . a g o , t o : T i m e . c u r r e n t } ) , )
  9. rukawa の活用 https://github.com/joker1007/rukawa Rails アプリから成長してきたので、いくつかのデータやロジ ックをRails のapp/models から参照している Ruby で直接コントロールできて、Rails

    をそのまま読み込める rukawa を作ったのはそういう理由に依る ヘルパーメソッドを定義して、集計時刻等のパラメーターを 渡し易くしている 大体の処理はテンプレートを元にクエリをレンダリングして Bigquery やHive/Presto 等にリクエストをする
  10. c l a s s W o r k f

    l o w S a m p l e < R u k a w a : : J o b N e t d e f s e l f . d e p e n d e n c i e s { A g g r e g a t e d C l i p s D a y = > [ ] , C o n v e r s i o n F a c t s D a y = > [ A g g r e g a t e d C l i p s D a y ] , E x p o r t C o n v e r s i o n F a c t s D a y = > [ C o n v e r s i o n F a c t s D a y ] , C o p y C o n v e r s i o n F a c t s D a y = > [ E x p o r t C o n v e r s i o n F a c t s D a y ] , I m p o r t C o n v e r s i o n F a c t s D a y = > [ C o p y C o n v e r s i o n F a c t s D a y ] , R e t e n t i o n F a c t s D a y = > [ A g g r e g a t e d C l i p s D a y , C o n v e r s i o n F a c t s D a y ] , E x p o r t R e t e n t i o n F a c t s D a y = > [ R e t e n t i o n F a c t s D a y ] , C o p y R e t e n t i o n F a c t s D a y = > [ E x p o r t R e t e n t i o n F a c t s D a y ] , I m p o r t R e t e n t i o n F a c t s D a y = > [ C o p y R e t e n t i o n F a c t s D a y ] , } e n d e n d
  11. Amazon Fargate EC2 インスタンス無しで、コンテナ上で処理を実行できる 必要なタイミングでECS のAPI を叩いて、特定のイメージでコ マンドを実行するジョブをrukawa で定義する コマンド引数や環境変数、S3

    等を利用してデータを引き 渡す セキュリティはTask Role でコントロールする クリーンで運用負荷の無いジョブ実行環境が、Fargate の制限 に到達するまではいくつでも並行に用意できる アプリイメージの外にembulk やrclone やhive クライアント等の コマンドラインツールを用意して独立して管理できる
  12. wrapbox https://github.com/reproio/wrapbox Ruby からECS のAPI を叩いてコンテナ上でコマンドを実行して、 終了を待ち受けるgem hako oneshot みたいなもの

    元々はECS 上でRSpec を実行するために作ったが、Fargate が日本 に来たタイミングでバッチ処理と相性が良さそうだったため、改 修してFargate 対応した rukawa と組み合わせることで、並列度の高いバッチワークフロー 実行環境が低価格で手に入った