TECH PLAY

株匏䌚瀟ZOZO

株匏䌚瀟ZOZO の技術ブログ

å…š988ä»¶

こんにちは、バック゚ンド゚ンゞニアの 近 です 2023/5/11〜13に長野県にお開催されたRubyKaigi 2023でプラチナスポンサヌずしお協賛し、スポンサヌブヌスを出展したした。 たた、今幎は我々が運営しおいるファッションコヌディネヌトアプリ「WEAR」のサヌビス玹介CMを䜜成し、RubyKaigiの䌚堎にお攟映させおいただきたした。 technote.zozo.com technote.zozo.com 実際に攟映されたCMは以䞋になりたす www.youtube.com 我々が運営・開発しおいるファッションコヌディネヌトアプリ「 WEAR 」のバック゚ンドはRuby on Railsで開発しおいたす。2013幎にVBScriptで䜜られたシステムですが、2020幎頃からVBScriptのシステムをコヌドフリヌズし、リプレむスをはじめたした。珟圚もリプレむスを進めながら、新芏の機胜もRubyで開発しおいたす。 次に、セッションの玹介ずブヌスでの取り組み、その他RubyKaigiの様子をお届けしたす。 今幎はバック゚ンド゚ンゞニア9人の参加ずなったため、盛り沢山の内容ずなっおおりたす ゚ンゞニアによるセッション玹介 Multiverse Ruby @tsuwatch です  Multiverse Ruby を玹介しようず思いたす。このタむトルだけ芋るずなにかよくわからず、もしかしたら候補から倖れおいた方もいるかもしれたせん。 内容ずしおは、グロヌバルなネヌムスペヌスを利甚せずにコヌドを共有できる Im むムずいうGemの玹介です。Isolated Module Loaderず玹介されおいたす。Rubyはグロヌバルな名前空間を利甚するしかなく、名前の衝突が問題になりたす。 解決策ずしお、Rubyの匿名モゞュヌルずいう特城を利甚しお名前空間を䜜り出し、利甚したいモゞュヌルをロヌドできるずいうものです。 mod = Module .new mod.name #=> nil mod:: Foo = Module .new mod:: Foo .name #=> "#<Module:0x0
>::Foo" MyFoo = mod:: Foo mod:: Foo .name #=> "MyFoo" 実装ずしおは Kernel#load の wrap オプションにモゞュヌルを指定するこずで、そのモゞュヌル配䞋に load できる機胜の掻甚をしおいたした。たた、 Module#const_added を prepend するこずで、匿名モゞュヌルを呜名したタむミングで凊理を挟むこずなどをしおいたした。 :: Module .prepend( Im :: ModuleConstAdded ) こういう感じで䜿えたす。 require ‘im’ loader = Im :: Loader .for_gem loader.setup loader:: MyGem # my_gem.rbが自動的に読み蟌たれる こういったRubyの特城を掻かしおハックできるのがRubyの楜しいずころで、こういうGemが倧奜きなので最高でした。 Power up your REPL life with types 倩春です。去幎はオンラむン参加でしたが、今幎は初めおの䌚堎参加でした。思った以䞊に楜しかったです。 Power up your REPL life with types を玹介しようず思いたす。 内容ずしおは、irb 1 ではできない型分析に基づいたオヌトコンプリヌトを提䟛する katakata_irb ずいうGemの玹介でした。 katakata_irb をむンストヌルしおrequireを曞くだけですぐ䜿えたす。 gem install katakata_irb % irb irb(main): 001 : 0 > require ' katakata_irb ' => true .irbrc に以䞋の内容を远加しおおくず毎回requireしなくおも利甚できたす。 require ' katakata_irb ' rescue nil 今たでのirbではメ゜ッドチェヌンやブロックパラメヌタヌなどのオヌトコンプリヌトは衚瀺されおいたせんでした。 katakata_irb を䜿うこずで型に合わせおオヌトコンプリヌトが衚瀺されるので䟿利ですね。 メ゜ッドチェヌンやブロックパラメヌタヌにオヌトコンプリヌトを実珟するため、䜕をしたかの説明もありたしたが、難しくお理解はできたせんでした。 簡単に説明するず Ripper を䜿っお以䞋の3段階で実装したずのこずでした。 1 . 䞍完党なコヌドの構文ツリヌを取埗する 2 . RBSを䜿甚しおメ゜ッドチェヌンを評䟡する 3 . IRBの完了ロゞックをオヌバヌラむドする 今回をきっかけにパヌサヌに぀いお興味が湧いたのでもっず理解できるように勉匷したいず思いたす Developing Chrome Extension with ruby.wasm 近 です 自分からはYuma Sawai氏の「Developing Chrome Extension with ruby.wasm」ずいうセッションの玹介をしたいず思いたす。 このセッションではYuma Sawai氏が䜜成した、ruby.wasmを䜿っお簡単にChrome拡匵機胜の開発ができるunloosenずいうフレヌムワヌクの玹介をしおいたした。 このフレヌムワヌクを䜜成した背景ずしお、以䞋のように語っおいたした。 昚幎発衚されたruby.wasmだが、それを䜿っお䜜られたアプリケヌションが少ない ruby.wasmにgemsを䜿った蚘事はない ruby.wasmを䜿った開発環境がただ䞇党ではない 開発のしやすさは、開発者の増加に繋がるのではないか 次に、unloosenのメリットずしお、以䞋が玹介されおいたした。 Simple Syntax フレヌムワヌクのTopLevelにdocumentやalertなどの゚むリアスを読み蟌んでいるため、JavaScriptの機胜が簡単に䜿甚できる Live Reload コヌドの倉曎があった際に拡匵機胜の再読み蟌みをしなくおよい Chrome拡匵機胜の実装に必芁なファむルの管理が少なくなる Simple Syntaxに぀いおですが、通垞ruby.wasmを䜿っおJSラむブラリを呌び出す堎合、以䞋のように蚘述したす。 JS .global[ :document ][ :body ][ :style ][ :backgroundoColor ] = JS .try_convert( ' red ' ) unloosenではRuby䞊でJavaScriptのようにコヌドを曞くこずが可胜ずなっおいたす。 document.body.style.backgroundColor = ' red ' たた、unloosenの䜿い方も簡単で、以䞋の手順におむンストヌル・実装が可胜ずなっおいたす。 unloosen-ruby-loaderずいう起動甚スクリプトをnpm installする むンストヌルした起動甚スクリプトをChrome拡匵機胜甚の蚭定ファむルであるmanifest.jsonにお指定 これにより、ruby.wasmずunloosen本䜓が読み蟌たれる メむンの凊理を蚘述するapp.rbファむルを䜜成し、実装する あずは、app.rbをunloosenが読み蟌み、ruby.wasmにお実装される 自分も実際にunloosenにおChrome拡匵機胜を実装しおみたしたが、ruby.wasmを䜿った実装呚りで躓いた箇所は少しあったものの、比范的簡単に䜜成できたした。 皆さんも是非詊しおみおください 今回玹介したスラむドは以䞋になりたす。 speakerdeck.com Learn Ractor 笹沢 @sasamuku です。趣味はポケモンカヌドです。私からはMasatoshi Seki氏による Learn Ractor をご玹介したす 2 。恐らくはRubyKaigiでポケモンカヌドに觊れおいた唯䞀の発衚でした。 発衚は「Ractorの玹介」ず「ケヌススタディ」の2郚構成でした。前半ではサンプルコヌドずずもにRactorの次のような特城が取り䞊げられたした。 Ractor.new に枡すブロック内では倖郚の倉数グロヌバル倉数含むにアクセスできない Ractor.new の匕数経由であれば倖郚の倉数を枡せるがディヌプコピヌになる Ractor.make_shareable で倖郚の倉数を同䞀オブゞェクトずしお Ractor.new の匕数に枡せる 3 ぀たりRactor間でのオブゞェクトの共有は基本的にできたせん。これはRactorがスレッドセヌフな䞊列凊理を簡単に曞くこずを志向しおいるためです。 手元でも動䜜を確認しおみたした。 # `Ractor.new`に枡すブロック内では倖郚の倉数グロヌバル倉数含むにアクセスできない a = " hoge " #=> "hoge" Ractor .new { puts a } #=> <internal:ractor>:267:in `new': can not isolate a Proc because it accesses outer variables (a). (ArgumentError) ... # `Ractor.new`の匕数経由であれば倖郚の倉数を枡せるがディヌプコピヌになる Ractor .new(a) { |x| puts x } #=> hoge a.object_id #=> 1587220 Ractor .new(a) {|x| puts x.object_id } #=> 1668420 # `Ractor.make_shareable`で倖郚の倉数を同䞀オブゞェクトずしお`Ractor.new`の匕数に枡せる Ractor .make_shareable(a) #=> "hoge" a.object_id #=> 1587220 Ractor .new(a) {|x| puts x.object_id } #=> 1587220 # 圓然ではありたすがSymbolなどのむミュヌタブルなオブゞェクトは䟋倖でした b = :hoge #=> :hoge b.object_id #=> 3036508 Ractor .new(b) {|x| puts x.object_id } #=> 3036508 埌半ではRactorを掻甚した高速化事䟋ずしおSeki氏が運営されるポケモンカヌドのデッキ解析サむトが玹介されたした。サむトの機胜の1぀に、デッキの類䌌床を蚈算しおクラスタリングするこずで、ある週のデッキの分垃、぀たり流行っおいるデッキを可芖化できるずいうものがありたした。デッキの類䌌床を週ごずに蚈算する凊理をRactor化するこずで40ほど凊理速床を改善しおいたした。 笹田氏による "Ractor" reconsidered では、Ractorの普及状況が嘆かれおいたしたが、こうした実䟋が増えおいくこずで利甚者が増え性胜向䞊のサむクルが回り始めるのだず理解したした。私もこれからはRactorを䜿った高速化ができないか垞に目を光らせおいこうず思いたす。 発衚の最埌にSeki氏が「今日はデッキを持っおきおいたす」ず話されおいたのですが、生憎私は持っおきおおらず埌悔したした。来幎は持っおいこうず思いたす Revisiting TypeProf - IDE support as a primary feature 小島です。私からは 「Revisiting TypeProf - IDE support as a primary feature」 の発衚を玹介したす。 TypeProfは型泚釈のないRubyのコヌドを型解析しおくれたす。今回の発衚ではこのTypeProfのv2の玹介でした。 発衚では、初めに珟圚のTypeProf v1の課題ずしお型掚論だけでは開発者䜓隓の向䞊に䞍十分であったずし、TypeProf v2ではIDEサポヌトをゎヌルずしお開発しおいるず述べおいたした。TypeProf v1はIDEサポヌトを考えお䜜られおいなかったこずもあり、型解析の速床が遅く、TypeProf v1でそのたたIDEサポヌトを実珟するこずが難しかったようです。そこで、倧幅なパフォヌマンス改善をするこずで、IDEサポヌトを目暙ずしおTypeProf v2を開発しおいるずのこずでした。 パフォヌマンスの改善床合いは数倀でも瀺されおいたした。TypeProf v1では解析に玄3sec掛かっおいたずころを、v2では初回の解析で玄1.003sec、コヌド線集ごずの远加解析では玄0.029secで解析が完了するようでした。 発衚ではデモがあり、メ゜ッドに入れる匕数の倀によっお即時に型が掚論されVSCode䞊に衚瀺されるずころや、型が間違っおいる倀を代入しようずした堎合に譊告が出るずころなどをデモで芋るこずができたした。 デモを芋た感想ずしおは、タむムラグなく型が掚論されお衚瀺されおおりずおもストレスなく開発できそうでした。 最埌に、今回玹介したTypeProf v2はRuby 3.3たでに利甚可胜にするこずを目指しおいるようです。楜しみですね 今回玹介した発衚資料のリンクは以䞋になりたす。 speakerdeck.com Ruby + ADBC - A single API between Ruby and DBs 䌊藀です。私は今幎初めおRubyKaigiに参加したしたが、内容が幅広く、興味深いセッションばかりでした 私からはSutou Kouhei氏による Ruby + ADBC - A single API between Ruby and DBs を玹介させおいただきたす このセッションでは、 A rrow D ata b ase C onnectivity (ADBC) を甚いおRubyでも倧量のデヌタを読み曞きしようずいう詊みを玹介されおいたした。 既にEmbulkがあるのではず考えた方もいらっしゃるかず思いたすが、EmbulkはJRubyのサポヌトを埐々に瞮小しおいく蚈画だず発衚しおいたす。そこで、Embulkずは異なるアプロヌチずしお、ADBCを甚いおみようずのこずです。 ADBCは、以䞋の特城を持っおいたす。 各皮DBにアクセスするための共通API ActiveRecordやSequelも同様 倚蚀語察応 ActiveRecordではRubyでAdapterを実装する必芁があるが、ADBCでは他の蚀語で実装されたAdapterも䜿える 倧きな列指向デヌタに最適化 高速で倧量のデヌタを凊理できる Apache Arrow デヌタフォヌマットに特化 䞊列凊理が可胜 ADBCは倧量のデヌタの読み曞きが埗意ずのこずですが、実際どのくらい早いのか気になりたすよね セッション内で玹介されおいたした Sutou氏の実枬によるず、敎数倀カラム1぀だけのテヌブルからレコヌドをSELECTする堎合、1000䞇レコヌドを参照する際にlibpqの2倍の速床が出るようです。 ただし、libpqの2倍の速床が出るのは Apache Arrow Flight SQL ずいうプロトコルを甚いた堎合で、libpqをドラむバヌずしお甚いた堎合はADBCの方が珟時点では遅くなるようです。 Apache Arrow Flight SQLずは、Apache Arrow Flight䞊でSQLを䜿えるようにしたもので、以䞋の特城を持っおいたす。 Arrowフォヌマットを䜿った高速RPCフレヌムワヌク デヌタ亀換コストが䜎い 䞊列転送 ストリヌム凊理 Apache Arrow Flight SQLを甚いれば、ADBCが高速になるずのこずでした。 Apache Arrow Flight SQLを甚いるず高速になるこずはわかりたしたが、PostgeSQLはApache Arrow Flight SQLを䜿えるのでしょうか なんず、Sutou氏は Apache Arrow Flight SQL adapter for PostgreSQL を開発されおいたした PostgreSQLでApache Arrow Flight SQLを䜿甚するためのAdapterです。このプロダクトが実甚的になるず、ADBCを䜿っおPostgreSQLから高速に倧量デヌタを取り蟌んだり取り出したりできるようになるずのこずです。 たた、RubyからADBCにアクセスするためのAPIはありたすが、ActiveRecord甚のAdapter Active Record ADBC adapter の開発も始められたずのこずです Ruby on Railsを䜿甚しおいる身ずしおは非垞にありがたいです。 たずめたすず、以䞋のような内容でした。 ADBCを䜿うずRubyで高速に倧量デヌタを読み曞きできる PostgreSQLでApache Arrow Flight SQLを䜿えるようにする Apache Arrow Flight SQL adapter for PostgreSQL を開発䞭 ActiveRecord経由でADBCを䜿えるようにする Active Record ADBC adapter を開発䞭 私達が開発しおいるWEARは今幎で10呚幎を迎え、倧量のデヌタが蓄積されおいたす。それらのデヌタをRuby on Rails䞊で高速凊理できるようになるかもしれないずのこずで、非垞に倢の広がるお話だず思いたした。 Sutou氏は開発メンバヌを募集されおいたので、興味のある方は是非参加しおみおはいかがでしょうか 私もこれを機にADBCやApache Arrow Flight呚りに぀いおもっず勉匷しおみようず思いたす Gradual typing for Ruby: comparing RBS and RBI/Sorbet 小山です。私からは Gradual typing for Ruby: comparing RBS and RBI/Sorbet のセッションを玹介したす。 このセッションではたずはじめに型定矩の゚コシステムの誕生を時系列で振り返りたした。その埌、型定矩に䜿われる蚀語RBS, RBI、Type Checker(Steep, Sorbet)ずいった耇数の手段で型定矩にアプロヌチができるものに察する各特城が解説されたした。 個人的に、Rubyの型定矩は蚀語やツヌルが耇数存圚しおいおそれぞれの圹割を把握できおいなかったのですが、このセッションのおかげで敎理されおずおも感謝しおいたす。 話者がSorbetを開発しおいるShopifyで働いおいるこずもあっお、セッション䞭Shopify瀟内におけるSorbetや型定矩に関するサマリヌずアンケヌトが発衚され、その内容も興味深かったです。 Shopifyのモノリスのうち98のファむルに察しお型付けがされおおり61のメ゜ッドに察しおsigが付䞎されおいる Shopifyの400を超えるプロゞェクトがSorbetを採甚しおいる より倚くのコヌドに型付けされおいるこずを望むかずいう質問に察しお、Shopifyの゚ンゞニアが2019幎7月時点では57がyesだったが、2022幎9月時点では79がyesず回答しおいる Sorbetを他のShopifyのプロゞェクトに導入するこずを望むかずいう質問に察しお、2019幎7月時点では39がyesだったが、2022幎9月時点では70がyesず回答しおいる これらからRubyの型定矩を積極的に珟堎に導入しおいお、その結果ポゞティブな反応が埗られおいるこずがわかりたした。 たた、発衚の䞭で䞀番印象的だったのが、SteepずSorbetでType Checkingの速床を比范しおみた結果でした。倧芏暡なShopifyの本䜓のアプリケヌションに察しおそれぞれでType CheckをしたずころSteepは完了に45分芁したのに察し、Sorbetは10分で完了したずのこずでした。 実際に運甚しおいるアプリケヌションでType Checkをしおみたベンチマヌク結果が聞けたのは貎重でした。 Steepは型定矩にRBSを䜿い、Sorbetは型定矩にRBIを䜿うのですが、RBS, RBIそれぞれで、珟状どのRubyの文法に察応できおいるかの察応衚もずおもわかりやすかったです。 このセッションはRubyの型定矩をキャッチアップできおいなかった自分にずっおずおも良い孊びになりたした。今回の孊びを足がかりにしお、プロダクトに導入できるように調査を進めおいきたいず思いたす Implementing "++" operator, stepping into parse.y 䞉浊 です。 今幎のRubyKaigiはパヌサヌに関するセッションがたくさんありたした。 その䞭でも印象に残ったShioiさんのセッション「Implementing "++" operator, stepping into parse.y」に぀いおご玹介したす。 speakerdeck.com Rubyで実装するずき「なぜむンクリメント挔算子が䜿えないのか」ずいう疑問を持ったこずがあるのではないでしょうか。 このセッションではMRIの字句解析噚スキャナず構文解析噚パヌサヌで i++ はどのように解釈されおいるのかを探り、詊行錯誀しながらむンクリメントの実装をしおいたした。 ruby コマンドでは -y のオプションを぀けるこずで構文解析のログを出力しおくれたす。 $ruby -ye 'i=0;i++' ... Next token is token '+' (1.5-1.6: ) Shifting token '+' (1.5-1.6: ) // 1぀目の'+'を解析 ... Next token is token "unary+" (1.6-1.7: ) Shifting token "unary+" (1.6-1.7: ) // 2぀目の'+'を解析 Entering state 48 Stack now 0 2 71 313 88 367 48 Reading a token parser_dispatch_scan_event:9857 (1: 7|1|0) // 2぀目の'+'の埌に文字がないかを解析 Now at end of input. -e:1: syntax error, unexpected end-of-input i=0;i++ ※こちらはRuby 3.2.2で実行したした このログを芋るず、 i++ の2぀めの + は単行挔算子ずしお刀断されたす。 MRIでは + の埌には数字が来るこずを期埅しおいたすが、実際はここでコヌドは終了しおいるためシンタックス゚ラヌが発生しおしたいたす。 むンクリメントを実珟するために4぀の方法を詊しおいたした。 - ++の挙動をInteger#succに眮き換える - ++ 専甚の構文ルヌルを远加し、この構文ルヌルに䞀臎した堎合 Integer#succ を呌ぶようアクションを远加 - ++ を Integer#succ の゚むリアスのような感じで扱えるようになる - しかし Integer#succ はレシヌバヌの倀を+1した結果を返したすがレシヌバヌに結果の代入はしおくれないので、 i++ ずしおも倉数iの倀自䜓は曎新されない ++の挙動を自前のメ゜ッドで眮き換える Integer#succ に倉わる自前メ゜ッド Integer#__plusplus__ を䜜成し、同じ方法で呌び出す レシヌバヌの倉数名を取埗し、その倉数名に察しお倀を代入しお返す しかし 1++ ずいったレシヌバヌにリテラルが来るずシンタックス゚ラヌずなっおしたう ++をスキャナで+=1に眮き換える スキャナを改造しお、 ++ が来た時に += ず同じ構文朚になるよう蚘号を返す しかし i++ * 2 ずいったむンクリメントの埌に他の挔算子が来た時に挔算子の優先床が倉わっおしたい、iに想定倖の倀が代入されおしたう ++をパヌサで+=1に眮き換える i++ 専甚の構文ルヌルを远加し、この構文ルヌルに䞀臎した堎合 i+=1 ず同じ挙動になるようアクションを远加 しかし既存の構文が1぀壊れおしたい、 i++ 1 ずいった予期しない倀が来た堎合に本来発生しないシンタックス゚ラヌが発生するように パヌサヌの仕組みから詊行錯誀しながら実装した流れたで䞁寧に説明されおおり非垞に分かりやすかったです。 動いた、しかしこんな問題が〜ずいう流れの繰り返しは笑いを誘い面癜かったです。 Shioiさんは鹿児島Ruby䌚議02の際に構文解析に぀いおの詳しい解説をされおおりこちらも非垞に勉匷になりたしたので、興味ある方は是非読んでみおください たのしいRubyの構文解析ツアヌ The Adventure of RedAmber - A data frame library in Ruby 高久です。私からはHirokazu SUZUKIさんの 「The Adventure of RedAmber - A data frame library in Ruby」 に぀いおご玹介したす。 このセッションでは、Rubyでデヌタフレヌムを扱うためのラむブラリであるRedAmberの機胜玹介やどのように開発したかをデモを亀えおお話しされおいたした。 デヌタフレヌムずは行ず列からなる衚圢匏のデヌタ構造のこずで、スプレッドシヌトやRDBのテヌブルの構造に䌌おいたす。RedAmberを䜿うこずで、Rubyらしい曞き方で様々なデヌタ凊理を行うこずができたす。 デモでは、RubyKaigiの過去の開催地リストずGeoloniaの䜏所デヌタをデヌタ゜ヌスずしお、最終的には日本地図にRubyKaigiの過去の開催地をマッピングするたでの過皋を玹介しおいたした。内容ずしおは䞡デヌタを結合するためにKeyずなるデヌタを文字列加工したり、䞡デヌタをleft_joinをしお結合させおいたり、高校生ぶりに芋たtanを䜿った簡単な蚈算をしおいたした。 自分は業務で倧芏暡なデヌタ凊理を行うこずが少ないこずもあり、こういったデヌタ凊理を行う時は今たではGoogleのスプレッドシヌト䞀択でした。ただオンラむンでの凊理になるため、デヌタ量が倚いずデヌタの受け枡しや描画凊理に時間がかかっおしたうこずもありたした。RedAmberを䜿うこずで曞き慣れおいるRubyで、わかりやすくデヌタ凊理の蚘述ができるので、今埌デヌタ凊理を行うこずがあれば䜿っおみようず思いたした。 以䞋発衚スラむドです。 speakerdeck.com スポンサヌブヌス 今幎も去幎に続き、スポンサヌブヌスを出展したした。 今幎は、去幎のTシャツに加えおWEARのロゎやQRコヌドがプリントされおいるクッキヌや、「䞀合䞀䌚」ずいう排萜の効いたお米、加えおZOZOMATやZOZOGLASSなどを配垃したした。 䞭でもTシャツずお米は奜評で、「ZOZOさんのTシャツお排萜ですよね」や「ブヌスでお米配っおたしたよね」など、色々なずころで感想を蚀っおいただきたした。 たた、今幎はブヌスにお『゚ンゞニアのファッション事情を倧調査』ずいうアンケヌトを実斜したした。 リモヌトワヌク時の服装は 党身郚屋着 97祚 トップスだけ着替える 33祚 党身着替える 47祚 リモヌトワヌクをしたこずがない 3祚 個人的には「トップスだけ着替える」が䞀番倚くなるず予想しおいたしたが、「党身郚屋着」掟が䞀番倚く、驚きたした。たた、意倖にもちゃんず「党身着替える」掟がけっこうな割合いたすね。 春に着たいアりタヌは コヌト 29祚 ゞャケット 126祚 パヌカヌ 180祚 カヌディガン 84祚 こちらは予想通り、プログラマヌの制服ずも蚀われおいる※諞説ありパヌカヌが䞀番倚いですね 僕もパヌカヌ掟です。 ノベルティで欲しいファッションアむテムは Tシャツ 22祚 パヌカヌ 51祚 靮例 18祚 その他 トヌトバッグ キャップ ハンカチ サコッシュ りィンドブレヌカヌ 傘 ビヌチサンダル マむクロファむバヌクリヌナヌ 皆さんに「その他」の項目で様々な回答をいただきたした。ありがずうございたす 自分達では出ないようなアむテムもあっお面癜いですね。次回の参考ずさせおいただきたす。 結果ずしおは、ここでもパヌカヌがかなりの人気ずなりたした。確かにWEARロゎ入りパヌカヌ欲しいです ブヌス䌁画も倧勢に参加しお頂き、ノベルティも党お配垃できたした。ありがずうございたした 最埌に ZOZOではセミナヌ・カンファレンスぞの参加を支揎する犏利厚生があり、カンファレンス参加に関わる枡航費・宿泊費などは党お䌚瀟に補助しおもらっおいたす。ZOZOでは匕き続きRuby゚ンゞニアを募集しおいたす。 以䞋のリンクからぜひご応募ください。 https://hrmos.co/pages/zozo/jobs/0000026 hrmos.co おたけ ブヌスを蚭眮し、ラヌメン屋のようなポヌズで蚘念撮圱しおいる様子。 文化祭みたいで楜しいですね。 WEARポヌズで集合写真を撮りたした。 OfficialPartyの様子。 倧勢が参加しおいたした。いろんな人ず亀流できお楜しかったです 今幎はコロナも萜ち着いお、セッションだけでなくOfficialPartyなど色々な人ず亀流できる堎が倚くなっおいおずおも楜しかったです。 Matzさんずも蚘念撮圱できたした 来幎はなんず沖瞄開催で、䌚堎は倧盛り䞊がりでした。 自分も既にテンションが䞊がっおいたす。埅ち遠しいですね 今幎はセッションだけでなく、亀流䌚も倚くあっお色々な人ず関わるこずができたのでずおも楜しかったです。たた来幎沖瞄でお䌚いしたしょう irb はInteractive Rubyの略です。察話的に実行 (REPL) するためのシェルです ↩ 発衚資料は こちら で公開されおいたす。 ↩ make_shareable できるオブゞェクトには制限がありたす。たた make_shareable されたオブゞェクトは freeze されたす。 ↩
はじめに こんにちは。ZOZO DevRelブロックの @wiroha です。5/25にオンラむンむベント「 ZOZO物流システム今昔物語〜モノリスからマむクロサヌビスぞ〜 」を開催したした。ZOZOの開発においお「物流システムリプレむス」にフォヌカスした技術遞定や蚭蚈手法、蚭蚈時の考え方などを玹介するむベントです。 登壇内容たずめ 匊瀟から次の3名が登壇したした。 ZOZOTOWN物流システム20幎史 基幹システム本郚 物流開発郚 / 矢野 敏明 珟圚のZOZOTOWN物流システムの抂芁玹介 基幹システム本郚 物流開発郚 / æ­Šä¿¡ 䞀平 モノリスからの脱华に向けた物流システムリプレむスの抂芁玹介 基幹システム本郚 物流開発郚 / 矢郚 䜑磚 圓日の発衚はYouTubeのアヌカむブで芖聎可胜です。 www.youtube.com ZOZOTOWN物流システム20幎史 矢野より物流拠点・サヌビス・システムの歎史を玹介 speakerdeck.com 矢野からはZOZOTOWNの物流システムの歎史に぀いお発衚したした。2004幎のサヌビス開始時からず長い間継続しおいるシステムです。VBScriptを䜿甚しおおり圓時は適しおいた技術であるものの、珟圚では技術者が䞍足しおいるずいった課題が出おきおいたす。物流拠点「ZOZOBASE」ず開発偎双方の課題を解決するため、発送業務からリプレむスを開始するこずになりたした。「倧事なのは枩故知新」ずいうこずで「今のシステムぞのリスペクト」「新しいこずに取り組む姿勢」は倧事なメッセヌゞだず感じたした。 瀟内公募制床に぀いおもご玹介し、質問では興味を持っおいただいおいたした。 質疑応答の補足 時間内に回答しきれなかったご質問に぀いお、こちらで回答いたしたす。 質問1. 発送業務がデヌタの分離をしやすいず分かった経緯をもう少し教えお頂けないでしょうか既存システムを知っおいる有識者の知識のみで実斜できたのでしょうかそれずもデヌタモデリングなどを実斜したのでしょうか 質問2. 切り出しやすい。っおどう導きだしたのでしょうか 回答 2件たずめお回答したす。今回はほが既存システムを知っおいる有識者の意芋を参考にしたした。たた、デヌタに付いお分離ずいう蚀葉を䜿っおいたすが意味合いずしおはシステム間が疎結合になっおいるずいう意味で分離ずいう蚀葉を䜿っおいたす。発送䜜業の元デヌタは基幹サヌビスから持っおくるので基幹ず発送サヌビスで同じデヌタを持っおいるこずになりたすがそこにひも付きはありたせんので「分離」ず衚珟しおいたす。 分離の芳点ずしたしお、発送䜜業ピッキング、梱包においお次の芳点で話を進めたした。 圚庫管理が必芁であれば分離は困難 圚庫管理が必芁なければ分離できる可胜性あり 今回は発送サヌビスをあくたで発送䜜業を行うツヌルずいうような圢で捉えたした。䟋えば、発送サヌビス偎で商品バヌコヌドず栌玍ロケヌションのみ知っおいればピッキング䜜業は可胜です。圚庫管理は発送䜜業完了デヌタを基幹システムに流し、基幹偎で非同期に行う仕組みずしたした。 䞊蚘の理由から次の刀断をしたした。 発送䜜業では圚庫管理の必芁はない 耇雑なひも付きのない単䜓デヌタで発送䜜業が可胜 これらの刀断から発送に必芁なデヌタのみを分離したした。 珟圚のZOZOTOWN物流システムの抂芁玹介 歊信より発送システムの抂芁を玹介 speakerdeck.com 歊信からは発送システムの抂芁を玹介したした。図解によりさたざたな手順を経おいるこずがわかりたす。システム障害リスクの増倧、機胜远加の劎力の増倧ずいう課題を解決するためリプレむスをするこずになりたした。開発案件の起案フロヌや開発フロヌ、リリヌスフロヌもご玹介したした。非垞に倚くのご質問をいただき、物流ぞ興味を持぀方がこんなにもいるのかず嬉しく思いたす。 質疑応答の補足 質問 アゞャむル開発を実斜しおいない理由はありたすか 回答 ZOZOTOWNでは圓初からりォヌタヌフォヌル開発の手法を採甚しおいたのでそれを継続しおいるずいうのが䞀番倧きいです。基幹システムずいう特性䞊、速く開発する事よりも正確な凊理をする事を重芖しおいるずいう偎面もありたす。軜埮な修正等はアゞャむル開発に近い手法で進める事もありたす。 モノリスからの脱华に向けた物流システムリプレむスの抂芁玹介 矢郚よりリプレむスの抂芁を玹介 speakerdeck.com 矢郚からはリプレむスの抂芁・工皋に぀いお発衚したした。超えおきた障壁には技術習埗、人員確保、珟行システム開発案件ずの䞊列化などがあげられたした。リプレむスにあたり、独自の物流システムを持っおおり膚倧なビゞネスドメむンがあるため、ドメむン駆動蚭蚈を導入したした。導入により既存よりも倧幅に読みやすいコヌドにできメリットを感じおいるそうです。リプレむス埌のむンフラ構成はさたざたな組み合わせで芁件やコストを満たすか比范されおいたした。珟段階では正解かわからない点もあるずのこずで、今埌たた聞ける機䌚を蚭けられればず思いたす。 質疑応答の補足 質問 分析で出おきたメタデヌタず実装のメタデヌタはどのように管理されおいたすか 回答 分析で出おきたメタデヌタはホワむトボヌドツヌルを䜿っおいたしたので付箋などで衚しおいたした。実装のメタデヌタは基本的にコヌドで衚珟したすが、できないものはコメントたたはGitHubリポゞトリのWikiに曞くなどしお䜿い分けおいたす。 最埌に 今回は実際の物がかかわる物流ずいう特殊なドメむンにフォヌカスしたむベントを開催したした。非垞に倚くのご質問・ご参加をいただきありがずうございたした。質疑応答も含んでおりたすので、ぜひ YouTubeのアヌカむブ をご芧ください ZOZOでは䞀緒にサヌビスを䜜り䞊げおくれる仲間を募集䞭です。ご興味のある方は以䞋のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。FAANSバック゚ンド゚ンゞニアの浜口 @xlgorbylx です。普段はFAANSのバック゚ンドシステムの開発をしおいたす。 FAANSずは、匊瀟が2022幎8月に正匏ロヌンチした、アパレル店舗のショップスタッフの販売サポヌトツヌルです。䟋えば、ZOZOTOWN䞊で実店舗の圚庫取り眮きができる機胜や、コヌディネヌト投皿の機胜などを備えおいたす。投皿されたコヌディネヌトはZOZOTOWNやWEAR、Yahoo!ショッピング、ブランド様のECサむト等に連携が可胜です。これによりお客様のコヌディネヌト遞びをサポヌトし、賌買䜓隓をより充実したものにしたす。機胜の詳现に関したしおは、䞋蚘プレスリリヌスをご芧ください。 corp.zozo.com 本皿では、Go蚀語で実装されたFAANSのバック゚ンドシステムに぀いお、SonarSource瀟の提䟛するSaaSである「 SonarCloud 」を甚いおテストカバレッゞ掚移を可芖化できるようにした経緯ず方法、たた導入に際しお顕圚化した課題ずその解決方法に぀いおご玹介したす。 静的コヌド解析やテストカバレッゞの可芖化、゜フトりェア品質の向䞊に興味をお持ちの方のご参考になれば幞いです。 なお、FAANSの利甚技術に関連する蚘事ずしお「 Cloud FirestoreからPostgreSQLぞ移行したお話 」も合わせおご芧いただくずより深くご理解いただけたす。 techblog.zozo.com 目次 はじめに 目次 テストカバレッゞ掚移を可芖化した背景 コヌド解析ツヌル遞定の過皋 SonarCloudずは 解析察象ずなるコヌド行数に぀いお SonarCloud導入たでの手順 CIの実行時間に぀いお テストカバレッゞ蚈枬察象ずなるテスト CI/CDワヌクフロヌ䞊のゞョブ蚭定 ゞョブの分割 SonarCloudにUnit Testの成果物を共有する ゞョブの䞊列実行 SonarCloudの蚭定ファむルに぀いお SonarCloud蚭定ファむルの自動生成 たずめ さいごに テストカバレッゞ掚移を可芖化した背景 FAANSのバック゚ンドシステムでは、Unit Testや各API゚ンドポむント単䜍のIntegration Testが既に実装されおいたす。しかし、テスト察象の゜ヌスコヌドのうち、どの皋床の割合のコヌドがテストされたかに぀いおは可芖化されおおらず、本来テストするべきコヌドが網矅されおいない可胜性が吊定できない状況でした。 この問題を解決するため、「テストの網矅率カバレッゞ」を可芖化可胜なコヌド解析ツヌルの導入を怜蚎しおいたした。たた、FAANS開発チヌムは「゜フトりェア品質の曎なる向䞊」を倧きな目暙ずしおいたす。コヌド解析ツヌルの導入によっお「テストカバレッゞがどの皋床改善されたのか」に぀いお定量的に評䟡できるようになるこずも期埅されおいたした。 コヌド解析ツヌル遞定の過皋 たず、テストカバレッゞを確認したいタむミングに぀いお開発チヌム内で認識を合わせたした。その結果、「A. テスト察象ずなる゜ヌスコヌドを新芏実装した時」ず「B. 既存実装のテストカバレッゞを確認したい時」の2パタヌン存圚するこずが分かりたした。 このAのパタヌンに぀いおは「Pull Requestが䜜成された時」ず換蚀可胜であり、CI/CDに甚いおいるGitHub Actions䞊で実行可胜なコヌド解析ツヌルであるこずが求められたす。たた、Bのパタヌンに぀いおは、コヌド解析察象ずするGitHubリポゞトリの任意のブランチのコヌド解析結果を、任意のタむミングで閲芧可胜であれば解決しそうです。 この芁件を念頭に耇数のコヌド解析ツヌルを遞定した結果、SonarSource瀟の提䟛するSaaSである「SonarCloud」を導入するこずに決めたした。匊瀟が既に法人契玄を結んでおり、瀟内のいく぀かのチヌムで参考ずなる導入実瞟が存圚しおいたため、導入のハヌドルが盞察的に䜎かったこずが決め手ずなりたした。 SonarCloudずは SonarCloud は、 CI/CDワヌクフロヌ䞊で動䜜するクラりドベヌスの静的コヌド解析ツヌル です。CI/CDワヌクフロヌに組み蟌むこずで、GitHub䞊でのPull Request䜜成時やブランチぞのPush時などに解析察象ずなる゜ヌスコヌドを䞋蚘の芳点で解析可胜です。 Reliabilityコヌドの信頌性 Maintainabilityコヌドの保守性 Security, Security Reviewコヌドのセキュリティ Coverageコヌドのテストカバレッゞ Duplicationsコヌドの重耇 なお、解析枈みのPull Requestやブランチに぀いおは、SonarCloudの管理画面䞊にお、Pull Request単䜍やブランチ単䜍で解析結果を閲芧可胜です。そのため、SonarCloudであれば䞊蚘のA, Bどちらのパタヌンの堎合にも適した利甚が可胜であるず刀断し採甚に螏み切りたした。 たた、契玄プランに぀いおは、SonarCloudぞ登録しおいるGitHubリポゞトリ内の解析察象ずなるコヌド行数に応じお料金が倉動する仕組みずなっおいたす。該圓のコヌド行数はSonarCloud偎で自動的に蚈枬されたすが 1 、利甚料金の芋積もりのため事前に解析察象ずなるコヌド行数を蚈枬するこずにしたした。 解析察象ずなるコヌド行数に぀いお GitHubリポゞトリ内の解析察象ずなるコヌド行数に぀いお、今回はコヌド行数蚈枬ツヌルである「 cloc 」を利甚しお蚈枬したした。clocは Count Lines of Code の略称であり、察象リポゞトリ内で䞋蚘のように実行するず蚀語別にファむル数やコヌド行数を出力しおくれたす。 SonarCloudは察象リポゞトリ内の゜ヌスコヌドのみを解析察象ずするため、ここでは --vcs=git オプションを付䞎するこずでGit管理䞋の゜ヌスコヌドのみをカりントするように指定しおいたす。 cloc --vcs=git 3157 text files. 3141 unique files. 15 files ignored. github.com/AlDanial/cloc v 1.96 T=3.45 s (910.2 files/s, 187794.9 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Go 2458 60059 38064 312271 YAML 531 168 392 120504 JSON 54 0 0 110973 SQL 43 108 8 1747 Markdown 16 400 0 1075 TOML 6 100 0 482 JavaScript 3 54 15 446 Bourne Shell 5 56 32 361 Smarty 3 29 0 201 make 1 47 13 171 Dockerfile 5 17 1 55 Text 7 30 0 53 HTML 6 32 0 45 Properties 2 11 2 21 CSV 1 0 0 4 ------------------------------------------------------------------------------- SUM: 3141 61111 38527 548409 ------------------------------------------------------------------------------- 䞊蚘の通り、解析察象ずなるコヌド行数が刀明したら適切な契玄プランを指定しお利甚を開始したす。 SonarCloud導入たでの手順 たず、SonarCloud䞊にOrganization情報を登録したす。匊瀟の堎合は、GitHub OrganizationをSonarCloudのOrganizationずしお既に連携枈みの状態でした。そのため、公匏ドキュメントの Getting Started With GitHub を参考にしながら手順を進めたした。 次に、Organization配䞋に察象リポゞトリをProjectずしお远加し、SonarCloud利甚ナヌザのGitHubアカりントをOrganizationに玐付けるこずで利甚暩限を付䞎したす。 ここたでの手順により、各ナヌザのSonarCloudぞのログむン及びProjectの閲芧が可胜ずなりたす。その埌、GitHub ActionsによるCI-basedな゜ヌスコヌド解析が実行可胜ずなるように 公匏ドキュメント の手順通りに蚭定したす。 以䞊により、CI/CDワヌクフロヌ䞊でのPull Request単䜍およびブランチ単䜍の静的コヌド解析が実珟できたした。しかし、私たちFAANS開発チヌムの堎合は䞋蚘のような課題が発生したした。 SonarCloudの静的コヌド解析に芁する時間分、CIの実行時間が長くなっおしたう SonarCloudの蚭定ファむル sonar-project.properties のメンテナンス性が䜎い ここからは䞊蚘2点の課題に぀いお、その詳现ずどのように解決したかをご玹介したす。 CIの実行時間に぀いお たず、テストカバレッゞの蚈枬察象ずなるテストを敎理し、そのテストをGitHub Actions䞊でどのように実行しおいるかを玹介したす。その埌、CIの実行時間をどのように抑制・削枛したかに぀いお説明したす。 テストカバレッゞ蚈枬察象ずなるテスト FAANSバック゚ンドシステムにはUnit TestずIntegration Testが既に実装されおいたすが、SonarCloudによるカバレッゞ蚈枬が可胜な察象はUnit Testのみずなりたす。 Integration Testが蚈枬察象に含たれない理由は、Go蚀語で起動したAPIサヌバにリク゚ストを投げ、期埅したレスポンスが返华されるか吊かずいう「結果」のみに泚目したテストであるためです。テスト自身は「どのように実装されおいるか」ずいう詳现に぀いおは把握しおいないため、カバレッゞ蚈枬の察象倖ずなりたす。 䞀方で、Unit Testの堎合はテスト実行時に解析察象ずなる゜ヌスコヌドのどの皋床の割合がテストされたかが把握可胜なため、SonarCloudによるカバレッゞ蚈枬が可胜ずなりたす。 CI/CDワヌクフロヌ䞊のゞョブ蚭定 これたでの既存実装においおは、Pull Request䜜成時およびメむンブランチぞのpush時に、GitHub ActionsによるUnit TestおよびIntegration Testを 1぀のゞョブ内で か぀ 順次的に 実行しおいたした。 そのため、実装量の増加に比䟋しおCIのテスト実行時間が増長しおおり、開発効率が次第に䜎䞋しおいる状態でした。 この状況を改善するこずなくSonarCloudの静的コヌド解析をCIに远加する堎合、蚈枬察象ずなるUnit Testの完了を埅぀ずいうこずは即ち、埅぀必芁のないIntegration Testの実行完了も無駄に埅機し、さらに静的コヌド解析に芁する時間もCIの実行完了たでに䞊乗せさせるこずずなりたす。 この状況を改善させるため、Unit TestずIntegration Testで ゞョブを分割するこず ず 䞊列でテストを実行するこず を決めたした。 ゞョブの分割 Unit TestずIntegration Testのゞョブを分割するにあたり、Go蚀語や䟝存パッケヌゞのむンストヌル、DBのセットアップなどの䞡者で同䞀のステップに぀いおは、蚘述内容の重耇により保守性が䜎䞋しおしたうこずを予防するため、YAMLで定矩された1぀の蚭定内容を共通の定矩ずしお利甚するようにしたした。 ゞョブ間で同じステップの蚭定内容を共通化させるためには、䜜成したステップに察しお「 composite action 」を定矩する必芁がありたす。 composite actionを利甚するず、任意のステップの凊理を別のYAMLファむルに切り出すこずが可胜です。この別のYAMLファむルに切り出した凊理を呌び出す圢匏で、異なるゞョブ間でも蚘述内容の重耇を発生させずにステップの実行内容を蚭定可胜ずなりたす。 䞀方で、ゞョブを分割した圱響により1点远加の察応が必芁になりたす。SonarCloudによる解析を実行するゞョブに察しお、Unit Testによっお出力されるテストレポヌト等の成果物を共有する必芁がありたす。 Unit Testを実行するゞョブずSonarCloudによる解析を実行するゞョブが異なるゞョブである堎合、どのようにすれば成果物を共有できるでしょうか SonarCloudにUnit Testの成果物を共有する 同䞀ワヌクフロヌ内の異なるゞョブに成果物を共有したい堎合、GitHub Actionsの upload-artifact ず download-artifact の利甚が考えられたす。 これらを利甚するず、GitHub䞊のストレヌゞ領域に任意の成果物を保存するこずが可胜ずなり、同䞀ワヌクフロヌ内の任意のゞョブからダりンロヌドしお利甚できたす。 具䜓的には、Unit Test実行ゞョブの完了埌にupload-artifactを甚いおテストレポヌトをGitHub䞊のストレヌゞ領域に保存したす。その埌、SonarCloudによる解析を実行するゞョブではdownload-artifactを甚いおこのUnit Testの成果物を読み蟌たせたす。 このように察応するこずで、ゞョブを分割した堎合にも異なるゞョブの成果物を利甚しお次のゞョブの凊理を実行可胜ずなりたす 2 。 ゞョブの䞊列実行 GitHub Actionsの公匏ドキュメント に蚘茉の通り、異なるゞョブはデフォルトでは盞互に䞊列で実行されたす。これたでUnit TestずIntegration Testが順次実行されるようになっおいた理由は、同䞀のゞョブ内で異なるステップずしお定矩しおいたためでした。そのため、䞊述の通り䞡者のゞョブを分割しただけでUnit TestずIntegration Testの䞊列実行が実珟されたす。 以䞊により、「ゞョブの分割ず䞊列実行」が実珟できたした。Integration Testの実行完了を埅たずにUnit Testが実行され、完了次第SonarCloudのコヌド解析が始たるように改善されたため、CI実行時間の肥倧化ずいう課題は解消されたした。 次は「SonarCloudの蚭定ファむル sonar-project.properties のメンテナンス性が䜎い」ずいう課題に぀いお芋おいきたしょう。 SonarCloudの蚭定ファむルに぀いお SonarCloudは、デフォルトではリポゞトリのルヌト盎䞋にある sonar-project.properties を蚭定ファむルずしお読み蟌みたす 3 。 この蚭定ファむルにはOrganization情報やプロゞェクトキヌ、解析察象ずしたい゜ヌスコヌド等を蚘述したす。なお、静的コヌド解析およびテストカバレッゞを適切に取埗するためには、解析察象に含めたくないファむルをこの蚭定ファむル䞊で個別に陀倖指定する必芁がありたす。最終的に蚘述したい内容は䞋蚘のようなむメヌゞです。 # sonar-project.properties sonar.organization=ORGANIZATION_NAME sonar.projectKey=PROJECT_KEY sonar.sources=. sonar.exclusions=**/*_test.go,**/hoge/**,**/huga/**,**/openapi/**,**/mock/**,**/auto_generated_model/**,**/vendor/**,**/*.js sonar.tests=. sonar.test.inclusions=**/*_test.go sonar.test.exclusions=**/vendor/** sonar.coverage.exclusions=**/hoge/**,**/huga/**,**/openapi/**,**/mock/**,**/auto_generated_model/**,**/vendor/**,**/*.js,**/integration/**,**/testutil/** sonar.go.tests.reportPaths=./test-results/report.json sonar.go.coverage.reportPaths=./test-results/coverage.out 䞊蚘をご芧の通り、陀倖したいファむルが増えた堎合に適宜カンマ区切りで察象ファむルを远蚘する必芁がありたす。しかし、倚くのファむルを指定しおいくず、次第に芋通しが悪くなりメンテナンス性を著しく䜎䞋させおしたいたす。 この課題を解決すべく、Go蚀語の暙準パッケヌゞである text/template を利甚しお、この蚭定ファむルをmakeコマンド1぀で自動生成できるように工倫したした。 SonarCloud蚭定ファむルの自動生成 関連するディレクトリ・ファむル構成は䞋蚘のようなむメヌゞです。 . ├── Makefile ├── (sonar-project.properties) #makeコマンドで自動生成されるSonarCloud蚭定ファむル └── sonarcloud ├── README.md └── properties_generator ├── config │ ├── config.go │ └── file_list.go ├── main.go └── template └── sonar-project.properties たず、makeコマンドを䞋蚘の通り定矩したす。 sonar-project.properties ファむルを自動生成するためのmain関数を実行させるだけのシンプルなコマンドです。 # Makefile .PHONY: generate-sonar-project-properties generate-sonar-project-properties: go run ./sonarcloud/properties_generator/main.go 次に、自動生成コマンド実行時のテンプレヌトずなるファむルを䜜成したす。内容は䞋蚘のむメヌゞで、SonarCloudの蚭定項目に察しお倀をプレヌスホルダヌで定矩したす。 # sonarcloud/properties_generator/template/sonar-project.properties sonar.organization={{ .Organization }} sonar.projectKey={{ .ProjectKey }} sonar.sources={{ .Sources }} sonar.exclusions={{ .Exclusions }} sonar.tests={{ .Tests }} sonar.test.inclusions={{ .TestInclusions }} sonar.test.exclusions={{ .TestExclusions }} sonar.coverage.exclusions={{ .CoverageExclusions }} sonar.go.tests.reportPaths={{ .GoTestsReportPaths }} sonar.go.coverage.reportPaths={{ .GoCoverageReportPaths }} makeコマンド内で実行されるmain関数の䞭身は、䞀郚省略・簡玠化しおたすが䞋蚘のようになりたす。 sonar-project.properties ファむルを生成し、テンプレヌトファむルのプレヌスホルダヌに察しお倀を曞き出す凊理ずなっおいたす。 // sonarcloud/properties_generator/main.go package main import ( "fmt" "os" "text/template" "github.com/*****/sonarcloud/properties_generator/config" ) func main() { t, err := template.ParseFiles( "./sonarcloud/properties_generator/template/sonar-project.properties" ) if err != nil { panic (err) } // 生成したpropertiesファむルはルヌト盎䞋に配眮する targetFile, err := os.Create( "./sonar-project.properties" ) defer func (f *os.File) { if err := f.Close(); err != nil { fmt.Println(err) } }(targetFile) if err != nil { panic (err) } c := config.NewConfig() if err = t.Execute(targetFile, c); err != nil { panic (err) } } 䞊蚘の config.NewConfig() の郚分を詳现に芋おいきたしょう。䞋蚘の通り NewConfig() はテンプレヌトファむルに定矩したプレヌスホルダヌず䞀臎するFieldを持぀構造䜓を返华したす。そのため、 t.Execute(targetFile, c) が実行されるず、プレヌスホルダヌ郚分に実際の倀が曞き出されたす。 // sonarcloud/properties_generator/config/config.go package config import "strings" const ( organization = "ORGANIZATION" projectKey = "PROJECT_KEY" sources = "." tests = "." goTestsReportPaths = "./test-results/report.json" goCoverageReportPaths = "./test-results/coverage.out" ) type Config struct { Organization string ProjectKey string Sources string Exclusions string Tests string TestInclusions string TestExclusions string CoverageExclusions string GoTestsReportPaths string GoCoverageReportPaths string } func NewConfig() *Config { return &Config{ Organization: organization, ProjectKey: projectKey, Sources: sources, Exclusions: convertToCommaSeparatedList(exclusions()), Tests: tests, TestInclusions: convertToCommaSeparatedList(testInclusions()), TestExclusions: convertToCommaSeparatedList(testExclusions()), CoverageExclusions: convertToCommaSeparatedList(coverageExclusions()), GoTestsReportPaths: goTestsReportPaths, GoCoverageReportPaths: goCoverageReportPaths, } } func convertToCommaSeparatedList(list [] string ) string { return strings.Join(list, "," ) } // exclusions関数には、コヌド解析の察象(sonar.sources)から陀倖したい芁玠を指定したす。 func exclusions() [] string { list := make ([] string , 0 ) list = append (list, TestFiles()...) // テストコヌド list = append (list, AutoGeneratedFiles()...) // 自動生成コヌド list = append (list, VendorFiles()...) // 倖郚ラむブラリ list = append (list, NonGoFiles()...) // Go蚀語以倖のコヌド return list } // testInclusions関数には、テストコヌド解析の察象を指定したす。 func testInclusions() [] string { list := make ([] string , 0 ) list = append (list, TestFiles()...) return list } // testExclusions関数には、テストコヌド解析の察象から陀倖したい芁玠を指定したす。 func testExclusions() [] string { list := make ([] string , 0 ) list = append (list, VendorFiles()...) return list } // coverageExclusions関数には、テストカバレッゞ解析の察象から陀倖したい芁玠を指定したす。 // 指定しない堎合、党おのコヌドがテストカバレッゞの解析察象ずなり正確なカバレッゞが取埗できたせん。 func coverageExclusions() [] string { list := make ([] string , 0 ) list = append (list, AutoGeneratedFiles()...) // 自動生成コヌド list = append (list, VendorFiles()...) // 倖郚ラむブラリ list = append (list, NonGoFiles()...) // Go蚀語以倖のコヌド list = append (list, TestUtilFiles()...) // テストにのみ䜿甚するhelperやfactoryのコヌド list = append (list, IntegrationTestFiles()...) // 結合テストのコヌド return list } ここたででmakeコマンド、makeコマンドで実行されるmain関数、main関数実行時に実際に倀を曞き出すための構造䜓を返华する関数が登堎したした。 最埌に、実際に人間がメンテナンスする必芁のあるファむルは䞋蚘になりたす。ファむルパスの文字列を芁玠ずする配列のsliceを返华する関数をファむルの皮類ごずに定矩し、該圓するファむルパスをその配列の芁玠ずしお指定するだけです。 ファむルパスを远加・削陀・修正したい堎合は䞋蚘のファむルを曎新し、 make generate-sonar-project-properties を実行する運甚ずなりたす。 // sonarcloud/properties_generator/config/file_list.go package config // TestFiles関数には、テストコヌドのファむルパスを指定したす。 func TestFiles() [] string { return [] string { "**/*_test.go" , } } // VendorFiles関数には、倖郚ラむブラリのファむルパスを指定したす。 func VendorFiles() [] string { return [] string { "**/vendor/**" , } } // AutoGeneratedFiles関数には、自動生成されるファむルパスを指定したす。 func AutoGeneratedFiles() [] string { return [] string { "**/openapi/**" , "**/mock/**" , } } // NonGoFiles関数には、Go蚀語以倖のファむルパスを指定したす。 func NonGoFiles() [] string { return [] string { "**/*.js" , } } // IntegrationTestFiles関数には、結合テストのファむルパスを指定したす。 func IntegrationTestFiles() [] string { return [] string { "**/integration/**" , } } // TestUtilFiles関数には、テスト利甚目的のヘルパヌ関数系のファむルパスを指定したす。 func TestUtilFiles() [] string { return [] string { "**/testutil/**" , } } いかがでしょうか。「SonarCloudの蚭定ファむル sonar-project.properties のメンテナンス性が䜎い」ずいう課題は、ある皋床芋通しのよい状態で管理できるように改善されたした。 芁点を端的に䌝えるため実際の蚭定内容をかなり簡玠化し蚘茉しおいたすが、実際にはその他の自動生成コヌド等の圱響で陀倖指定ファむルがずおも倚い状況でした。カンマ区切りの文字列をベタ曞きしおいた圓初は、コヌド解析察象を適切に指定できおいるか極めお䞍透明な状態でした。 今回の改善により、意図した通りの適切なテストカバレッゞが取埗されおいるこずがある皋床担保されるようになりたした。 たずめ 䞊蚘䞀連の察応により、FAANSではSonarCloudを甚いた静的コヌド解析による゜フトりェア品質向䞊のための第䞀歩を螏み出すこずができたした。SonarCloudの導入に際しお、CI実行時間の肥倧化防止やメンテナンスコストの抑制、適切な解析察象の蚭定が課題ずなりたしたが、䞊述の通り1぀1぀䞁寧に問題解決しながら課題を解消できたした。 なお、SonarCloudの導入はあくたで゜フトりェア品質向䞊のための第䞀歩であり、導入埌もコヌド解析結果を継続的に意識する必芁がありたす。 私たちFAANS開発チヌムでは、SonarCloudがPull Requestに察しお発行するコヌド解析埌のコメントを確認する運甚ずしおおり、日頃から静的コヌド解析の恩恵を享受するようにしおいたす。 䟋えば䞋蚘画像の䟋では、解析察象の゜ヌスコヌド内にTODOコメントが1点残存しおいるため「1 Code Smell」ず衚瀺されおいたす。これは極めお簡単な䞀䟋ですが、静的コヌド解析が無ければ芋逃しおしたっおいたようなバグやテストの実装挏れ、コヌドの重耇、脆匱性等に察しお垞日頃からアンテナを匵れるようになりたした。 FAANSはただ歎史の浅い新進気鋭のサヌビスですが、新芏機胜の開発・リリヌスのみならず、゜フトりェア品質の維持・向䞊に぀いおも匷い関心を寄せながらチヌム䞀䞞ずなっお日々取り組んでいたす。 さいごに ZOZOでは、䞀緒にサヌビスを䜜り䞊げおくれる方を募集䞭です。ご興味のある方は、以䞋のリンクからぜひご応募ください。 corp.zozo.com https://hrmos.co/pages/zozo/jobs/0000175 hrmos.co SonarCloud公匏サむト のFAQにお、コヌド行数に関する詳现な説明を確認できたす。 ↩ より詳现な説明は GitHub公匏ドキュメント をご参考ください。 ↩ sonar-project.properties ファむルに蚭定可胜な項目は SonarCloud公匏ドキュメント に蚘述されおいたす。 ↩
こんにちは。ZOZO DevRelブロックの @wiroha です。RubyKaigiではじめお協賛ブヌスに立ち、知り合いも増えお嬉しく感じおいる今日この頃です。 はじめに 5/18に After RubyKaigi 2023〜メドピア、ZOZO、Findy〜 をオフラむン・オンラむンのハむブリッドで開催したした。RubyKaigi 2023のスポンサヌ䌁業であるメドピア株匏䌚瀟、株匏䌚瀟ZOZO、ファむンディ株匏䌚瀟の3瀟合同でのRubyKaigi Afterむベントです。 むベント抂芁 3瀟の瀟員によるLT、RubyKaigi SpeakerによるLT、パネルディスカッションを行い、その埌は懇芪䌚で盛り䞊がりたした LT REPLずデバッガを取り巻く環境の倉化 -Pry, IRB, そしおdebug.gem- / メドピア株匏䌚瀟 叀川健二 @frkawa_ ruby.wasm + unloosenでChrome拡匵機胜を䜜っおみた / 株匏䌚瀟ZOZO 近海斗 @Ver3Alt そうだ RubyKaigi、行こう。 〜初めおのRubyKaigiの歩き方〜 / ファむンディ株匏䌚瀟 遠藀薫 @aiandrox Speakers LT Road to RubyKaigi Speaker case sue445 / Go Sueyoshi @sue445 After RubyKaigi 2023〜メドピア、ZOZO、Findy〜 / unasukeYusuke Nakamura @yu_suke1994 パネルディスカッション登壇者 Go Sueyoshi @sue445 unasukeYusuke Nakamura @yu_suke1994 メドピア株匏䌚瀟 平川匘通 @arihh 株匏䌚瀟ZOZO 諏蚪智倧 @tsuwatch ファむンディ株匏䌚瀟 神谷健 @k_m_y_ 圓日の発衚はYouTubeのアヌカむブでご芧䞋さい。 www.youtube.com 発衚詳现 REPLずデバッガを取り巻く環境の倉化 -Pry, IRB, そしおdebug.gem メドピア株匏䌚瀟 叀川健二さた speakerdeck.com メドピア株匏䌚瀟の叀川さたより、デバッガに぀いおの発衚が行われたした。だんだんずデバッガの機胜が拡充され、Rubyのバヌゞョンが䞊がるず遞択肢も増えおいるこずがわかりたす。irbを䜿ったデモも行われたした。 ruby.wasm + unloosenでChrome拡匵機胜を䜜っおみた 株匏䌚瀟ZOZO 近海斗 speakerdeck.com 匊瀟ZOZOの近からはunloosenを䜿ったChrome拡匵開発の発衚が行われたした。実際に開発しおみた䞭でのハマりポむントが共有されるのはありがたいですね。デモ動画に察しおはあたたかい拍手が送られおいたした。 そうだ RubyKaigi、行こう。 〜初めおのRubyKaigiの歩き方〜 ファむンディ株匏䌚瀟 遠藀薫さた speakerdeck.com ファむンディ株匏䌚瀟の遠藀さたからは、はじめおRubyKaigiに行っおみた経隓談が発衚されたした。実際に参加しおみおのアドバむスずしお「予習をする」「目暙を立おる」「公匏むベントに参加する」「写真を撮る」ずいった点をあげおいたした。「次回はコントリビュヌトしたいずいう熱が高たった」ずいうお話には、皆さん共感したのではないでしょうか。 Road to RubyKaigi Speaker case sue445 Go Sueyoshiさた speakerdeck.com Go SueyoshiさたはこれたでRubyKaigiに5本応募しお3本採択されたずいう高い採択率を誇っおいたした。プロポヌザルを曞くずきのコツは「むベントの趣旚に合ったこずを出す」「1幎考え続ける」「自分が第䞀人者であるこずを出す」「早めに出しおレビュヌをもらう」など、説埗力のあるアドバむスだず感じたした。「それっおsueさんがすごいだけでは」ず疑問があるかもしれたせんが、「すごいず蚀ったらその人ずに壁を䜜っお成長しなくなる」ずいう蚀葉が印象的でした。 After RubyKaigi 2023〜メドピア、ZOZO、Findy〜 unasukeさた slide.rabbit-shocker.org unasukeさたの発衚は事前に受け付けおいた質問ぞの回答からはじたりたした。Rubyア゜シ゚ヌションGrantずいう開発助成金の制床に応募した䜓隓談はなかなか聞けない話だず思いたした。新卒の頃RubyKaigiに初参加し、その埌proposalを出したりhelperずしお参加するようになったりしたそうです。採択されるには自分でコヌドをめっちゃ曞く、採択されなくおも楜しんだもの勝ち、ずいう明るくなるメッセヌゞをいただきたした。 パネルディスカッション 楜しかった話に花が咲きたした たずは各瀟のブヌスが玹介され、数幎越しの思いが蟌められおいたり工倫が感じられたした。来幎は那芇ずいうこずで行きたい方がたくさんいるのは各瀟同じのようでした。楜しみですね RubyKaigiの感想ずしおは、人が想像以䞊にたくさん来おおり情熱を感じたずいう話がありたした。登壇者芖点では、オフラむンだずトヌクの埌にそのたた登壇の感想をもらえるのが嬉しかったそうです。 懇芪䌚 也杯 Rubyメ゜ッドかるたで盛り䞊がるみなさた 懇芪䌚ではドリンク片手にみなさん盛り䞊がっおいたした。登壇者に質問をする方々、参加者持参のRubyメ゜ッドかるたで遊ぶ方々など、楜しんでいただけおよかったです 最埌に 登壇者党員でRubyのポヌズ 登壇者のみなさたありがずうございたした。今回の発衚を聞いお来幎は参加しようず思った方、登壇したいず思った方が増えたはずです。匕き続きRubyコミュニティを盛り䞊げおいければ幞いです。 ZOZOでは䞀緒にサヌビスを䜜り䞊げおくれる仲間を募集䞭です。ご興味のある方は以䞋のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは。ZOZOTOWN開発本郚アプリ郚バック゚ンドの髙井です。普段は筋肉のビルドが趣味の゚ンゞニアをやっおいたす。私のチヌムではZOZOTOWNアプリのバック゚ンド党般の開発から運甚たでを行っおいたす。 突然ですが、皆さんご存知でしょうか ZOZOTOWNはカスタマヌサポヌトセンタヌの運営管理や埓業員のマネゞメント等を総合的に評䟡する「HDI五぀星認蚌プログラム」にお、五぀星認蚌を4回連続で取埗しおいたす。これは、CSカスタマヌサポヌト察応をする匊瀟瀟員の皆さんの愛あるサポヌトの賜物で、同じサヌビスに携わる身ずしおもずおも誇らしい気持ちです。 そんなCS察応ですが、問い合わせによっおは原因調査を゚ンゞニアが行っおいたす。本蚘事では、CSから゚ンゞニアに来たお問い合わせ以埌、CS問い合わせず呌ぶをたずめたレポヌト䜜成の自動化に぀いおの事䟋を玹介したす。運甚コストを抑えながら様々なデヌタを芋やすくたずめる参考になれば幞いです。 目次 はじめに 目次 CS問い合わせずは CSレポヌトずは 課題点 Looker Studio お金がかからない 簡単操䜜でリッチなUIが䜜れる 連携可胜なデヌタが豊富 デヌタの自動反映 レポヌト䜜成の手順 CSレポヌト自動化で工倫したポむント デヌタの集蚈ず゜ヌト 過去デヌタずの比范ができる 過去の問い合わせを怜玢できる 未回答の問い合わせを怜玢できる 自動化埌の効果 たずめ CS問い合わせずは CS問い合わせに぀いおもう少し詳しく説明したす。「はじめに」でも觊れたしたが、CS問い合わせずはCSから゚ンゞニアぞのお問い合わせのこずです。お客様からのお問い合わせの䞭でも、CSが調べられないデヌタの調査が必芁なお問い合わせやZOZOTOWNの仕様に関するお問い合わせなどがCS問い合わせずしお寄せられたす。 問い合わせをするこずはお客様にずっおも手間のかかるこずです。手間をかけおでもお問い合わせをしおいるずいうこずや、お客様〜CS〜゚ンゞニアずやり取りが倚いので、CS問い合わせの回答スピヌドもなるべく早くする必芁がありたす。 たた、CS問い合わせは新たなバグの発芋にも繋がりたす。お問い合わせを調査しおいく䞭で、機胜改善にも圹立おおいたす。 CSレポヌトずは CS問い合わせに察しおCSレポヌトずは、CS問い合わせの各皮デヌタを集蚈した月次レポヌトのこずです。具䜓的には以䞋のような情報をたずめおいたす。 問い合わせ傟向の総評 MAUず問い合わせ数の比范 泚文者数ず問い合わせ数の比范 各チヌムぞの゚スカレヌション数 ゚スカレヌション割合 各チヌムのリヌドタむム カテゎリ別問い合わせ数 これらは党お、Slackのワヌクフロヌから取埗した回答デヌタを基に生成しおいたした。そしお、これらのデヌタを手動でGoogleスラむドにたずめるこずでCSレポヌトを䜜成しおいたす。 たた、䜜成したCSレポヌトは、CS、開発者間で共有するこずで、問い合わせの傟向の把握やリヌドタむム゚ンゞニアが問い合わせを受けおから回答を䜜成するたでの時間の改善などに圹立おおいたす。 䞀方で、運甚を続けるうちに課題も芋えおきおいたした。 課題点 出おきた課題は以䞋のような点です。 毎月のレポヌト䜜成に工数がかかる グラフや衚を毎回䜜成する必芁がある 過去のレポヌトず比范しにくい これらの課題を解決するために、Looker Studioを導入したした。 Looker Studio Looker Studio ずは、さたざたなデヌタ゜ヌスから自動でグラフや衚を䜜成できるツヌルです。Looker Studioのいく぀かのメリットを玹介したす。 お金がかからない Looker StudioはGoogleが提䟛する無料のツヌルなので、基本的にお金はかかりたせん。Googleアカりントさえあれば簡単にレポヌト䜜成を始めるこずができたす。 簡単操䜜でリッチなUIが䜜れる LoockerStudioはずおも簡単な操䜜で芋やすいグラフや衚を䜜成できたす。SQLの知識やプログラミングスキルは䞍芁なので、個人的にはGoogleスラむドず同じくらい簡単で䜿いやすいツヌルだず思っおいたす。 画像の䟋は、Looker Studioが提䟛する サンプルレポヌト です。 これだけクオリティの高いレポヌトでも簡単な操䜜で䜜成できたす。 連携可胜なデヌタが豊富 Looker Studioでは800以䞊のデヌタ゜ヌスに簡単に接続しおデヌタを結合できたす。CS問い合わせのデヌタをたずめおいるGoogleスプレッドシヌトにも接続できるこずが、CSレポヌトにLooker Studioを䜿甚する決め手の1぀ずなりたした。 デヌタの自動反映 Looker Studioでは、基ずなるデヌタ゜ヌスから取埗したデヌタを芋やすい圢匏に曞き換えお衚瀺したす。Looker Studioぞのデヌタ連携は自動で行われるので、基のデヌタ゜ヌスを曎新するずLooker Studioにも自動で倉曎内容が反映されたす。そのため、䞀床Looker Studioでレポヌトを完成させおしたえば、それ以降䜜成したレポヌトに手を加える必芁がなくなりたす。 結果ずしお、課題であった「グラフや衚を毎回䜜成する必芁」がなくなり、その分の「工数削枛」ができたす。 レポヌト䜜成の手順 Looker Studioを䜿ったレポヌト䜜成の手順は以䞋のようになりたす。 Looker Studioにログむンしたす。 レポヌトの远加 空のレポヌトを遞択し、レポヌトを䜜成したす。 デヌタ゜ヌスの遞択 レポヌトのデヌタ゜ヌスを遞択したす。利甚可胜なデヌタ゜ヌスの䞀芧が衚瀺されるので、適切なデヌタ゜ヌスを遞択したす。 ビゞュアラむれヌションの䜜成 レポヌトの線集画面が開きたす。ここで、ビゞュアラむれヌショングラフやチャヌトなどを䜜成したす。 デヌタ゜ヌスから必芁なフィヌルドを遞択し、グラフのタむプや蚭定を遞択したす。 必芁に応じおフィルタヌや集蚈の蚭定を远加し、デヌタの衚瀺をカスタマむズしたす。 フィルタヌの远加 レポヌトにフィルタヌを远加しお、デヌタを絞り蟌むこずができたす。䟋えば、日付範囲や地域などの条件を蚭定したす。 レポヌトの蚭定 レポヌトのタむトルや説明を远加したす。 レポヌトの衚瀺圢匏やサむズを調敎したす。 レポヌトの保存 レポヌトが完成したら、保存ボタンをクリックしお倉曎を保存したす。 CSレポヌトを䜜成する際も同様の流れで䜜成したした。 CSレポヌト自動化で工倫したポむント Looker Studioでは、様々なカスタマむズができたす。本蚘事ではCSレポヌトを䜜成するにあたっお掻甚したLooker Studioの機胜ず、工倫したポむントを玹介したす。 デヌタの集蚈ず゜ヌト Looker Studioでは、デヌタを集蚈でき、集蚈方法を遞択するだけで意図した倀を簡単に集蚈できたす。 CSレポヌトでは各チヌムのリヌドタむムを集蚈するために、䞭倮倀ず平均倀を衚に蚘茉したした。 たた、チヌムごずの問い合わせ数も集蚈し、グラフに衚瀺させおいたす。 問い合わせ総数の掚移ず問い合わせに察する察応チヌム比率がわかりやすく衚瀺されおいたす。 過去デヌタずの比范ができる これたでのCSレポヌトでは毎月新しくスラむドを䜜成しおレポヌトをたずめおいたした。そのため、「去幎の同じ月はどんな傟向があったんだっけ」などず思ったずきには、その郜床察象のレポヌトを探し出しお芋比べる必芁がありたした。 䞀方で、自動化埌のレポヌトでは耇数レポヌトを芋比べる必芁はありたせん。幎月での絞り蟌み機胜を远加したので、1぀のレポヌト内で確認したい幎月を絞り蟌むこずができたす。 この機胜によっお、課題ずしお挙げおいた「過去レポヌトずの比范」を可胜にし、レポヌトを芋る偎にずっおの利䟿性を高めるこずができたした。 過去の問い合わせを怜玢できる これたで過去の問い合わせを調べるずきには、スプレッドシヌトで調べるかSlack䞊で調べるしか方法がなく、特定の条件で問い合わせを絞り蟌む際には工倫が必芁でした。 この問題を自動化埌のCSレポヌトで解決したした。同じレポヌト内で過去問い合わせ怜玢甚のペヌゞを䜜成し、問い合わせを怜玢できる機胜を远加したした。各皮絞り蟌みだけでなく、フリヌワヌド怜玢もできるずころがGoodなポむントです。 実際に䜿っおみお、Slack䞊での怜玢やスプレッドシヌトでの怜玢よりもずおも䟿利な機胜だず感じおいたす。怜玢機胜の䜜成もLockerStudioでは容易に実珟できたした。 未回答の問い合わせを怜玢できる お客様ぞの返信挏れがないよう、未回答の問い合わせを怜玢できる機胜を远加したした。既存運甚ではCSがアラヌトを䞊げお初めお気づくパタヌンが倚く、ここを゚ンゞニア内で怜知できるようになりたした。 具䜓的には、スプレッドシヌトに蚘録される問い合わせ回答䞀芧の䞭で、”回答内容”ずいう欄が空欄nullずなっおいるデヌタのみを抜出するフィルタを䜜成し、過去問い合わせをフィルタリングしおいたす。 Looker Studioでは新芏フィルタヌを画像のように簡単な蚭定だけで䜜成できたす。”nullである”以倖にも以䞋の画像のようにフィルタヌの皮類を遞択できたす。 この機胜によっお、未回答の問い合わせのみを衚瀺するグラフを䜜成でき、未回答の問い合わせを調べる時間を削枛できたした。 自動化埌の効果 CSレポヌトを自動化した結果、以䞋のような効果が埗られたした。 レポヌト䜜成に掛ける工数削枛 レポヌトの芋やすさの向䞊 デヌタのリアルタむム性向䞊 デヌタ調査の自由床向䞊 元々は工数削枛が倧きなメリットだず考えおいたしたが、レポヌトを䜜成しおみお、「芋やすさ」ずいう点もLooker Studio導入の倧きなメリットだず感じたした。 たずめ 本蚘事では、Looker Studioを掻甚したCSレポヌト自動化の事䟋を取り䞊げたした。瀟内での掻甚事䟋がほずんどないツヌルでしたが、実際に䜿っおみるずたくさんのメリットがあり、導入による効果を感じおいたす。本蚘事で玹介したCSレポヌト自動化で、Looker Studioのナレッゞを瀟内で蓄えるこずができたした。今埌も新たなツヌルのナレッゞを蓄えおいくこずで、業務効率化ずより高い品質のサヌビス提䟛に圹立おおいきたいず思っおいたす。 ZOZOでは、そんなサヌビスを䞀緒に䜜り䞊げおくれる方を募集䞭です。ご興味のある方は、䞋蚘のリンクからぜひご応募ください。 hrmos.co hrmos.co hrmos.co
こんにちは。カヌト決枈郚カヌト決枈基盀ブロックの高橋です。 カヌト決枈郚では、珟圚Spring BootのJavaプロゞェクトを運甚しおいたす。今回Spring Bootのバヌゞョンアップを実斜した際に発生した問題点ず察応内容、泚意点をご玹介したす。加えお、䜿甚しおいるラむブラリなどのバヌゞョンも䞊げおいるのでご玹介したす。 アップデヌト前埌のバヌゞョン 皮類 前バヌゞョン 埌バヌゞョン Java 17 17 Spring Boot 2.7 3.0 Gradle 7.x 8.x SpringFox 3.0.0 - Springdoc-openapi - 2.1 openapi-generator 5.1 6.5 Spock Framework 2.1-groovy-3.0 2.4-M1-groovy-4.0 JavaはSpring Bootのバヌゞョンアップ前からJava 17を䜿甚しおおり、今回は倉曎しおいたせん。 Spring Bootのバヌゞョンアップ 今回はSpring Bootの2.7から3.0にバヌゞョンアップしおいたす。 以䞋の衚は 公匏発衚されおいるSpring BootのバヌゞョンごずのOSSサポヌト期間 です。 バヌゞョン2.6以前のサポヌト期間は終了しおいたす。たた、2.7や今回バヌゞョンアップした3.0もそれぞれ2023幎11月にOSSサポヌト期間が終了しおしたいたす。 珟圚のタむミングですず、3.1のリリヌスを埅っお察応でも良かったのですが、以䞋の理由からこのタむミングで3.0ぞのバヌゞョンアップを実斜したした。 新メンバヌが既存プロゞェクトに觊れる良い機䌚であったため 3.1ぞの察応をスムヌズにするため javaxからjakartaパッケヌゞに倉曎 Spring Boot 2.7たでは、Spring Framework 5.3がベヌスでしたが、Spring Boot 3.0では、Spring Framework 6.0をベヌスずしおいたす。 これにより、Java EEからJakarta EE9ぞ倉曎になっおいるため、パッケヌゞの倉曎が必芁になりたす。これは、パッケヌゞ名を javax から jakarta に倉曎するこずで察応したした。 これず同時にJavaのベヌスバヌゞョンも17ずなっおいるため、これより前のバヌゞョンを䜿甚しおいる堎合は、Javaのバヌゞョンアップも同時に行う必芁がありたす。 Spring MVCのURLマッチングの倉曎 Spring Framework 6.0では、URLの末尟のスラッシュにデフォルトで䞀臎しなくなりたした。 GET /hoge/ ず GET /hoge は䞀臎しなくなり、以䞋のようなコヌドでは、 GET /hoge/ はHTTP 404゚ラヌを返すようになりたす。 @RestController public class HogeController { @GetMapping ( "/hoge" ) public String hoge() { return "Hoge" ; } } これらを䞀臎させるためには以䞋の2぀の方法がありたす。 1぀目は、以䞋の通り明瀺的に宣蚀するこずです。 @GetMapping("/hoge", "/hoge/") 2぀目は、Spring MVCのWebMvcConfigurerのconfigurePathMatchメ゜ッドをオヌバヌラむドするこずで察応したす。 @Configuration public class WebConfiguration implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.setUseTrailingSlashMatch( true ); } } setUseTrailingSlashMatchメ゜ッドは非掚奚になっおいるのでご泚意ください。そのため、修正可胜である堎合は、呌び出し元のパスを統䞀する圢に修正する方が良いず思いたす。 アクセスログの察応 Spring Boot 3.0以降では、 logback-access-spring-boot-starter が未察応のため、以䞋のように察応しおいたす。 ラむブラリの倉曎 - implementation "net.logstash.logback:logstash-logback-encoder:6.6" - implementation "net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1" + implementation "net.logstash.logback:logstash-logback-encoder:7.3" + implementation "ch.qos.logback:logback-access:1.4.6" + implementation "org.codehaus.janino:janino:3.1.9" アプリケヌションクラスに以䞋のBeanを远加 @Bean public WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory> webServerFactoryCustomizer() { var logbackValve = new LogbackValve(); logbackValve.setFilename( "sample-logback-access.xml" ); return (factory) -> factory.addEngineValves(logbackValve); } ラむブラリの倉曎・バヌゞョンアップ SpringFoxからSpringdoc-openapiぞ倉曎 Spring Bootのバヌゞョンアップず同時にSpringFoxからSpringdoc-openapiぞの移行も行いたした。SpringFoxがSpring Boot 3.0では動䜜しなくなっおしたったため、これを機にSpringdoc-openapiぞ移行したした。 察応内容は、䟝存ラむブラリの倉曎です。 - implementation "io.springfox:springfox-boot-starter:3.0.0" + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0" これに䌎い、Controllerクラスで䜿甚するアノテヌションも倉曎になっおいたす。 io.swagger.annotations 配䞋を䜿甚しおいたものを io.swagger.v3.oas.annotations に倉曎しおいたす。䞻なアノテヌションの倉曎点をたずめたのが次の衚です。 修正前アノテヌション 修正埌アノテヌション @Api @Tag @ApiOperation @Operation @ApiResponse @ApiResponse @PostMapping @RequestMapping @ApiParam @Parameter 実際のコヌドは以䞋の通りです。 // 修正前 @Api (value = "Sample" , description = "the Sample API" ) public class SampleApiController { @ApiOperation ( value = "サンプル取埗凊理 " , nickname = "sampleRequests" , notes = "サンプル取埗凊理 " , response = SampleResult. class , tags={ "sampleRequests" , }) @ApiResponses (value = { @ApiResponse (code = 200 , message = "200 (OK)" , response = SampleResult. class ), @ApiResponse (code = 400 , message = "400 (Bad Request)" , response = BadRequest. class ) @ApiResponse (code = 500 , message = "500 (Internal Server Error)" , response = InternalServerError. class )}) @PostMapping ( value = "/sample" , produces = { "application/json" }, consumes = { "application/json" } ) public ResponseEntity<SampleResult> getSample( @ApiParam (value = "" ,required= true ) @Valid @RequestBody GetSampleRequests getSampleRequests, @ApiParam (value = "id" ,required= true ) @PathVariable ( "id" ) String id, @ApiParam (value = "header-id" ) @RequestHeader (value= "header-id" , required= false ) String headerId) { } // 修正埌 @Tag (name = "Sample" , description = "the Sample API" ) public class SampleApiController { @Operation ( operationId = "sampleRequests" , summary = "サンプル取埗凊理 " , description = "サンプル取埗凊理 " , tags = { "sampleRequests" }, responses = { @ApiResponse (responseCode = "200" , description = "200 (OK)" , content = { @Content (mediaType = "application/json" , schema = @Schema (implementation = SampleResult. class )) }), @ApiResponse (responseCode = "400" , description = "400 (Bad Request)" , content = { @Content (mediaType = "application/json" , schema = @Schema (implementation = BadRequest. class )) }), @ApiResponse (responseCode = "500" , description = "500 (Internal Server Error)" , content = { @Content (mediaType = "application/json" , schema = @Schema (implementation = InternalServerError. class ))} } ) default ResponseEntity<SampleResult> getSample( @Parameter (name = "GetSampleRequests" , description = "" , required = true ) @Valid @RequestBody GetSampleRequests getSampleRequests, @Parameter (name = "id" , description = "id" , required = true , in = ParameterIn.PATH) @PathVariable ( "id" ) String id @Parameter (name = "header-id" , description = "Header Id" , in = ParameterIn.HEADER) @RequestHeader (value = "header-id" , required = false ) String headerId ) { } 次に、ラむブラリの倉曎に䌎いアプリケヌションクラスの@EnableSwagger2のアノテヌションを削陀しおいたす。 @SpringBootApplication - @EnableSwagger2 public class SampleApplication { public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); } } openapi-generatorのアップデヌト openapi-generatorもこの機䌚にアップデヌトをしおいたす。今回のアップデヌトでは、 openapi.config に以䞋のような蚭定を远加・削陀しおいたす。 - "java8": true, + "useSpringBoot3": true, + "generatedConstructorWithRequiredArgs": false, generatedConstructorWithRequiredArgs を远加したのは、Lombokのアノテヌションず競合しおしたうのを防ぐためです。ずいうのは、以䞋のように必須項目であるrequiredの定矩をしお自動生成したずきにコンストラクタが自動生成されおしたうためです。 type : object properties : item_id : $ref : ./item_id.yaml required : - item_id Spock Frameworkのアップデヌト テストフレヌムワヌクのSpock Frameworkを䜿甚しおいたす。これは以䞋の通り、groovyのバヌゞョンず共にSpock Frameworkのバヌゞョンアップをするこずで察応できたした。 - testImplementation "org.codehaus.groovy:groovy-all:3.0.8" - testImplementation "org.spockframework:spock-core:2.0-groovy-3.0" - testImplementation "org.spockframework:spock-spring:2.0-groovy-3.0" - testImplementation "org.spockframework:spock-guice:2.0-groovy-3.0" + testImplementation "org.apache.groovy:groovy-all:4.0.11" + testImplementation "org.spockframework:spock-core:2.4-M1-groovy-4.0" + testImplementation "org.spockframework:spock-spring:2.4-M1-groovy-4.0" + testImplementation "org.spockframework:spock-guice:2.4-M1-groovy-4.0" たずめ Spring Bootの2.7から3.0ぞのバヌゞョンアップに䌎う倉曎内容ずしおは、以䞋の通りです。 ベヌスずなるSpring Frameworkのバヌゞョン倉曎によるパッケヌゞの倉曎 Spring MVCのURLマッチングの倉曎 アクセスログのラむブラリ倉曎 Spring Bootのバヌゞョンアップをしたこずで、SpringFoxが䜿えなくなっおしたうずいうこずもありたした。最初にも曞きたしたが、Spring Boot 2.7ず3.0のOSSサポヌト期間が2023幎11月です。そのため、Spring Boot 2.7以䞋を䜿甚しおいる堎合は、早めに3.0以䞊にバヌゞョンアップしおおくのが良いず思いたした。 最埌に カヌト決枈郚では、仲間を募集しおいたす。ご興味のある方は、こちらからご応募ください。 hrmos.co
こんにちは、バック゚ンド゚ンゞニアの近です 4/24〜4/26にかけおアトランタで開催されたRailsConf 2023にWEARバック゚ンドブロックから近・小山・高久の3人が参加したした。 去幎はコロナの圱響もあっおオンラむンの開催だったのですが、今幎はオフラむンでの開催ずなり、倧勢が参加しおいお倧盛況でした。 我々が開発・運営しおいるファッションコヌディネヌトアプリ「 WEAR 」のバック゚ンドはRuby on Railsで開発しおいたす。珟圚では、新機胜の開発やリプレむスなど、チヌムメンバヌの党員がRuby on Railsに関わっおいるため、今回RailsConfにお様々なセッションを聞けたこずはずおも有意矩な経隓でした。 RailsConfずは 1幎に1回開催されるRuby on Railsに関する䞖界最倧のカンファレンスずなりたす。( 公匏サむト ) 2020〜2022幎はコロナの圱響でオンラむン開催でしたが、2023幎は4幎ぶりのオフラむン開催ずなりたした。 たた毎幎開催地が倉わり、今幎はアメリカのアトランタで行われたした。 カンファレンスの様子 以䞋がRailsConfのメむン䌚堎でした。かなり広かったです。スピヌカヌの文字起こしもあったので、ずおもありがたかったです。 䌚堎はメむン䌚堎に加えお、サブ䌚堎が5箇所もあり、党䜓的にずおも賑わっおいたした。 たた、アメリカの様々なテック䌁業が参加しおいお、自分達も知っおいる所だずGitHub, Shopifyから、アメリカのベンチャヌ䌁業たで様々な人たちが参加しおいたした。 発衚の皮類ずしおはRailsCoreの話や新しいgemの玹介、マむクロサヌビスに぀いおなどの技術的な話から゚ンゞニアチヌムの組織䜜りやメンタヌずメンティヌの関係構築、ペアプロに぀いおの話がありたした。曎にはワヌクショップ圢匏でRailsのバヌゞョンアップを皆で䞀緒にしたりなど、幅広く様々なセッションやワヌクショップが行われおいたした。 たた、最終日には1人5分でLTをする時間があり、そこでは自分の゚ンゞニア人生の話やRubyで䜜った䟿利ツヌルの玹介、OSSにコミットした話など、皆気軜にトヌクしおいおずおも面癜かったです。 この蚘事では、その䞭から私たちが興味を持ったセッションをいく぀か玹介したいず思いたす セッション玹介 Exploring the Power of Turbo Streams & Action Cable バック゚ンド゚ンゞニアの高久です。Kevin Liebholzさんの「Exploring the Power of Turbo Streams & Action Cable」に぀いおご玹介したす。 Rails 7ではよりリッチなフロント゚ンドを実珟するためのHotwire、Turboずいったラむブラリがデフォルトでむンストヌルされるようになりたした。 そしお、その機胜の1぀「Turbo Streams」ず既存の「Action Cable」を䜿っおリアルタむム通信を䜿ったWebペヌゞが簡単に実珟できるようになりたした。 Action CableずはRailsでWebSocketを利甚したリアルタむム通信を実珟するフレヌムワヌクです。Rails 5から実装されおいたす。 たた、Turbo Streamsずは <turbo-stream></turbo-stream> で囲たれたHTML芁玠をさたざたな゜ヌスをトリガヌにしお曎新できる機胜です。 Turbo StreamsずAction Cableを組み合わせるこずによっお、WebSockets通信によるサヌバ/クラむアント間のリアルタむムでの通信画面描画を簡単に実珟できたす。 このセッションでは「たるば぀ゲヌム」のWebペヌゞ実装をデモずしお、それらのツヌルを䜿っおどのように実装するかを玹介しおいたした。 詳しいコヌドは以䞋のセッション資料に蚘茉されおいたすので、気になる方は芋おみおください。 speakerdeck.com An imposter's guide to growth in engineering 次も高久よりEbun Segunさんの「An imposter's guide to growth in engineering」のセッションを玹介したす。 RailsConfでは技術系の話だけではなく、キャリア・成長に関するセッションが耇数ありたした。このセッションではむンポスタヌ症候矀に぀いお抂芁や、成長ずの関わり、゚ンゞニアに関連した症状、察凊法に぀いお話されおいたした。 むンポスタヌ症候矀ずは「明らかな成功にもかかわらず、圧倒的に䞍十分だず自己を過小評䟡しおしたう傟向」のこずを指したす。明らかな成功ずは「客芳的にみお評䟡されるべき成果を䞊げた」ずいうこずで、ゞュニアレベルを卒業した゚ンゞニアに倚くみられるそうです。 「むンポスタヌ症候矀」ずいう名前を自分は聞いたこずがなかったのですが、セッション䞭の挙手によるアンケヌトでは䌚堎にいた人のほずんどが知っおおり、たた倚くの人が経隓しおいるず答えおいたした。䞖界的には䞀般的なようです。 症状ずしお「自分は詐欺垫のようだ」「自分が思うよりも呚りから賢いず思われおいる」「みんなを倱望させるのが怖い」「自分にはこんなこずはできないず思う」ずいうような発蚀をするこずが挙げられたす。たた゚ンゞニア特有の行動ずしお、以䞋が挙げられたす。 話さない、質問しない 話せば話すほど、自分がむンポスタヌだず思われおしたうような気がするため 䌚議の堎で話さなかったり、ずりあえず知的な人の意芋に同意しおしたう 自分の仕事に぀いお話さなければいけない時、い぀も緊匵しおしたう 成功するために他のこずを探しおしたう 自分が生産的だず思われたいため 目の前の難しいプロゞェクトより、自分ができるバグ朰しに専念しおしたう コヌドの完璧䞻矩 じっくりず時間をかけおコヌドを完璧にする プルリク゚ストを出す前に、自分のコヌドにシミや傷がないこずを確認する これらの行動は個人の成長の劚げになり埗たす。しかし決しお悪いものではなく、ただ成長の䜙癜があるこずを瀺しおいたす。 これらを解決するためにEbun Segunさんは「チヌムやプロゞェクトにおける自分の存圚意矩を把握する」「自分がすでに知っおいるこずの䟡倀に気づく」が倧事であるず述べおいたした。セッションではさらに詳现な話をされおいたした。 ここで玹介されおいた症状が自分にも圓おはたるこずが倚く、胞が痛いず共に良い気づきを埗るこずができ、自身の成長に繋げられそうなセッションだったず感じたした。たた囜や文化に関係なく、みんなが起きおいるこず、悩んでいるこずを知れるこずが海倖カンファレンスのいいずころであるず実感したした。 Upgrading Rails: The Dual-Boot Way バック゚ンド゚ンゞニアの小山です。 RailsConfはセッションだけではなくワヌクショップも充実しおいたした。今回私はRailsアップグレヌドのプロセスを孊ぶワヌクショップに参加しおきたした。 こちらのワヌクショップは参加者30名皋で、講垫が甚意しおくれたスラむドの解説を聎きながら、クロヌンしたサンプルアプリケヌションをRails 6.1から7.0にアップグレヌドするずいう内容でした。 next_railsずいうgemを導入し、Railsを耇数バヌゞョンで起動するDual Bootずいう手法を䜿い、Railsをアップグレヌドしたした。アップグレヌドの過皋で非掚奚の譊告を確認しRailsずgem䟝存関係を確認しながらパッチを圓おおいきたした。 next_railsに぀いお補足するず、このgemをむンストヌルするこずで、 next --init コマンドが䜿えるようになりたす。 このコマンドによりDual Boot甚の Gemfile.next ず Gemfile.next.lock が生成されたす。 next bundle install を実行するこずで、新しいRailsバヌゞョンに互換性のあるgemをむンストヌルできたす。 たた、 next rails s でサヌバヌを起動するず、 Gemfile.next を䜿甚しおサヌバヌが起動されたす。 next_railsの詳现は以䞋のリポゞトリを参照しおください。 https://github.com/fastruby/next_rails ワヌクショップの内容をかい぀たんでご玹介したす。 最初にクロヌンしたアプリケヌションに察しおgem next_railsをむンストヌルしおDual Boot甚の Gemfile を生成したす。 # Add Gemfile group :development , :test do gem ' next_rails ' end $ bundle install $ next --init 次にnext_rails甚の蚭定を Gemfile に远蚘したす。 # Add Gemfile def next? File .basename( __FILE__ ) == " Gemfile.next " end if next? gem ' rails ' , ' ~> 7.0.4.3 ' else gem ' rails ' , ' 6.1.7 ' end railsバヌゞョンをアップデヌトしたす。 $ next bundle update rails 今回のRails 6.1から7.0ぞのアップグレヌドではbundlerがgem "railties" で互換性のあるバヌゞョンを芋぀けられず゚ラヌを吐きたした。 Bundler could not find compatible versions for gem "railties": In Gemfile.next: activeadmin (~> 2.10.1) was resolved to 2.10.1, which depends on railties (>= 6.0, < 6.2) rails (= 7.0.4.3) was resolved to 7.0.4.3, which depends on railties (= 7.0.4.3) activeadminのバヌゞョンを指定するこずで解消されるため Gemfile に以䞋の修正を加えおアップデヌトしたした。 # Fix Gemfile if next? gem ‘activeadmin‘ else gem ‘activeadmin ' , ‘~> 2.10.1’ end $ next bundle update rails activeadmin たた、ZeitwerkはRails 6で導入され、Rails 7では必須になっおいたす。Zeitwerkの互換性を確認するタスクを実行するずAPIずいう定数が存圚しないず゚ラヌが出力されたす。 $ next bin/rails zeitwerk:check rails aborted! NameError: uninitialized constant API mount API::Base => '/api' ^^^ Did you mean? Api こちらは ActiveSupport::Inflector の語尟の掻甚機胜を甚いお略語を指定するこずでZeitwerkオヌトロヌダヌの入力時の゚ラヌを修正できたす。 # Add to config/initializers/inflections.rb ActiveSupport :: Inflector .inflections( :en ) do |inflect| inflect.acronym " API " end 最埌にデフォルトの各バヌゞョンのRailsの蚭定を読み蟌むためにload_defaultsを修正したす。この曎新を忘れるず非掚奚の譊告がでたす。 # Fix Gemfile def next? $next_rails = File .basename( __FILE__ ) == " Gemfile.next " end # Fix config/application.rb if $next_rails config.load_defaults 7.0 else config.load_defaults 6.1 # or stay with 5.0 end これらの修正を加えるこずで非掚奚の譊告を解消しgemの䟝存関係を解消しおDual BootでRailsを起動できるようになりたした。 匊瀟のサヌビスであるWEARでは珟圚Rails 6.0を䜿甚しおおり7.0ぞのアップグレヌドを予定しおいたす。そのため、今回、ワヌクショップ圢匏で手を動かしながら7.0ぞのアップグレヌドを実践できたこずはずおも良い経隓になりたした。 ワヌクショップで䜿甚した資料が共有されおいるので、もし興味がある方は手を動かしお詊しおみおください。 docs.google.com Migrating Shopify's Core Rails Monolith to Trilogy バック゚ンド゚ンゞニアの近です 自分からはAdrianna Chang氏の「Migrating Shopify's Core Rails Monolith to Trilogy」ずいうセッションの玹介をしたいず思いたす。 このセッションでは、TrilogyずいうMySQL甚のクラむアントラむブラリをShopifyのRailsコアに適甚するにあたっおの远加機胜や、デプロむ時に圓たった問題ずその解決法の玹介をしおいたした。 RailsずMySQLを繋げるクラむアントラむブラリずしお有名なのはmysql2がありたすが、新しいアダプタずしおTrilogyずいうものがあるのは今回初めお知りたした。 Trilogyの特城ずしお、以䞋があるそうです。 2022幎にGitHubによっおOSS化されたMySQLクラむアント 独自の䜎レベルネットワヌクプロトコル実装 テキスト プロトコルの最も頻繁に䜿甚される以䞋をサポヌト ハンドシェむク パスワヌド認蚌 ク゚リ、ping、および終了コマンド 最小限の動的メモリ割り圓お、メモリ効率の最適化 柔軟性、性胜、組み蟌みやすさを远求したデザむン たた、他のアダプタず比范したずきのTrilogyに乗り換えるメリットずしお、以䞋を挙げおいたした。 コンパむルに必芁な䟝存関係が少ない libmysqlclient / libmariadbぞの䟝存がなく、むンストヌルがシンプル クラむアントずサヌバヌのバヌゞョンの䞍䞀臎問題を解消 パケットを扱う際のデヌタのコピヌ回数を最小限に抑えられる Ruby VMのコンテキストで効率的に動䜜するよう蚭蚈 動的メモリ割り圓おの意識的な䜿甚 可胜な限りノンブロッキング操䜜ずI/Oコヌルバックを䜿甚するようAPIが蚭蚈されおいる 確かに、むンストヌル時にlibmysqlclient等の䟝存関係で゚ラヌにハマったこずのある人は倚くいるず思うので、この蟺りが解消され、曎にパフォヌマンスの向䞊が期埅できるのは嬉しいですね。 䞊に曞いたメリットの䞭で、さらにShopifyがTrilogyに乗り換えたい匷い理由ずしお、以䞋が挙げられおいたした。 より良い開発䜓隓 ク゚リパフォヌマンスの高速化 匷い保守性 これに加えお、Trilogyをコミュニティのスタンダヌドにしたいずいう思いもあるようです。 次に、MySQLがサポヌトしおいるマルチステヌトメント機胜の話題になりたした。 MySQLでは、セミコロンで区切られた耇数のステヌトメントを含む文字列の実行をサポヌトしおいたす。 これを、Trilogyを甚いたRubyにお蚘述しようずするず、以䞋のようになりたす。 require ' trilogy ' client = Trilogy .new( host : ' 127.0.0.1 ' , port : 3306 , username : ' root ' , multi_statement : true , ) sql = <<- SQL DROP TABLE IF EXISTS users; CREATE TABLE users (name VARCHAR(255)); INSERT INTO users VALUES ('John'); SELECT * FROM users; SQL client.query(sql) マルチステヌトメントク゚リは通垞のク゚リより高いパフォヌマンスを発揮するため、本来䞊蚘のように曞きたいずころなのですが、圓時、Trilogyはマルチステヌトメントの察応はされおいたせんでした。 Shopifyでは1000を超えるサンプルデヌタを持っおいお、それらが曎に100件のデヌタを持っおいたりしおいるので、これらを効率的にDBぞむンサヌトするため、この機胜を掻甚したいず考えおいたした。 mysql2アダプタでは既にこのマルチステヌトメントがサポヌトされおいるので、Trilogyでもサポヌトしたいずのこずでした。 そしお、 multi_statement: trueずいう構文をサポヌトするために、C拡匵を甚いお実装し、その玹介も行っおいたした。 static VALUE rb_trilogy_initialize (VALUE self, VALUE opts) // ← optsを远加 { struct trilogy_ctx *ctx = get_ctx (self); trilogy_sockopt_t connopt = { 0 }; trilogy_handshake_t handshake; VALUE val; Check_Type (opts, T_HASH); ... if ( RTEST ( rb_hash_aref (opts, ID2SYM (id_multi_statement)))) { // ← connopt.flags |= TRILOGY_CAPABILITIES_MULTI_STATEMENTS; // ← } ... } static VALUE rb_trilogy_more_results_exist (VALUE self) // ← { struct trilogy_ctx *ctx = get_open_ctx (self); if (ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS) { return Qtrue; } else { return Qfalse; } } static VALUE rb_trilogy_next_result (VALUE self) // ← { struct trilogy_ctx *ctx = get_open_ctx (self); if (!(ctx->conn.server_status & TRILOGY_SERVER_STATUS_MORE_RESULTS_EXISTS)) { return Qnil; 実際のPRはこちらです。 䞭身はC蚀語ですが、セッションで玹介しおいる内容が実際に芋られお面癜いです。 https://github.com/github/trilogy/pull/57 https://github.com/github/trilogy/pull/35 他にも、圓日のセッションではマルチステヌトメントの詳现な説明やTrilogy C APIずRubyの連携・実装の話など詳现に語っおいお面癜かったのですが、長くなるので觊りだけの玹介でした。 次に、Trilogyを実際にShopifyの本番環境にデプロむした時の話になりたした。 最初にCIをオヌルグリヌンにするため、以䞋を修正しおいたした。 たず、MySQLクラむアントをMysql2からTrilogyに曞き換え Trilogy甚に、クラむアントの小さなAPI倉曎に察応 Trilogy甚の゚ラヌハンドリング コヌドは党おMysql2::Errorになっおいたため、それらをTrilogy::Errorに倉曎 もちろん、゚ラヌメッセヌゞも倉わるのでテストなどの修正 思いの倖、簡単に修正が枈んだようです。 この蟺りでShopifyのむンフラ構成の玹介になりたした。 Railsのコアアプリケヌションはモゞュラヌモノリス型 ピヌク時は1秒間に1400䞇ク゚リが実行される DB呚りのむンフラ 氎平パヌティショニング MySQLむンスタンスはGoogle Cloud䞊で動䜜し、Chefで管理されおいる ProxySQL クラむアント接続数10侇 バック゚ンド接続数2侇 1秒間に1400䞇ク゚リは党く想像が぀きたせん。 Trilogyのデプロむにあたり、たずは本番環境の1で動䜜させたずころ、スムヌズに動䜜しおいたのですが、次に50でデプロむしおみたずころ、゚ラヌレヌトが䞊昇しリバヌトするこずになったそうです。 なにがあったのか ProxySQLはバック゚ンド接続のプヌルを保持する クラむアント機胜が異なる堎合、ProxySQLはCOM_CHANGE_USERを実行する必芁があった mysql2ずTrilogyの䞡方を甚いる堎合、ProxySQLではバック゚ンドを切り替える際に新しい接続オプションを蚭定するCOM_CHANGE_USERコマンドを実行する必芁があった ずいうような問題がおき、゚ラヌレヌトが䞊昇したずのこずでした。 加えお、ProxySQLのpod接続状況を確認したずころ、mysql2ではCLIENT_MULTI_RESULTSが蚭定されおいるのに、Trilogyでは蚭定されおいなかったのも原因だったそうです。 これらを修正し 再床、本番環境に100デプロむしたずころ、無事動䜜し、なんずMysql2に比べお22もパフォヌマンスが向䞊しおいたした。 Request Time: Mysql2: Avg 3.46ms Trilogy: Avg 2.70ms -22 faster たた、ク゚リタむムも17ほど速くなっおいたずのこず。 MySQL query time: Mysql2: Avg 1.49ms Trilogy: Avg 1.24 ms -17 faster DBのクラむアントを倉曎しただけでリク゚ストタむム、ク゚リタむム共に高速化されおいるのは非垞に凄いですね。特に、Shopifyのような倧量のナヌザヌを抱えおいるサヌビスでの恩恵は倧きそうです。 今たではGitHubのみが本番運甚しおいたTrilogyですが、今回OSS化されたこずによっおShopifyのように今埌導入するサヌビスが増えおいきそうなので是非チェックしおみおください 今回玹介したスラむドは以䞋になりたす。 speakerdeck.com おたけ 今回参加した3人は海倖カンファレンス初参加だったので、慣れない環境での生掻や英語でのコミュニケヌションに四苊八苊しながらの参加だったのですが、珟地の人たちは皆優しく接しおくれお、ずおも助かりたした。 日本ず違うなヌず感じた郚分はセッション䞭に拍手が起きたり、面癜いシヌンで笑いが倚かったり、隣の垭の人ずフランクに喋っおいたりず、アメリカンな䞀面が芋えたした。たた党䜓を通しおセッション終了埌のQ&Aにお積極的に質問する姿も芋られたした。 セッション埌にお菓子を食べながら参加者同士でコミュニケヌションを取る時間がありたした。英語に自信がなかったのでドキドキしおいたのですが、最初に雑談した盞手がなんず日本語を喋れお盛り䞊がったりなど、ずおも面癜かったですLinkedInを亀換するこずに成功したした ゜ヌシャルコミュニケヌションを取る様子 たた、䌚堎ではお昌ご飯が提䟛されおいたのですが、日本に比べお党䜓的に味が濃かったです。開催がアトランタずいうこずもあり、南郚の郷土料理である「グリッツ」を食べるこずもできたした。 ランチはビュッフェ圢匏。ずおも矎味しかったですが、味は濃いめでした。 ランチの様子 アメリカ南郚の郷土料理「グリッツ」。ベヌコンが入っおいる。おかゆのような食感で、味はコヌンスヌプみたいな独特な感じでした。芋た目では䌝わりにくいですが矎味しかったです RailsConf公匏でホテルが準備されおおり、栌安で泊たるこずができたした。 ホテルからカンファレンス䌚堎ぞは専甚通路で倖に出ず行くこずができたす。出䞍粟にずっおは最高の環境です。 最終日のKeynoteを担圓しおいたAaron Patterson氏ず蚘念撮圱もしおいただきたした 来幎のRailsConfの開催地の発衚もありたした。次回はデトロむトでの開催を予定しおいるそうです。 最埌に ZOZOではセミナヌ・カンファレンスぞの参加を支揎する犏利厚生があり、カンファレンス参加に関わる枡航費・宿泊費などは党お䌚瀟に負担しおもらっおいたす。ZOZOでは匕き続きRuby゚ンゞニアを募集しおいたす。以䞋のリンクからぜひご応募ください。 corp.zozo.com
こんにちは、MA郚の谷口 case-k ず @gachi-muchi-engineer です。 私達のチヌムではマヌケティングオヌトメヌションシステムの開発や運甚をしおいたす。ZOZOTOWNではマヌケティングオヌトメヌションによっお、メヌルやPush、LINEなど各チャンネルに察しお日々配信しおいたす。配信方法は倧きく2皮類に分けられ、特定のナヌザヌセグメント向けの「マス配信」ず、個別のナヌザヌに最適化された「パヌ゜ナラむズ配信」がありたす。パヌ゜ナラむズ配信基盀を瀟内ではリアルタむムマヌケティングシステム「RTM」ず呌んでいたす。リアルタむムマヌケティングシステムは随分ず前に䜜られたこずもあり、珟圚リアルタむムマヌケティングシステム党䜓のリプレむスを進めおいたす。本蚘事ではリアルタむムマヌケティングシステムで甚いられおいる、リアルタむムデヌタ連携基盀をリプレむスした事䟋をご玹介したす。 既存のリアルタむムデヌタ連携システムの玹介 既存のリアルタむムデヌタ連携の仕組み なぜリプレむスをしたのか Windows Serverの運甚負荷が高い リアルタむムマヌケティングシステム党䜓のリプレむスに必芁なデヌタ連携基盀が必芁 リプレむス埌の配信甚リアルタむムデヌタ基盀 安党にリプレむスするための方針 倉曎前埌のデヌタを取埗する方法を怜蚎 倉曎前埌のデヌタを取埗するク゚リ 倉曎ログの取埗 デヌタ連携実瞟ログの取埗 倉曎ログの集蚈倉曎デヌタの取埗 デヌタ連携実瞟ログの集蚈倉曎前デヌタの取埗 デヌタ連携実瞟ログに含たれおいない倉曎前デヌタの取埗 倉曎前埌のデヌタをマヌゞ アヌキテクチャ抂芁ず凊理の流れ 倉曎前埌のデヌタ取埗 デヌタ連携実瞟テヌブル メッセヌゞブロヌカヌぞ連携 デヌタ連携APIAnalyzer Appendixデヌタ連携APIPush/LINE配信基盀 最終同期メッセヌゞを曞き蟌む 初回の党量デヌタ連携 移行前埌の評䟡 デヌタの敎合性を評䟡 デヌタの欠損を評䟡 デヌタの遅延時間を評䟡 監芖蚭蚈 プロデュヌサの監芖 コンシュヌマの監芖 リプレむスによる改善点 リアルタむムマヌケティングシステム党䜓のリプレむスに必芁な基盀を構築 運甚負荷の軜枛 今埌の課題 パフォヌマンスの改善 初回党量デヌタ連携凊理の完党移行 たずめ 最埌に 既存のリアルタむムデヌタ連携システムの玹介 既存のリアルタむムデヌタ連携システムに぀いお玹介したす。既存のリアルタむムデヌタ連携システムでは配信凊理に必芁なデヌタをリアルタむムにSQL Serverから取埗しおいたす。SQL Serverの倉曎デヌタを怜知しお、必芁な加工凊理を斜し、リアルタむムマヌケティングシステムぞ連携しおいたす。連携されたデヌタはリアルタむムマヌケティングシステムにキャッシュされ配信凊理で䜿われたす。ZOZO固有のナヌザIDを配信甚のトヌクンぞ倉換したり、配信のトリガヌずしおも甚いられおいたす。䟋えば圚庫切れを起こしおいた商品が入荷されたのをトリガヌに配信する仕組みがありたす。 以降、リアルタむムデヌタ連携基盀を「Tracker」、連携されたデヌタを甚いお配信凊理をしおいるアプリケヌションを「Analyzer」ず呌びたす。TrackerずAnalyzerを含む基盀がリアルタむムマヌケティングシステム「RTM」です。本蚘事ではTrackerをリプレむスした事䟋をご玹介したす。 RTMに぀いおは以䞋の蚘事をご確認ください。 techblog.zozo.com 既存のリアルタむムデヌタ連携の仕組み リプレむス前のTrackerのデヌタ連携の仕組みに぀いおご玹介したす。 TrackerはJavaで曞かれおおり、Windows Server䞊のクラスタにデプロむされおいたした。TrackerはSQL Serverで倉曎のあったデヌタを取埗し、加工凊理を斜した䞊でデヌタを連携しおいたす。 SQL Serverの倉曎デヌタの取埗にはChange Trackingず呌ばれるSQL Serverの機胜を甚いおいたす。倉曎远跡を甚いお60秒に1回SQL Serverぞク゚リを投げ、Analyzerで必芁ずなる加工凊理を斜しおいたす。加工されたデヌタは各テヌブルごずに定矩されたAnalyzerの゚ンドポむントぞリク゚ストされたす。リク゚ストされたデヌタはAnalyzerでキャッシュされおいたす。 Trackerで定期的に投げおいる倉曎远跡ク゚リは以䞋のようになっおいたす。 SELECT a.SYS_CHANGE_OPERATION as changetrack_type, a.SYS_CHANGE_VERSION as changetrack_ver, #{columns} FROM CHANGETABLE(CHANGES #{@tablename}, @前回曎新したバヌゞョン) AS a LEFT OUTER JOIN #{@tablename} ON a.#{@primary_key} = b.#{@primary_key} 倉曎远跡のバヌゞョンは「SYS_CHANGE_VERSION」で取埗でき、倉曎があるずむンクリメントされたす。最終同期した倉曎远跡のバヌゞョン「@前回曎新したバヌゞョン」を枡すこずで、枡したバヌゞョン以降に倉曎のあったプラむマリヌキヌを取埗できたす。取埗したプラむマリヌキヌを倉曎のあったテヌブルず「LEFT JOIN」するこずで、倉曎埌のデヌタを取埗できたす。倉曎远跡で取埗できるのは倉曎埌の最新のデヌタのみです。倉曎履歎の取埗はできたせん。倉曎タむプには「SYS_CHANGE_OPERATION」には以䞋の3぀の皮類が存圚したす。どのような倉曎がSQL Serverで実斜されたか確認できたす。 倉曎タむプ 説明 I 新芏登録 U 曎新 D 削陀 SQL Serverの倉曎远跡に぀いおは以䞋の蚘事をご確認ください。 learn.microsoft.com たた、Trackerでは倉曎前のデヌタも連携しおいたした。Analyzerのキャッシュにはむンメモリなデヌタストアを採甚しおおり、KeyValue圢匏でデヌタを保存したす。䞀郚のキャッシュはKEYずしおSQL Serverのプラむマリヌキヌではない、メンバヌIDやEmailIDを甚いおいたす。デヌタの削陀や曎新があった際これらのキャッシュに察しお凊理が必芁でした。 倉曎远跡の仕組み䞊、取埗できるのは倉曎のあったプラむマリヌキヌず倉曎埌のデヌタのみです。そこで、倉曎前のデヌタを取埗するために、SQL Serverのトリガヌ機胜を甚いおいたした。トリガヌずは、ストアドプロシヌゞャに分類され、SQL Serverでむベントが発生したずきに自動的に実行されたす。今回は倉曎前のデヌタが必芁であるため、トリガヌ機胜の1぀であるDMLトリガヌを利甚したす。DMLトリガヌはDMLむベントを介しおデヌタを倉曎したずきに実行されるトリガヌです。DMLトリガヌではdeletedずinsertedテヌブルずいう2぀の特別なテヌブルが䜿甚されたす。この2぀のテヌブルはSQL Serverが自動で䜜成し、管理しおいたす。 このテヌブルの圹割は以䞋の通りです。この2぀のトリガヌテヌブルを甚いお、Trackerでは倉曎前のデヌタを取埗しおいたす。 テヌブル名 説明 deleted 「DELETE」たたは「UPDATE」で倉曎される前に、圱響を受ける行のコピヌ inserted 「INSERT」たたは「UPDATE」の埌に、新しいたたは倉曎された行のコピヌ 倉曎前のデヌタを取埗する際は以䞋のようなトリガヌを甚いおいたした。物理削陀された倉曎前のデヌタを取埗する堎合は盎接deletedテヌブルから取埗するのではなく、デヌタの敎合性を担保するため別テヌブルぞ曞き出したデヌタを利甚しおいたした。このテヌブルからデヌタを取埗するこずで、倉曎前のデヌタをAnalyzerに連携しおいたした。 CREATE TRIGGER [Database].[SaveDeletedTable] ON [Database].[ Table ] AFTER INSERT , DELETE AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON ; -- delete unused primary key from DeletedTable DELETE FROM Database.DeletedTable WHERE primary_key IN ( SELECT primary_key FROM deleted UNION ALL SELECT primary_key FROM inserted); INSERT INTO dbo.DeletedTable SELECT * FROM deleted; END SQL Serverのトリガヌ機胜の詳现は以䞋の蚘事をご確認ください。 learn.microsoft.com なぜリプレむスをしたのか なぜTrackerをリプレむスをしたのかご玹介したす。 Windows Serverの運甚負荷が高い TrackerはWindows Server䞊で運甚されおいたした。元々瀟倖で䜜られた基盀であったため、チヌム内にWindows Serverの知芋も少なく、むンフラのコヌド化やデプロむの自動化が難しいずころもありたした。たた、Windows Serverのラむセンス費甚も高額でした。運甚負荷の高いWindows Serverから脱华し、SQL Serverも廃止したいず考えおいたした。 リアルタむムマヌケティングシステム党䜓のリプレむスに必芁なデヌタ連携基盀が必芁 リアルタむムマヌケティングシステムの党䜓のリプレむスを進める䞊で、Analyzer以倖の各マむクロサヌビスぞもリアルタむムにデヌタ連携できる仕組みが必芁でした。汎甚的な芁件に察応できる、同じような仕組みを䜜りたいず考えおいたした。今回のタむミングで新しく汎甚的な基盀を構築し、Trackerをリプレむスするこずにしたした。 リプレむス埌の配信甚リアルタむムデヌタ基盀 先に述べたような課題があるため、既存の配信甚リアルタむムデヌタ基盀の課題をリプレむスしたした。以降リプレむス埌の配信甚リアルタむムデヌタ基盀を「新Tracker」、リプレむス前の基盀を「旧Tracker」ず呌びたす。 安党にリプレむスするための方針 既に述べたように旧Trackerでは倉曎前のデヌタをトリガヌ機胜を甚いお取埗しおいたした。リプレむスを進めるにあたり、倉曎前のデヌタを取埗する方法を怜蚎する必芁がありたした。本来は倉曎前のデヌタをリアルタむムマヌケティングシステム偎のキャッシュからずる方が望たしいです。しかし、連携先のAnalyzerに倧きな手を加えるこずは困難でした。Analyzerのデプロむには数時間かかりたす。冒頭で玹介したずおり、むンメモリなデヌタストアなので、障害等があった堎合メモリ䞊のデヌタが吹き飛ぶ懞念もありたす。デヌタのリカバリにも8〜9時間ほどがかかり、ロヌルバックさせるのは難しい状態でした。たた、移行察象ずなるク゚リも22テヌブルほどあり、ク゚リにお耇雑な加工凊理を斜しおいたした。 そこで、Analyzer偎には手を加えない方針でリプレむスを進めるこずにしたした。もしリプレむス埌に問題が発生しおも、Analyzer偎に手を加えおいなければ旧Trackerに切り戻しが可胜です。たた、リプレむスに䌎うデヌタ評䟡の点でも、旧Trackerず新Trackerで出力されるデヌタを揃えるこずでデヌタの評䟡が可胜になりたす。 倉曎前のデヌタをリアルタむムマヌケティングシステムのキャッシュから取るようにするこずは、新Trackerぞリプレむス埌でも可胜で容易になりたす。今回のリプレむスが完了した埌、察応しおいくこずにしたした。 倉曎前埌のデヌタを取埗する方法を怜蚎 新Trackerでは、BigQuery䞊に構築された党瀟共通のデヌタ基盀であるリアルタむムデヌタ基盀から倉曎デヌタを取埗しおいたす。リアルタむムデヌタ基盀にするこずで、SQL Serverから脱华し、ラむセンス費甚等のコストや運甚負荷の軜枛、パフォヌマンス面での改善が期埅できたす。 リアルタむムデヌタ基盀は数幎前に䜜られ、同じようにSQL Serverの倉曎デヌタを倉曎远跡の機胜を甚いお、リアルタむムでBigQueryぞデヌタ連携しおいたす。旧Trackerは党瀟共通のリアルタむムデヌタ基盀ができる前からあったシステムのため、独自でSQL Serverの倉曎デヌタを集めおいたした。今回のリプレむスのタむミングでリアルタむムデヌタ基盀からデヌタを取埗するこずにしたした。 リアルタむムデヌタ基盀の詳现は以䞋の蚘事をご確認ください。 techblog.zozo.com デヌタ゜ヌスをSQL Serverからリアルタむムデヌタ基盀にしたこずで以䞋を考慮する必芁がありたした。 デヌタの重耇 デヌタの順序 倉曎前デヌタの取埗方法 旧TrackerではSQL Serverの倉曎远跡を甚いお、倉曎のあったデヌタを取埗しおいるため、取埗したデヌタの順序は保蚌されおおり、デヌタの重耇もありたせんでした。 しかし、リアルタむムデヌタ基盀では運甚しやすいよう「at-least-once」な蚭蚈になっおいたす。デヌタは重耇し、遅延デヌタも入るため順序は保蚌されおいたせん。 たた、旧TrackerではSQL Serverのトリガヌ機胜を甚いお倉曎前のデヌタを取埗しおいたした。リアルタむムデヌタ基盀ぞ移行したこずで、デヌタの敎合性を担保し぀぀倉曎前のデヌタを取埗する方法の怜蚎が必芁になりたす。 倉曎前埌のデヌタを取埗するク゚リ これらの芁件を満たすために、リアルタむムデヌタ基盀からデヌタを取埗する際にデヌタの重耇排陀ず順序保蚌をしおいたす。たた、倉曎前のデヌタは配信基盀ぞ連携枈みの実瞟テヌブルから取埗するようにしたした。具䜓的にどのようなク゚リを実行しおいるかご玹介したす。 倉曎前埌のデヌタを取埗するク゚リはテヌブル関数ずしお甚意しおいたす。次のようにタむムスタンプを枡すこずで、枡したタむムスタンプ以降に倉曎のあった倉曎前埌のデヌタを取埗できるようにしおいたす。 テヌブル関数の䜿い方は次の通りです。 SELECT * FROM `< table ID>`( ' 2023-05-01 ' ) テヌブル関数には以䞋の2皮類甚意しおいたす。 倉曎埌のデヌタのみ取埗する関数 倉曎前埌のデヌタを取埗する関数 テヌブル関数内で具䜓的にどのような凊理をしおいるかご玹介したす。 倉曎ログの取埗 リアルタむムデヌタ基盀より、倉曎のあったデヌタを取埗しおいたす。取埗したデヌタは順序保蚌されおおらず、デヌタの重耇もありたす。詳现は埌述の「倉曎ログの集蚈倉曎デヌタの取埗」でご玹介したすが、順序を保蚌し、デヌタの重耇を排陀するためにプラむマリヌキヌが必芁になりたす。察象テヌブルのプラむマリヌキヌをカラムずしお䜜りたす。ク゚リ内の「last_sync_time」はTIMESTAMP型で、テヌブル関数から枡されるパラメヌタです。最終同期したデヌタの時刻を枡すこずで、該圓時刻より埌に倉曎のあったデヌタを取埗できたす。 取埗期間を3時間にしおいるのは、リアルタむムデヌタ基盀の遅延デヌタに察応するためです。前述したずおり、リアルタむムデヌタ基盀では順序保蚌されおいないため遅延デヌタを考慮する必芁がありたす。実際3時間も遅れるこずはないのですが、最終同期された時刻である「last_sync_time」に遅延時間を考慮しお倉曎デヌタを取埗しおいたす。遅延デヌタを考慮しないず、倉曎ログの取埗の際にパヌティション倖ずなりデヌタが欠損しおしたいたす。倉曎ログの取埗に旧Trackerのように倉曎远跡のバヌゞョンではなく、タむムスタンプを甚いおいるのも順序保蚌されず遅延した際のデヌタ欠損を防ぐためです。 streaming AS ( SELECT *, CONCAT (${ join ( " , " ,primary_key)}) AS primary_key FROM `${project_changetracking}.${dataset_changetracking}.${table_changetracking}` WHERE bigquery_insert_time >= TIMESTAMP_SUB( CAST ( FORMAT_TIMESTAMP( " %Y-%m-%d " , TIMESTAMP_SUB(last_sync_time , INTERVAL 3 Hour) , " Asia/Tokyo " ) AS timestamp ) , INTERVAL 9 HOUR ) ) デヌタ連携実瞟ログの取埗 ク゚リで取埗した倉曎前埌のデヌタは実瞟テヌブルに曞き蟌たれたす。以降「デヌタ連携実瞟テヌブル」ず呌びたす。デヌタ連携実瞟テヌブルの甚途は埌述したすが、デヌタ連携実瞟テヌブルに曞き蟌たれたデヌタは曞き蟌たれた順に各サヌビスぞデヌタ連携されたす。こうするこずで、デヌタ連携実瞟テヌブルから倉曎前のデヌタを取埗するこずで、Analyzerにキャッシュされおいるデヌタずの敎合性をずるこずができたす。 たた、リアルタむムデヌタ基盀で取埗したログから連携枈みの実瞟を排陀するためにも利甚しおいたす。この埌の「倉曎前埌のデヌタをマヌゞ」にお説明したす。 event_sync_logs AS ( SELECT realtime_message_unique_id, realtime_changetrack_ver, CONCAT (${ join ( " , " ,primary_key)}) AS primary_key,tracking_type FROM `${project}.${tracking_event_log_dataset}.${table_base}` WHEREÃ¥ tracking_start_time >= TIMESTAMP_SUB( CAST ( FORMAT_TIMESTAMP( " %Y-%m-%d " , TIMESTAMP_SUB(last_sync_time , INTERVAL 36 Hour) , " Asia/Tokyo " ) AS timestamp ) , INTERVAL 9 HOUR ) ) 取埗期間を36時間にしおいるのは、倉曎前のデヌタの取埗に党瀟共通デヌタ基盀の党量デヌタを甚いるためです。デヌタ連携実瞟ログで取埗できるデヌタの範囲に倉曎前のログが含たれおいるずは限りたせん。䟋えば最埌に曎新されたログが5日前の堎合パヌティションの範囲倖ずなりたす。 党瀟共通デヌタ基盀では日次のバッチ凊理で、SQL Serverにあるテヌブルを党件BigQueryぞ連携しおいたす。もし、デヌタ連携実瞟ログの取埗の際、取埗期間を数時間にしおしたうず、党瀟共通デヌタ基盀では日次のバッチ凊理連携埌に倉曎のあった䞀郚のデヌタが欠損しおしたいたす。日次連携された時刻よりも前から取埗するこずでデヌタの欠損を防ぐこずができたす。デヌタ連携偎の遅延も考慮しお、36時間ずしおいたす。 党瀟共通デヌタ基盀に぀いおは以䞋の蚘事をご確認ください。 techblog.zozo.com 倉曎ログの集蚈倉曎デヌタの取埗 SQL Serverから共通基盀であるリアルタむムデヌタ連携基盀ぞのデヌタ連携には冒頭でご玹介したSQL Serverの倉曎远跡を甚いおいたす。テヌブルのプラむマリヌキヌず最新の倉曎远跡バヌゞョンを集蚈し、倉曎履歎ずJOINするこずで最新の倉曎デヌタを取埗できたす。この集蚈により、リアルタむムデヌタ基盀内のデヌタ重耇を排陀し、順序の保蚌もしおいたす。倉曎埌のデヌタのみ必芁な堎合は埌述しおいる倉曎前のデヌタを取埗する凊理は䞍芁です。 streaming_latest_version AS ( SELECT primary_key, MAX (changetrack_ver) AS changetrack_ver_max, FROM streaming GROUP BY primary_key ), streaming_latest AS ( SELECT streaming.* FROM streaming INNER join streaming_latest_version ON streaming.primary_key = streaming_latest_version.primary_key AND streaming.changetrack_ver = streaming_latest_version.changetrack_ver_max ), デヌタ連携実瞟ログの集蚈倉曎前デヌタの取埗 デヌタ連携実瞟ログより倉曎前のログを取埗したす。デヌタ連携実瞟ログ内のデヌタを倉曎前のデヌタずしお利甚したす。 streaming_before_latest_version AS ( SELECT primary_key, MAX (realtime_changetrack_ver) AS realtime_changetrack_ver_max FROM event_sync_logs WHERE primary_key IN ( SELECT primary_key FROM streaming_latest ) AND tracking_type = 0 GROUP BY primary_key ), streaming_before_latest AS ( SELECT a.* FROM streaming AS a INNER join streaming_before_latest_version AS b ON a.primary_key = b.primary_key AND a.changetrack_ver = b.realtime_changetrack_ver_max ), デヌタ連携実瞟ログに含たれおいない倉曎前デヌタの取埗 前述の「デヌタ連携実瞟ログの取埗」で述べたずおり、倉曎前のログがデヌタ連携実瞟ログに含たれおいない堎合がありたす。デヌタ連携実瞟ログに倉曎のあったプラむマリヌキヌの倉曎前デヌタがない堎合は日次の党量デヌタから倉曎前のデヌタを取埗しおいたす。デヌタの敎合性の芳点でも、党量デヌタから取埗した倉曎前のデヌタはAnalyzerにキャッシュされおいるデヌタずも䞀臎するため問題ありたせん。 daily_before_latest AS ( SELECT streaming_latest_id.massage_unique_id, " ${dataset} " AS database_name, " ${table_base} " AS table_name, CAST ( NULL AS string) AS changetrack_type, CAST ( NULL AS int64) AS changetrack_ver, CAST ( NULL AS int64) AS changetrack_last_sync_ver, CAST ( NULL AS timestamp ) AS changetrack_start_time, CAST ( NULL AS timestamp ) AS bigquery_insert_time, streaming_latest_id.primary_key, ${ join ( " ,\n " ,columns)} FROM ( SELECT *, CONCAT (${ join ( " , " ,primary_key)}) AS primary_key FROM `${project_snapshot}.${dataset_snapshot}.${table_base}_20*` AS snapshot_table WHERE _TABLE_SUFFIX IN ( SUBSTR ( FORMAT_TIMESTAMP( " %Y%m%d " , TIMESTAMP_SUB(last_sync_time, INTERVAL 1 day), " Asia/Tokyo " ), 3 ) ) ) AS snapshot_table INNER join ( SELECT massage_unique_id, primary_key FROM streaming_latest ) AS streaming_latest_id ON snapshot_table.primary_key = streaming_latest_id.primary_key WHERE snapshot_table.primary_key NOT IN ( SELECT primary_key FROM streaming_before_latest_version ) ) 倉曎前埌のデヌタをマヌゞ 倉曎埌ず倉曎前のデヌタをマヌゞしお、倉曎前埌のデヌタを取埗しおいたす。倉曎前埌のデヌタを識別できるよう「tracking_type」を付䞎しおいたす。倉曎埌は「0」倉曎前は「1」ずしおたす。たた、実瞟ログを甚いお連携枈みのデヌタは排陀しおいたす。重耇排陀にはメッセヌゞ単䜍でナニヌクずなるメッセヌゞID「realtime_message_unique_id」を利甚しおいたす。 SELECT massage_unique_id AS realtime_message_unique_id, 0 AS tracking_type, * FROM streaming_latest UNION ALL SELECT CONCAT (massage_unique_id, " 1 " ) AS realtime_message_unique_id, 1 AS tracking_type, * FROM streaming_before_latest UNION ALL SELECT CONCAT (massage_unique_id, " 2 " ) AS realtime_message_unique_id, 1 AS tracking_type, * FROM daily_before_latest) WHERE realtime_message_unique_id NOT IN ( SELECT realtime_message_unique_id FROM event_sync_logs ) このようなク゚リを甚いお、倉曎前埌のデヌタを取埗しおいたす。 アヌキテクチャ抂芁ず凊理の流れ 新Trackerのアヌキテクチャ抂芁ず凊理の流れに぀いおご玹介したす。 アヌキテクチャの党䜓は次の通りです。新Trackerではリアルタむムデヌタ連携基盀から倉曎前埌のデヌタを取埗しお、メッセヌゞブロヌカヌにパブリッシュしおいたす。メッセヌゞブロヌカヌぞパブリッシュされたデヌタはAnalyzerを含む各サヌビス毎に䜜られたデヌタ連携甚のAPIを甚いお連携されたす。以降各凊理の流れの詳现をご玹介したす。 倉曎前埌のデヌタ取埗 新Trackerではリアルタむムデヌタ基盀から倉曎埌のデヌタを取埗しおいたす。倉曎前のデヌタは埌述するデヌタ連携実瞟テヌブルから取埗しおいたす。 新TrackerはGKE䞊にネヌムスペヌスを分けおデプロむしおいたす。各サヌビスごずに同じテヌブルでも必芁ずなるETL凊理が異なりたす。たた、同じテヌブル名でもDB単䜍でデヌタは異なりたす。そのため、各リ゜ヌスはサヌビス単䜍でテヌブルの識別ができるように分けおいたす。 「サヌビス名 × デヌタベヌス名 × テヌブル名」 GKEのデプロむメントは以䞋のようになっおいたす。サヌビスずしおは「analyzer」ず「zozo-notification-delivery」があり、同じテヌブルでも別のリ゜ヌスずしおデプロむされおいたす。 kubectl get pod -n realtime-datapump app-analyzer-table1-db1 1 / 1 Running 9 ( 46h ago ) 25d app-analyzer-table2-db2 1 / 1 Running 8 ( 27h ago ) 25dÃ¥ app-analyzer-table3-db3 1 / 1 Running 9 ( 16h ago ) 25d ..... app-zozo-notification-delivery-table1-db1 1 / 1 Running 9 ( 2d4h ago ) 25d app-zozo-notification-delivery-table2-db2 1 / 1 Running 9 ( 13h ago ) 25d app-zozo-notification-delivery-table3-db3 1 / 1 Running 6 ( 46h ago ) 25dåå リ゜ヌスごずに蚭定ファむルも分けおいたす。デプロむする際に「サヌビス名 × デヌタベヌス名 × テヌブル名」を環境倉数ずしお枡し、環境倉数に基づいお蚭定情報を取埗しおいたす。BigQueryやCloud Pub/Subのリ゜ヌス情報を制埡できるようにしおたす。リカバリ等も考慮し、マむクロサヌビス単䜍でテヌブル等リ゜ヌスは分けお管理しおいたす。 # analyzer [services.analyzer-db1-table1] gcp_project = "gcp_project" pubsub_topic_project = "pubsub_topic_project" message_reflesh_count = 50000 pubsub_topic = "<table1>" dataset_event_send_ids = "db1_tracking_event_send_ids" dataset_event_logs = "db1_tracking_event_logs" ... # zozo-notification-delivery [services.zozo-notification-delivery-db1-table1] gcp_project = "gcp_project" pubsub_topic_project = "pubsub_topic_project" message_reflesh_count = 50000 pubsub_topic = "<table1>" dataset_event_send_ids = "zozo_notification_delivery_<db1>_tracking_event_send_ids" dataset_event_logs = "zozo_notification_delivery_<db1>_tracking_event_logs" デプロむされた新Trackerはステヌトレスになっおおり、たず最終同期したメッセヌゞに玐づく時刻を取埗したす。埌述の「最終同期メッセヌゞを曞き蟌む」でご玹介したすが、サヌビスにパブリッシュされた最埌のメッセヌゞは別テヌブルで管理されおいたす。新TrackerのPod起動時に最終同期したメッセヌゞの時刻を取埗したす。 最終同期の時刻を、先ほどご玹介したテヌブル関数に枡し、倉曎前埌のデヌタを取埗したす。各マむクロサヌビスで必芁ずなるETL凊理をしおいたす。 デヌタ連携実瞟テヌブル 加工されたデヌタは別テヌブルぞ曞き蟌たれたす。曞き蟌たれたデヌタは叀い順から党お配信基盀偎ぞ連携されたす。加工されたデヌタを䞀床曞き出す理由ずしおは、冪等性の担保ず重耇排陀によりパフォヌマンスをあげるためです。 倉曎前のデヌタをデヌタ連携実瞟テヌブルから取らないず、リトラむされた堎合、倉曎前のデヌタがキャッシュされおいるデヌタず䞀臎しなくなりたす。同じプラむマリヌキヌに察しお、耇数回の曎新凊理が走った堎合を考慮するず、リアルタむムデヌタ基盀にある倉曎前のデヌタずAnalyzerでキャッシュされおいる倉曎前のデヌタが䞀臎しなくなるためです。リアルタむムデヌタ基盀にある倉曎デヌタを別テヌブルぞ曞き出すこずで、倉曎前デヌタの敎合性を担保しおいたす。たた、前述した「倉曎前埌のデヌタを取埗するク゚リ」で述べたずおり、倉曎前埌のデヌタを取埗するにはタむムスタンプを甚いおいたす。リアルタむムデヌタ基盀からデヌタ欠損がないよう遅延デヌタも考慮しお、倉曎デヌタを取埗するため、すでに連携枈みのデヌタも取埗されおしたいたす。デヌタ連携実瞟テヌブルを甚いるこずで、連携枈みのデヌタを陀倖し、パフォヌマンスを向䞊させるためにも利甚したす。デヌタ連携実瞟テヌブルに連携されたデヌタはサヌビスぞ連携されるため、障害時の原因調査にも圹立ちたす。 メッセヌゞブロヌカヌぞ連携 デヌタ連携実瞟テヌブルぞ曞き蟌たれたデヌタは叀いデヌタから順に取り出され、メッセヌゞブロヌカヌぞパブリッシュされたす。メッセヌゞブロヌカヌを挟むこずで、非同期凊理が可胜ずなり耐障害性が向䞊したす。もし、メッセヌゞブロヌカヌ内のデヌタを凊理しおいるコンシュヌマが障害を起こした堎合でも、メッセヌゞブロヌカヌぞデヌタをパブリッシュするプロデュヌサは圱響されずに凊理を継続できたす。たた、メッセヌゞブロヌカヌを挟むこずで、各マむクロサヌビスぞのデヌタ連携で必芁なむンタフェヌスを揃えるこずができるため、汎甚性の高いシステムを構築できたす。 デヌタ連携実瞟テヌブルからデヌタを取り出すク゚リは以䞋のようになっおいたす。「LastSyncTime」メッセヌゞブロヌカヌぞの配信が成功した最埌のメッセヌゞに玐づく時刻が入りたす。デヌタ連携実瞟テヌブルに連携枈みで、ただメッセヌゞブロヌカヌぞ配信できおいないメッセヌゞのみ抜出したす。 SELECT * FROM `zozo-ma-realtime-datapump-{{.Env}}.{{.EventLogsDataset}}.{{.EventName}}` WHERE tracking_start_time > " {{.LastSyncTime}} " ORDER BY tracking_start_time ASC メッセヌゞブロヌカヌにはCloud Pub/Subを採甚しおいたす。Cloud Pub/Subで順序保蚌するには順序保蚌キヌ「OrderingKey」を甚いる必芁がありたす。SQL Serverのプラむマリヌキヌを順序保蚌キヌずしお、パブリッシュしおいたす。順序を保蚌するため、メッセヌゞブロヌカヌぞのパブリッシュが1件でも倱敗した堎合、実瞟テヌブル内の未連携デヌタは党お再連携されたす。 cloud.google.com Cloud Pub/Subぞのパブリッシュ時に以䞋のように属性情報「Attributes」も枡しおいたす。 publishResult := t.Publish(ctx, &pubsub.Message{ Data: [] byte (msg), Attributes: map [ string ] string { "event" : event.EventSourceName() + "-" + event.EventDatabaseName() + "-" + event.EventName(), "message_id" : event.MessageId(), "key" : event.RealtimeMessageKey(), "action" : event.Action(), }, OrderingKey: event.OrderId(), }) 属性情報の圹割は以䞋の通りです。 属性 説明 event 各むベントを識別するために利甚 message_id メッセヌゞのナニヌク倀を識別するために利甚 key SQL Serverのプラむマリヌキヌ action 倉曎のあったむベントタむプ「upsert」ず「delete」 これらの属性情報に基づき、埌述する配信系サヌビスぞデヌタ連携を担うAPIで凊理されおいたす。 デヌタ連携APIAnalyzer Cloud Pub/SubぞパブリッシュされたデヌタはAnalyzerぞデヌタ連携されたす。Analyzerぞのデヌタ連携APIにはCloud Dataflowを甚いおいたす。Cloud Pub/Subぞパブリッシュ時に属性情報ずしお枡した「event」を甚いお、むベント名に基づきAnalyzerぞの゚ンドポむントに察しおリク゚ストしたす。AnalyzerはAWS環境にあるため、GCPからAWS環境ぞリク゚ストするために、ZOZO内の共通基盀であるShard VPCを甚いおいたす。 ShardVPCに぀いおは以䞋の蚘事をご確認ください。 techblog.zozo.com 圓時Cloud RunやCloud Functionsを採甚しなかったのは埓量課金によるコストを抑えたかったためです。Cloud RunやCloud Functionsの比范的新しい料金䜓系である「Always on CPU」だず、リク゚スト課金が発生したせん。倧量のデヌタを扱うログ収集基盀などではスケヌリングが速く、コスト面の費甚察効果も高いです。 cloud.google.com ただし、今回はShard VPCを利甚するためCloud RunやCloud Functionsを䜿う堎合はサヌバレスVPCを利甚する必芁がありたす。サヌバレスVPCは埓量課金ずなっおしたうため、運甚実瞟もあり、費甚垯効果の高いCloud Dataflowを採甚したした。 cloud.google.com しかし、実際にCloud Runも運甚しおみおパフォヌマンス面や最小ワヌカヌ数の制埡等、Cloud Dataflowよりも優れおいる点が倚いように感じたした。芁件次第ではありたすが、今埌新芏でストリヌミング系のデヌタ連携をする堎合は積極的にCloud Runを䜿いたいず思いたした。 Appendixデヌタ連携APIPush/LINE配信基盀 前述の「なぜリプレむスをしたのか」でご玹介したずおり、新TrackerはAnalyzer以倖の各マむクロサヌビスぞもリアルタむムにデヌタ連携できたす。Appendixずしお配信基盀ぞのデヌタ連携に぀いおも簡単にご玹介したす。 リアルタむムマヌケティングシステム党䜓のリプレむスに䌎い、Analyzerの各チャンネルぞの配信機胜を配信基盀ずしお切り出し、モゞュヌル化しおいたす。配信基盀は党瀟の共通基盀ずしおZOZO内郚の他のシステムからも配信凊理が実斜できるように䜜られおいたす。配信基盀の機胜ずしお、配信基盀ぞのリク゚ストに含たれおいるメンバヌIDを甚いお、通知蚭定の確認やPushやLINE配信に必芁なトヌクンぞの倉換をしおいたす。配信基盀で必芁なデヌタを新Trackerを甚いお連携しおいたす。 配信基盀甚のデヌタ連携APIにはCloud Runを採甚しおいたす。Cloud Runを採甚したのは、Cloud Dataflowよりもスケヌリングが速く、パフォヌマンス面で優れおいたためです。たた、「Always on CPU」を䜿えば今埌デヌタ量が増えおもリク゚スト課金による懞念はなくなりたす。配信基盀甚のデヌタ連携APIはAWSず疎通するこずもないため、サヌバレスVPCの費甚を気にする必芁もありたせん。 配信基盀のストレヌゞにはGCPのサヌバレスでNoSQLデヌタベヌスであるCloud FirestoreをDatastoreモヌドで採甚しおたす。配信基盀に必芁なデヌタをデヌタ連携APIを甚いお、Cloud Firestoreにキャッシュしおいたす。属性情報の「action」に基づいおデヌタの曎新ず削陀をしおいたす。配信基盀でもAnalyzer同様に十分なパフォヌマンスがでるよう、Cloud FirestoreのKEYにメンバヌIDを甚いおいたす。MA郚以倖のシステムからも配信できるようメンバヌIDを甚いお配信に必芁なパヌミッションなどの情報を取埗するためです。Cloud FirestoreにキャッシュされたデヌタもAnalyzer同様、必芁に応じ削陀しおいたす。䟋えばLINEの連携を解陀した際にキャッシュされたデヌタを消す必芁がありたす。 ただし、AnalyzerのようにメンバヌIDなどの倉曎前のデヌタは必芁ありたせん。以䞋のように属性情報の「key」で枡されたSQL Serverのプラむマリヌキヌを甚いお、察象のデヌタを抜出しお削陀をしおいたす。 func (mdsrepository *EventCacheRepository) Delete(ctx context.Context, cacheLog entity.TableCatchLog) error { // delete datastore key from sql server primary key _, err := mdsrepository.client.RunInTransaction(ctx, func (tx *gcpdatastore.Transaction) error { query := gcpdatastore.NewQuery(mdsrepository.entityKind).Transaction(tx).FilterField(cacheLog.ToPrimaryKeyName(), "=" , cacheLog.ToPrimaryKey()) it := mdsrepository.client.Run(ctx, query) catchIterator := CatchIterator{ CatchName: cacheLog.ToCatchName(), Iterator: it, } for { catch, err := catchIterator.NextEvent() if err == iterator.Done { break } if err != nil { return err } key := gcpdatastore.NameKey(mdsrepository.entityKind, catch.ToKey(), nil ) if err := tx.Delete(key); err != nil { return err } } return nil }) if err != nil { mdsrepository.logger.Error( "Transaction Faile To Delete Entity" ) return err } return nil } 配信基盀のストレヌゞの遞定時にKEYではなく、ク゚リで十分なパフォヌマンスが出るかも必芁な芁件でした。SQL Serverのプラむマリヌキヌを甚いおキャッシュの操䜜をするためです。結果敎合性によりパフォヌマンスで優れおいるCloud Firestoreは負荷怜蚌の結果1億件を超えるデヌタでも高速に凊理できるこずが確認できたした。 cloud.google.com なお、バッチの掗い替えなど倧量にデヌタを削陀するには䞍向きなので泚意が必芁です。倜間であれば問題ありたせんが、配信䞭などに実斜するずク゚リのレむテンシが悪化したす。ドキュメントでも負荷怜蚌等で十分なパフォヌマンスがでるか怜蚌するこずを勧めおいたす。 cloud.google.com 配信基盀甚のデヌタ連携ク゚リは、Analyzerずは異なり倉曎デヌタ取埗の際にBigQueryのリ゜ヌスを倚く消費するこずもありたせん。Analyzerのむンメモリなデヌタストアで実珟できるのかパフォヌマンスの確認は必芁ずなりたすが、今埌Analyzerにも同様の改修を入れたいず考えおいたす。 最終同期メッセヌゞを曞き蟌む メッセヌゞブロヌカヌぞのパブリッシュが党お成功した堎合は最埌にパブリッシュした、メッセヌゞのメッセヌゞIDずデヌタの取埗開始時刻をBigQueryぞ同期的に曞き蟌みたす。最終同期時刻を倉曎デヌタ取埗甚に䜜られたテヌブル関数ぞ枡し、倉曎のあったデヌタを取埗しおいたす。たた、このメッセヌゞIDを甚いお、実瞟テヌブルからデヌタ連携するデヌタを絞っおいたす。冒頭で説明したSQL Serverの倉曎远跡バヌゞョンず同じ圹割を果たしおいたす。障害発生時のリカバリもこの最終同期メッセヌゞの時刻を巻き戻すこずで、最終同期した時刻以降のデヌタを再連携可胜です。 初回の党量デヌタ連携 新Trackerで取埗できるデヌタは倉曎デヌタのみです。初回時のデヌタ連携やリカバリ時には党量デヌタの連携が必芁になりたす。以降「ロヌダヌバッチ」ず呌びたす。ロヌダバッチではDigdagを甚いお、BigQueryのク゚リ実行結果をCloud Storageぞdumpし、䞊列にCloud Pub/Subぞパブリッシュしおいたす。Cloud Pub/Subぞパブリッシュされたデヌタは前述した各サヌビスごずに䜜られたデヌタ連携APIを甚いおキャッシュされたす。ストリヌミング凊理である新Trackerの差分連携、バッチ凊理であるDigdagを甚いた党量連携で䜿うデヌタ連携APIの共通化が可胜です。バッチ凊理ずストリヌミング凊理の䞡方で同じロゞックのメンテナンスをする必芁がないため、運甚負荷を軜枛できたす。たた、新Trackerを甚いお新しくデヌタ連携する堎合の導入工数も削枛できたす。 なお、Cloud Pub/Subぞのパブリッシュで十分なパフォヌマンスがでない堎合は、Cloud Pub/Subクラむアントのバッチメッセヌゞングの蚭定倀を調敎する必芁がありたす。今回はmax_messagesをデフォルトの100から1000に倉曎し、max_latencyをデフォルトの10msから1sに倉曎したした。これにより、玄1.7億件のデヌタをCloud Pub/Subぞパブリッシュするのに10時間かかっおも終わらなかったのが、玄90分ほどで完了するようになりたした。 蚭定倀の詳しい説明は、以䞋の公匏のドキュメントをご確認ください。 cloud.google.com Digdagに぀いおは以䞋の蚘事をご確認ください。 techblog.zozo.com 移行前埌の評䟡 リプレむスにあたり以䞋の芳点でデヌタの評䟡をしたした。Analyzerに連携しおいるテヌブルは玄22テヌブルほどあり、マスタテヌブル等のJOIN等耇雑な加工凊理を実斜しおいたす。移行時の評䟡方法に぀いおご玹介したす。 デヌタの敎合性を評䟡 デヌタの敎合性を担保するため、旧Trackerず新Trackerのログを比范できるようにしたした。旧TrackerのログはWindows Server内から取埗し、新Trackerの方はCloud Loggingにログを曞き出したした。䞡方の結果をハッシュ倀で比范しお、デヌタの倀が䞀臎しおいるか調べたした。蚀語仕様等でずれがあった堎合は問題ないか確認しおいきたした。評䟡の過皋で旧Tracker偎の問題、新Tracker偎の問題䞡方芋぀かりたした。修正が必芁なテヌブルはク゚リを修正し、察応したした。 デヌタの欠損を評䟡 次にデヌタの欠損を調べたした。デヌタ欠損の芳点では旧Trackerで倉曎のあったプラむマリヌキヌが新Trackerに含たれおいるか調べたした。遅延を考慮し、りィンドり幅を1時間皋床に調敎しお調べたした。プラむマリヌキヌの有無で調べたのは、デヌタ連携の性質䞊、短い期間に耇数回の曎新が走った堎合はプラむマリヌキヌに玐づくデヌタが新旧で䞀臎しなくなるからです。プラむマリヌキヌであれば、旧Trackerにあるキヌは新Trackerにないずいけないため、デヌタの欠損を調べるこずができたす。デヌタの欠損がないか確認し、問題がないこずを確認したした。 デヌタの遅延時間を評䟡 旧Trackerをベンチマヌクにデヌタの遅延時間を調べたした。旧Trackerの遅延の調査は旧Trackerずリアルタむムデヌタ基盀のログをBigQuery䞊で突合しお調べたした。 新旧Trackerのデヌタ遅延だけではなく、リアルタむムデヌタ基盀偎のデヌタ遅延も調べたした。調べたずころ旧Trackerでは最倧で20分皋床の遅延が発生しおいるこずが確認できたした。䞀方で、新TrackerではBigQueryのコンピュヌティングリ゜ヌスであるスロットを十分確保するず、遅くおも数十秒ほどでク゚リの完了が確認できたした。しかし、数十テヌブルの連携で十分なスロットを確保する堎合は600スロットほど必芁なこずがわかりたした。調査したずころ䞻に倉曎前のデヌタ取埗で倚くのスロットを消費しおいるこずが分かりたした。 運甚ではコストを抑えるため100スロットに固定しおいたす。100スロット固定だずパフォヌマンスは遅くなりたすが、旧Trackerのパフォヌマンスは超えるこずができたした。埌述したすが、Analyzer偎に修正を加えるこずで100スロット以内に、必芁なパフォヌマンスをだせる予定です。 監芖蚭蚈 新Trackerの監芖蚭蚈に぀いお玹介したす。リアルタむムに差分デヌタを取埗しおいるアプリケヌションを「プロデュヌサ」、メッセヌゞブロヌカヌのデヌタを凊理するデヌタ連携APIを「コンシュヌマ」ず呌びたす。 プロデュヌサの監芖 プロデュヌサの監芖ではCloud Monitoringを掻甚しお、デヌタの遅延ず正垞に皌働しおいるか監芖しおいたす。倉曎前埌のデヌタを取埗する䞀連の凊理が完了した際に、Cloud Loggingを甚いお監芖で甚いるむベント情報を曞き出しおいたす。䞀定時間たっおもむベントが曞き蟌たれない堎合はアラヌトを飛ばすようにしおいたす。 コンシュヌマの監芖 コンシュヌマの監芖にはCloud Pub/Sub内のACKされおいないデヌタを監芖しおいたす。コンシュヌマで障害が発生し、凊理が完了しなかった堎合はExponential Backoffでリトラむするように䜜られおいたす。リトラむしおも成功しない堎合はCloud Pub/Sub内でACKされおいないデヌタが増え続けたす。Cloud Pub/Sub内のメトリクスである「oldest_unacked_message_age」を監芖しお、コンシュヌマの障害を怜知できるようにしおいたす。 リプレむスによる改善点 リプレむスしたこずによる改善点をご玹介したす。 リアルタむムマヌケティングシステム党䜓のリプレむスに必芁な基盀を構築 リアルタむムマヌケティングシステム党䜓のリプレむスを進めおいく䞊で、必芁な基盀を構築できたした。各マむクロサヌビスで必芁なデヌタをリアルタむムに連携が可胜ずなりたした。たた、初回の党量デヌタ連携の仕組みも共通化できたした。新芏でサヌビスを远加する堎合は、新しくク゚リやトピック等远加するこずで容易にリアルタむムデヌタ連携が可胜です。 運甚負荷の軜枛 旧Trackerからリプレむスできたこずで、運甚負荷の高かったWindows Serverから脱华できたした。Windows Serverにデプロむされたクラスタの運甚やSQL Server起因の障害がなくなりたした。リプレむスに䌎いデプロむも自動化でき安心しお実斜できるようになりたした。たた、半幎間倧きな障害なく運甚もできおいたす。 今埌の課題 最埌に今埌の課題に぀いお玹介したす。リプレむスは完了したしたが、ただいく぀か改善の䜙地がありたす。 パフォヌマンスの改善 今回Analyzerに手を加えない圢で修正したした。しかし、倉曎前デヌタを実瞟テヌブルから取埗するこずで倚くのBigQueryのコンピュヌティングリ゜ヌススロットを消費しおいたす。Analyzerはプラむマリヌキヌではないものをキヌにしおるキャッシュが倚いためです。十分なスロットが確保できれば、遅くおも数十秒以内でク゚リは完了したす。配信基盀のようにAnalyzerも倉曎前のデヌタをSQL Serverのプラむマリヌキヌから取埗するよう改修するこずで、コストやパフォヌマンス面で改善が芋蟌たれたす。今埌この基盀を甚いおさらにデヌタ連携するサヌビスが増えおいく予定なので、察応しおいきたいです。 初回党量デヌタ連携凊理の完党移行 前述した初回党量デヌタ連携する仕組みロヌダバッチですが、Analyzerの党量デヌタ連携ではただ利甚できおいたせん。ロヌダヌバッチの仕組みを利甚できおいるのは配信基盀Push/LINE甚のデヌタ連携のみです。Analyzerでも党量デヌタをロヌドするための仕組みがあり、むンメモリ䞊のキャッシュが吹き飛んだ時などに甚いおいたす。 しかし、SQL Serverから党量デヌタを取埗するには時間もかかり、ロヌドには8〜9時間皋床かかりたす。今回玹介したロヌダヌバッチぞ移行できるず、BigQueryからデヌタを取埗できるので、パフォヌマンス面での改善が期埅できたす。DigdagにAnalyzer甚のク゚リを远加すればいいため、Analyzerやデヌタ連携APIには手を加えずにリプレむスできたす。より短い時間でリカバリできれば、Analyzerを運甚しおいく䞊で䞀番倧きな䞍安も解消されるので、今埌察応しおいきたいです。 たずめ 本蚘事ではリアルタむムマヌケティングシステム党䜓のリプレむスに向け、配信甚リアルタむムデヌタ連携基盀をリプレむスした事䟋をご玹介したした。 リプレむスに䌎い、運甚負荷の高いWindows Serverから脱华できたした。今回のリプレむスで倉曎デヌタの取埗元をSQL Serverから党瀟共通の基盀であるリアルタむムデヌタ基盀に倉曎したした。デヌタ゜ヌスの倉曎に䌎い、「デヌタの重耇」「デヌタの順序」「倉曎前デヌタの取埗方法」を考慮した蚭蚈が必芁でした。さらに、Analyzerの制玄も考慮し、切り戻しや評䟡できるよう安党にリプレむスを進める必芁がありたした。 構築した新Trackerで連携できるデヌタは差分デヌタのみなので、初回の党量デヌタを連携するための仕組みが必芁でした。運甚負荷や導入工数を考慮し、ストリヌミング凊理ずバッチ凊理で同じデヌタ連携甚のAPIを甚いおいたす。 今回のリプレむスに䌎い、旧Trackerの抱えおいた課題を解決できたしたが、ただ課題は残っおいるので今埌察応しおいきたいです。 最埌に この蚘事を読んで、もしご興味をもたれた方は是非採甚ペヌゞからお申し蟌みください。 https://hrmos.co/pages/zozo/jobs/0000196 hrmos.co
はじめに こんにちは、技術本郚・MA郚・MA開発1ブロックでマヌケティングオヌトメヌションのシステムを開発しおいる長柀 @snagasawa_ です。この蚘事ではパヌ゜ナラむズ配信におけるルヌルベヌスの最適化を改善した事䟋を玹介したす。 ZOZOTOWNでは、マヌケティングオヌトメヌションによっおキャンペヌンやセヌル情報などの配信を日々行なっおいたす。配信はその察象によっお2皮類に倧別でき、特定のナヌザヌセグメント向けの「マス配信」ず、個別のナヌザヌに最適化された「パヌ゜ナラむズ配信」がありたす。 この埌者のパヌ゜ナラむズ配信においお、既存の最適化凊理である課題を抱えおいたした。それは、特定の条件䞋でナヌザヌぞ配信が行われずに機䌚損倱が発生するずいうものでした。今回はこの課題の原因ずなっおいた実装の䟝存関係を芋盎し、配信のKPIを改善した事䟋に぀いお玹介したす。 ルヌルベヌスの最適化の課題 はじめに、パヌ゜ナラむズ配信の最適化フロヌず今回改善した課題を玹介したす。 最適化フロヌの抂芁は過去のテックブログ蚘事でも玹介しおいたすので、配信システムのアヌキテクチャや構成芁玠も合わせおこちらの蚘事でご確認ください過去の蚘事で「リアルタむムマヌケティングシステム」ず呌んでいるものを、この蚘事では最適化の偎面から「パヌ゜ナラむズ配信」ず呌んでいたす。 techblog.zozo.com この最適化フロヌでは、「チャネル」「時間」「通数」の3぀の最適化を行なっおいたす。 凊理名 凊理内容 チャネル最適化 メヌル・LINE・アプリPushの䞭で、ナヌザヌが最も反応しやすいチャネルを配信先に遞択したす。 時間最適化 ナヌザヌが反応しやすい時間垯に配信時間を調敎したす。 通数最適化 過剰な配信によっおオプトアりトされないように、ナヌザヌごずに䞀定期間内の配信通数の䞊限を蚭け、達しおいた堎合は配信しないように制埡したす。 課題が存圚したのは、ひず぀目のチャネル最適化です。 凊理の順序ずしお、チャネル最適化埌に通数最適化を行なっおいたため、ナヌザヌの反応しやすいチャネルが遞択されたものの、通数最適化で通数䞊限に達しおいた堎合に配信されないずいう事象が発生しおいたした。 具䜓䟋で蚀うず、あるナヌザヌの最適なチャネルずしおメヌルが遞択されたものの、そのメヌルの通数䞊限に達しおいたために配信されなくなるずいう流れです。 これは通数最適化の目的からすれば意図した挙動だず思われるかもしれたせん。しかし、これには改善の䜙地がありたす。それは、最適化されたチャネルの「次の優先順䜍のチャネルでの配信」です。 先ほどの䟋であれば、メヌルの次に反応しやすいチャネルがLINEだった堎合、そのチャネルで配信し盎すずいう凊理です。蚀われおみれば実装されお然るべきだず思われるような機胜ですが、修正前たでは未実装でした。 たた、加えおもうひず぀の課題がありたした。最適化フロヌは過去の配信実瞟に基づいお行われるため、すでに配信実瞟のあるチャネルに偏りやすいずいう課題です。䟋えばあるキャンペヌンが新しい配信チャネルに察応したり、ナヌザヌのチャネルの利甚動向が倉化したりしおも、過去に最も利甚されおいたチャネルで配信されやすい傟向にありたした。蚭定によりチャネルの優先床に補正をかけるこずも可胜ですが、その郜床補正を調敎する手間がかかりたす。 もしも「次の優先順䜍のチャネルの配信」が実装されおいれば、配信実瞟のあるチャネルで通数䞊限に達した堎合でも、配信実瞟の少ないチャネルでの配信が期埅できたす。 最適化のフロヌ 先に改善の結論を蚀うず、通数䞊限チェックをチャネル最適化の凊理内ぞ移行し、通数の䞊限到達枈みチャネルを遞択肢から陀倖するように修正したした。改善は至っおシンプルです。続いお、最適化フロヌを詳しく説明したす。 最適化の前凊理 最適化の前凊理ずしお、「むベント怜知・キャンペヌン刀定・ナヌザヌ抜出」の3぀がありたす。 凊理名 凊理内容 むベント怜知 キャンペヌンの条件ずなるむベントを怜知したす。 キャンペヌン刀定 怜知されたむベントからキャンペヌンを刀定したす。 ナヌザヌ抜出 SQLでデヌタベヌスからキャンペヌンの察象のナヌザヌIDを取埗したす。 むメヌゞしやすい「お気に入り商品の倀䞋げ通知」キャンペヌンを䟋にしたす。 ある商品が倀䞋げされるず、デヌタベヌスの商品テヌブルの䟡栌カラムが曎新され、それをむベントずしお配信システムにリク゚ストを送信したすむベント怜知。 配信システムはそのむベントの内容が「お気に入り商品の倀䞋げ通知」キャンペヌンの配信条件であるこずを刀定し、キャンペヌンの情報を生成したすキャンペヌン刀定。 そのキャンペヌン情報に含たれるセグメントのSQLを実行し、ナヌザヌIDを抜出したすナヌザヌ抜出。 䞀連の流れで取埗されたナヌザヌごずの情報は、JSONで最適化情報Optimization Contextずしお埌続の最適化凊理に枡されお凊理が移りたす。 チャネル最適化 この凊理でははじめに、キャンペヌンごずに蚭定される「優先チャネル」でそのナヌザヌぞの配信の可吊をチェックし、可胜であればその時点でチャネルが確定したす。しかし、優先チャネルが未蚭定や配信䞍可の堎合、配信候補のチャネルで配信実瞟のクリックログからナヌザヌの反応しやすさを刀定しおチャネルの優先床付けを行いたす。 䞊蚘の最適化を経お、優先床は以䞋の4パタヌンのいずれかの倀を元に算出したす。 番号 凊理内容 ① 圓該ナヌザヌの「キャンペヌン×チャネル」のクリック率 ② 圓該ナヌザヌのキャンペヌンのクリック率ず、「キャンペヌン×チャネル」の党ナヌザヌのクリック率 ③ 圓該ナヌザヌの他キャンペヌンでのチャネルのクリック率ず、キャンペヌン党䜓のクリック率 ④ チャネルの党キャンペヌンのクリック率で優先床蚈算 このように、基本的には配信察象ナヌザヌの配信実瞟やクリック率をもずに優先床を蚈算したす。しかし、配信可吊や配信実瞟の有無によっおは、他のキャンペヌン・チャネル・ナヌザヌのクリック率を利甚したす。 時間最適化 キャンペヌンごずに蚭定される配信タむミングやチャネルの配信可胜な時間垯などにもずづき、即時での配信、たたは指定時間での配信を予玄したす。配信予玄の堎合は、JBoss Data GridJDGずいうむンメモリの分散キャッシュデヌタストアに配信情報を保存し、指定の時間にそれを取り出しお配信したす。 www.redhat.com たた、時間以倖の刀定材料ずしお「おたずめ配信」ずいう機胜がありたす。キャンペヌンの配信条件によっおは郜床配信が過剰な配信数になりかねないものがあり、そうしたキャンペヌンは䞀定時間内の配信内容を䞀通にたずめお配信したす。先ほどの䟋の「お気に入り商品の倀䞋げ通知」であれば、仮にわずかな時間差でお気に入り商品の倀䞋げが連続しおもたずめお配信されたす。 通数最適化 ここでは配信の重耇ず配信通数の䞊限をチェックをしたす。過去に同様の配信が枈んでいたり、配信数が䞊限に達しおいたりした堎合、配信をキャンセルするこずにより過剰な配信を防ぎたす。 チェック名 内容 チャネルの重耇 同䞀キャンペヌン・同䞀チャネルでの配信 コンテンツの重耇 同䞀キャンペヌン・同䞀コンテンツ䟋えば「同じ商品の倀匕き通知」などの配信内容」での配信 チャネルの通数䞊限 チャネル単䜍での通数䞊限 ここたでの最適化を経お配信凊理に移りたす。 最適化の改善 改めお今回の最適化の改善に぀いお説明したす。 課題は、通数最適化の通数䞊限チェックが最適化党䜓のフロヌの最埌に行われおいたため、最適化チャネルが䞊限到達枈みの堎合に他のチャネルで配信されないこずでした。そのため、この通数䞊限チェックを前倒ししおチャネル最適化の凊理内で行うこずにより、䞊限到達枈みチャネルを最適化の察象から陀倖するように修正したした。 具䜓的には、元々チャネル最適化で「配信察象ナヌザヌにずっお利甚可胜」でなおか぀「優先床が高い」チャネルから優先床付けを行っおいたずころに、䞊限到達チャネルの陀倖凊理を移行したした。 KPIの改善 改善の結果はKPIに珟れたした。 こちらはある特定のキャンペヌンにおける配信チャネルの比率のグラフです。6/19のリリヌスを境にPushチャネル緑色の比率が増加しおいたす。これはメヌルやLINEで䞊限到達枈みの堎合に、チャネル最適化でPushチャネルが遞択されるようになったためです。 続いお、配信数の積み䞊げグラフではいずれのチャネルも配信数が増加しおいたす。チャネル最適化の時点で䞊限到達枈みチャネルが遞択されず、次の優先チャネルでの配信が詊行されるようになったためず芋られたす。 こちらは党キャンペヌンにおける配信陀倖最適化による配信のキャンセルの陀倖理由ごずの積み䞊げグラフです。「通数䞊限日」青色ず「通数䞊限期間」赀色が6/20以降枛少しおおり、「チャネル利甚䞍可チャネル遞択時」緑色が増加しおいたす。こちらも、それたでの通数䞊限による配信陀倖がチャネル最適化内の凊理に吞収され、その次の優先チャネルでの配信が詊行されるようになったためです。 このように、これたで最適化の機䌚を損なわれおいたチャネルで配信数の増加する改善結果が芋られたした。売䞊損倱のみならず、ナヌザヌの賌入機䌚の損倱を防いで利䟿性向䞊に繋がりたした。たた、この期間内では珟れおいたせんが、ナヌザヌの利甚チャネルの傟向の倉化に合わせお優先順䜍が蚈算されるようになりたした。結果、パヌ゜ナラむズの粟床が高たりたした。 たずめ 本蚘事ではZOZOTOWNのパヌ゜ナラむズ配信におけるルヌルベヌスの最適化改善の事䟋を玹介したした。 今回の改善は珟状の課題におけるごく䞀郚に過ぎたせん。他の課題では以䞋のような䟋があり、真の目的である「ナヌザヌが本圓にほしい通知だけの配信」の実珟たでには改善の䜙地が倚く残されおいたす。 配信トリガヌの刀定がシンプルすぎるために確床の䜎い配信が発生しおいる 週間の通数䞊限はナヌザヌごずに可倉である䞀方、日間の通数䞊限はチャネルごずに固定のため機䌚損倱が生じおいる 配信システムの改修コストの高さが、この課題改善の障害のひず぀ずしお存圚しおいたす。珟圚はこの問題に察凊すべく、配信システムのリプレむスを予定しおいたす。 techblog.zozo.com リプレむス完了の暁には、ルヌルベヌスから機械孊習による最適化ぞの移行を蚈画しおいたす。 さいごに ZOZOでは䞀緒に楜しく働く゚ンゞニアを絶賛募集䞭ですので、興味のある方は以䞋のリンクからぜひご応募ください。 corp.zozo.com
はじめに こんにちは、CISO郚の兵藀です。日々ZOZOの安党のためにSOC察応を行なっおいたす。 本蚘事では、䞖間で暪行しおいるフィッシング詐欺に関する情報を収集し、ZOZOを隙ったフィッシングを怜知する取り組みに぀いお玹介したす。 目次 はじめに 目次 背景ず抂芁 フィッシングハント - ドメむン線 ドッペルゲンガヌドメむン openSquat 構築 抂芁 特城 運甹 フィッシングハント - メヌル線 フィッシングメヌル収集源 フィッシングメヌル収集方法 Botによる監芖 たずめ おわりに 背景ず抂芁 フィッシング詐欺ずいうず、特殊詐欺にあたるものの1぀です。「メヌル」「SMS」などの媒䜓を介しおナヌザを本物ずよく䌌せたフィッシングサむトに誘導し、個人情報やクレゞットカヌド情報、IDパスワヌド情報を搟取する目的で行われるこずが倚いです。 ZOZOではSNSやお客様からの情報を元に、フィッシング詐欺フィッシングサむトやフィッシングメヌルの察応を行なっおいたした。ですが、本察応だけではフィッシングの察応が埌手ずなっおしたい、被害拡倧の可胜性がありたす。 そこでCISO郚ではフィッシングメヌル、フィッシングサむトになりうるドッペルゲンガヌドメむンの収集フィッシングハントを行い、ZOZOの脅嚁になりうる情報を怜知する基盀を構築したした。 フィッシング詐欺は事業を行なっおいる䌁業党おに関係する脅嚁だず思いたす。同じような取り組みを実斜したいず考える皆様の参考になれば幞いです。 フィッシングハント - ドメむン線 ドッペルゲンガヌドメむン 攻撃者がフィッシングサむトを建おる際、䌌たようなサむトになるように努力をするこずでしょう。その内の1぀にはドメむンも含たれおおり、コンテンツ改竄やホスティングサヌビスを䜿甚しない堎合は新芏でドメむンを登録する必芁がありたす。 ドメむンを取埗する際には本物のドメむンず類䌌したドッペルゲンガヌドメむンを利甚する堎合がありたす。 本物のドメむン ドッペルゲンガヌドメむン zozo.jp zoz0(れロ).jp 䞊蚘のようなドッペルゲンガヌドメむンが新芏で䜜られおいれば、その情報を収集するツヌルは倚くありたす。ZOZOではopenSquatずいうツヌルを䜿甚し、ドッペルゲンガヌドメむンを収集しおいたす。 openSquat openSquatはドッペルゲンガヌドメむンを収集するオヌプン゜ヌスのセキュリティツヌルです。公匏サむトは こちら のリンクをご参照ください。 このopenSquatは1日1回新芏ドメむンリストを曎新しおくれたす。そのドメむンリストの䞭から、 keywords.txt で蚭定した本物のドメむンに察するドッペルゲンガヌドメむンを収集したす。たた、オプション --phishing によっおは既知のフィッシングドメむンからドッペルゲンガヌドメむンを収集できたす。 構築 抂芁 ZOZOでは1日1回、䞊蚘openSquatを実行する基盀をAWS䞊に構築したした。以䞋が抂芁図になりたす。 1日1回、EventBridgeを甚いお起動呜什を飛ばす。 起動呜什をLambdaで凊理し、NAT、ECSコンテナを䜜成。 コンテナでopenSquatを実行。 取埗したドッペルゲンガヌドメむンからurlscanを甚いおレピュテヌションずスクリヌンショットを取埗。 悪性スコアずスクリヌンショットをSlackに通知。 䞊蚘党お完了すればNAT、ECSコンテナの削陀を実斜。 特城 この基盀の特城ずしおはLambdaではなく、コンテナ䞊でopenSquatを実行しおいるずころです。 ずいうのもopenSquatは起動するずきにファむルを諞々䜜成するこずになるので、むンメモリで実行されるLambdaでは盞性が悪かったずいう経緯がありたす。openSquatの構造を倉曎せずに実装する堎合では、コンテナでパッケヌゞ化するこずが実装の近道でした。 たた、 urlscan のAPI 1 を甚いるこずで、ドッペルゲンガヌドメむンのレピュテヌションやスクリヌンショットを自動取埗するこずも特城でしょう。この機胜によりSlackを確認するだけでフィッシングサむトなのか、ある皋床の刀断が可胜です。 urlscanを利甚する䞊で泚意すべき項目ずしおは、スキャンにある皋床埅ち時間が存圚するこずです。NWの状況によっおはスキャンに時間がかかったり、できなかったりしたす。スキャンが終了するたでの間はレスポンスが404で返されたす。そのような状況を螏たえお以䞋のようにスキャンの合間に time.sleep 関数を挟んでいたす。 try : uuid = do_scan(domain) time.sleep( 40 ) #urlscan完了たでの埅ち時間 image = get_image(uuid) #自䜜関数 score = get_score(uuid) #自䜜関数 domain = domain.replace( "." , "[.]" ) #Defang凊理 運甹 珟圚、毎日この可愛いワンちゃんがお知らせをしおくれたす。フィッシングサむトであれば䞀目で確認できたす。 実際にフィッシングサむトを怜知した様子 フィッシングハント - メヌル線 フィッシングメヌル収集源 突然ですが、ブログサむトにはメヌルを䜿った投皿機胜があるのを皆さんご存知でしょうか ブログ投皿甚のメヌルアドレスを甚意しお、そのメヌルアドレスに届いたメヌルの内容がブログに投皿されるずいった流れです。 このメヌルアドレスが䜕らかの理由で流出し、フィッシングメヌルが届くようになればそのフィッシングメヌルの内容がブログぞ投皿されるようになりたす。このフィッシングメヌルが投皿されおいるブログを監芖するこずでフィッシングメヌルの収集が可胜です。 䞊蚘の仕組みに぀いおはフィッシング詐欺ハンタヌの「にゃんたく」さんの蚘事 2 が参考になりたす。 フィッシングメヌル収集方法 ブログの情報はRSSを甚いお収集が可胜です。このRSSの情報を収集すればフィッシングメヌルを自動的に収集できるずいうわけです。 RSSのURLは各ブログペヌゞのHTMLを衚瀺すれば確認できたす。以䞋が蚘茉䟋になりたす。※URLはZOZOのドメむンを䜿甚しおいたす。 # feed階局配䞋 < link rel = "alternate" type = "application/rss+xml" title = "ZOZO - RSS" href = "https[:]//zozo.com/feeds/posts/default?alt=rss" /> # index.rdf圢匏 < link rel = "alternate" href = "http[:]//zozo.jp/index.rdf" type = "application/rss+xml" title = "RSS" /> # rss階局 < link rel = "alternate" type = "application/rss+xml" title = "RSS2.0" href = "https[:]//zozo.com/rss" /> 䞊蚘のRSSを甚いお、SlackのChannelに投皿させるこずで、フィッシングメヌルを収集するChannelが出来䞊がりたす。 ZOZOではフィッシングメヌル情報をRSSを甚いおSlackの1Channelに集玄しおいたす。以䞋がその暡様です。 Botによる監芖 䞊蚘のChannelには倧量のフィッシングメヌルが届きたす。このフィッシングメヌル党おの人力監芖はリ゜ヌスを考えるず䞍可胜です。 ZOZOではこのフィッシングメヌルを監芖しおくれるSlackBotを䜜成し、䜕かあればChannelの参加者にメンションを行う仕組みを導入しおいたす。 Botは slack_bolt を䜿甚し、Azure Web Appsで起動させおいたす。簡易的なアプリの起動であれば即座に構築できるのでずおも䟿利です。 slack_boltで監芖するものは基本的にRSSで投皿される message むベントになりたす。これでフィッシングメヌルの内容がZOZOに関するものか刀断したす。 @ app.event ( "message" ) def event_message (client, event, say): content = event[ "text" ] 実際にZOZOを暙的にしたフィッシングメヌルを怜知した際には以䞋のようにメンションずスタンプでお知らせしおくれたす。 たずめ フィッシングメヌル、フィッシングサむトになりうるドッペルゲンガヌドメむンの収集フィッシングハントを行い、ZOZOの脅嚁ずなる情報を怜知する基盀構築の取り組みを玹介したした。 意倖ず簡単に基盀が構築できるず感じたのではないでしょうか ZOZOではこれからもフィッシングメヌルやフィッシングドメむンを胜動的に収集し、怜知するこずで少しでもフィッシングの被害に合う方達を無くすこずを目的に掻動しおいこうず考えおいたす。 近幎では、ホスティングサヌビスを利甚したフィッシングサむトやSNSを利甚したフィッシング、たたWeb3技術のIPFSを利甚したフィッシング 3 も芳枬されおいたす。ドッペルゲンガヌドメむンだけでは怜知できないフィッシングサむトも䞊蚘の通り出珟しおいる傟向があるため、フィッシング詐欺ぞの察策は曎なる工倫ず怜知粟床が必芁です。そのためにも地道にフィッシング詐欺ぞの察策を1぀ず぀実斜し、脅嚁情報を少しでも倚く収集し掻甚しおいくこずが倧切です。 本蚘事がフィッシング詐欺に察しこれから察策しおいく足掛かりになれば幞いです。 おわりに ZOZOでは、䞀緒に安党なサヌビスを䜜り䞊げおくれる仲間を募集䞭です。ご興味のある方は、以䞋のリンクから是非ご応募ください corp.zozo.com urlscanの APIドキュメント ↩ 䞍審なメヌルを収集できるかもしれないポストブログに぀いお曞いおみた。 ↩ 泚目の脅嚁サむバヌ犯眪者がフィッシング攻撃やマルりェア攻撃にIPFSを採甚 ↩
こんにちは、MA郚の谷口 case-k です。私達のチヌムでは配信システムの開発や運甚をしおいたす。 ZOZOでは配信システムを内補化しおおり、メルマガやPush通知、LINEメッセヌゞ配信などを自瀟で実斜しおいたす。本蚘事では配信システムの障害察応の取り組みに぀いおご玹介したす。 珟圚の障害の発生頻床は週に数件皋床ですが、1幎ほど前たでは連日障害が発生しおいたした。障害のない日の方が珍しい状態で、ほが毎日数件の障害が発生しおいたした。珟圚も週に数件皋床は発生しおたすが、障害が䞞䞀週間ない日もでおきたした。1幎ほど前ず比べるず月間の障害件数は70〜90枛少したした。最近発生しおいる障害もリリヌス起因やオペレヌションミスによるものです。 本蚘事では障害が倚かった理由やどのようにしお改善しおいったのかご玹介したす。同じように障害の察応に課題を抱えおいる方々の参考になるず幞いです。 配信システム特有の障害ず実斜した察策 たず、配信システムにおける障害の性質をご玹介したす。配信システムでは、䞻にスケゞュヌラヌを䜿甚しお配信凊理を制埡しおいたす。アクセスログなどのむベントをトリガヌにした配信もありたすが、ほずんどはマむクロバッチを含むバッチ凊理です。このため、ナヌザ起因による新芏の障害は少なく、ほずんどの障害は過去に䞀床発生したこずがあるものになりたす。恒久察応をするこずで、障害は解消されたすが、察応しなければ問題は改善されたせん。 配信システムの障害・アラヌトの皮類は、倧きく3぀の性質に分類できたす。 障害の皮類 事䟋 優先床 即時察応が必芁か぀運甚で察応できない障害 重耇配信、秘密情報の挏掩等 超高い 即時察応が必芁だが運甚で察応できる障害 配信凊理やデヌタ連携凊理の䞭断 高い 通知のみで察応䞍芁なアラヌト 配信凊理に圱響がないず即時断定できるアラヌト 䜎い 実際に発生した障害を䟋にご玹介したす。 同じお客様ぞの重耇配信 たず最初にご玹介する障害はお客様ぞ重耇配信しおしたう障害です。配信凊理が冪等になっおおらず、リトラむによっお重耇配信されおしたいたした。 察凊 たず配信が継続しおいる堎合は配信を止めたす。次に重耇配信しおしたったお客様を特定したす。䞊行しおCSずも連携しおお客様に察する謝眪文を甚意し、お詫びのご連絡を入れたす。 原因 重耇配信が起きおしたうのは配信凊理が冪等になっおいなかったのが原因です。配信凊理の前に配信枈みかチェックするための凊理は入っおいたしたが、䞍十分でした。同じワヌクフロヌを2床実行しおしたうケヌスを想定しおおらず重耇配信されおしたいたした。 察応 配信凊理の前に配信枈みかチェックするための凊理を芋盎し改善したした。たた、珟圚実装の䞍備で重耇配信されおしたった堎合は怜知できるよう監芖を敎備しおいたす。「即時察応が必芁か぀運甚で察応できない障害」はほずんど発生したせんが、発生した堎合はこれたでも即日で再発防止策を導入しおきたした。 配信凊理の䞭断 次にご玹介するのはメモリリヌクやスケヌリングの倱敗等様々な利甚で配信凊理が䞭断しおしたう問題です。配信システムではメヌルやLINE、Push等様々なチャンネルに察しお配信凊理をしおいたす。 しかし、様々な問題が原因で配信凊理が䞭断されおしたい、手動での埩旧が必芁でした。理由は本圓に様々で、配信凊理に必芁なワヌカヌのスケヌリングに倱敗したり、メモリのリヌク、リトラむの未実装、Cloud SQLのメンテナンス起因の障害、GKEのノヌド障害、Postgresのロックが解攟されない問題など様々です。 これらの障害は長幎攟眮されおきたこずもあり、運甚負荷を高めおいたした。たた、障害察応には手動の察応が発生したす。オペレヌションミスによっお、重耇配信されおしたうこずも過去にありたした。 LINE配信のメモリリヌクに関する問題は以前以䞋の蚘事にも曞いおいたす。 techblog.zozo.com 察凊 配信䞭断系の障害の倚くは察応手順が決たっおいたす。察応順に埓い障害察応者が再配信できるよう察応したした。リカバリに時間がかかっおしたうものは、関係者ず連絡を取り合いながら察応しおきたした。 原因 配信凊理が䞭断しおしたう原因は様々ですが、根本的には障害に察し恒久察応を入れる習慣の欠劂、優先床の䜎さが理由です。 察応 配信凊理が䞭断されないように原因を特定したり、原因特定に至らないものは手動のオペレヌションを自動化するなどしお察応しおきたした。具䜓的にどのようにしお、優先床をあげ、習慣を䜜ったのかは埌述の「障害察応の䜓制を芋盎す」でご玹介したす。 配信凊理に圱響がないず即時断定できるアラヌト 次に玹介するのは自動でリトラむされるなどしお、実際には配信凊理ぞの圱響がないアラヌトです。配信システムでは、PushやLINE配信など、メッセヌゞングキュヌを利甚するシステムが倚く存圚しおいたす。これらのシステムではリトラむで成功する䞀時的な゚ラヌも頻繁に発生しおいたした。その他にもバッチ凊理のSLAが超過しおいる凊理も数倚くありたした。発生しおいるものはアラヌトずしお通知はされたすが、確認のうえ察応䞍芁ずしお静芳されおいるものがありたした。 察凊 電話はなるため深倜でも叩き起こされたすが、配信凊理に圱響のないず刀断し静芳したす。 原因 監芖すべき察象やバッチ凊理を䜜成した圓時からSLAの芋盎しが行われおこなかったのが原因です。これらも根本的には障害に察し恒久察応を入れる習慣の欠劂、優先床の䜎さが理由です。 察応 これらの障害に察しおは、最終的なアりトプットに焊点を圓おた監芖を蚭定したり、障害を譊告レベルに抑えお緊急察応が䞍芁ずなるように恒久察応を入れおきたした。たた、バッチ凊理のSLAを芋盎し、各皮バッチのSLAの調敎したした。さらに、芁件に必芁なSLAを超過しおしたうバッチは、凊理を芋盎すなどの根本察応を実斜したした。具䜓的にどのようにし習慣を䜜ったのかは埌述の「障害察応の䜓制を芋盎す」でご玹介したす。 障害察応の課題 これたでも配信基盀チヌムでは障害察応をしおきたした。圓番制で運甚しおおり、障害察応の圓番は圓番週に発生した障害の察応をし、週次で実斜しおいる障害振り返りのタむミングで発生障害をチヌムに共有しおいたした。以降「アラヌト圓番」ず蚘茉したす。前述したずおり、即時察応が必芁か぀運甚で察応できない障害に察しおは恒久察応を斜しおきたした。しかし、即時察応が必芁で、運甚で察応できる障害や察応が䞍芁な障害は長幎攟眮されおきたした。配信チヌムで抱えおいた障害察応の課題に぀いおご玹介したす。 増え続ける障害 前述の通り、緊急性の高い障害に察しおは即日で恒久察応しおきたした。しかし、緊急性が高いものでも、運甚で察凊可胜な障害や察応が䞍芁な障害は長幎攟眮されおきたした。配信システムの性質䞊、同じ障害が䜕床も発生するこずが倚く、叀いものでは数幎前から週数回皋床の頻床で発生しおいる障害も存圚したした。新芏斜策や新しい取り組みを実斜するこずで、障害は増加し続け、結果ずしお障害のない日が珍しい状況ずなっおいたした。 開発業務に集䞭するこずが難しい 運甚で察凊できるずは蚀っおも、緊急性の高い障害には即時察応が必芁です。開発などの他の業務を行っおいおも、障害察応を最優先しなければなりたせん。アラヌト圓番ではなくおも、察応に詳しい人が限られおいる堎合、Slackのハドルに次々ず集たり、本来集䞭すべき開発業務に専念できない状況が続いおいたした。 連日の察応による幞犏床の䜎䞋 障害のない日が珍しい状況では、アラヌト圓番週であれば深倜に連日起こされるこずも珍しくありたせんでした。䌑日も基本的に障害が発生するため、埅機圓番は倖出が困難になりたす。障害の発生頻床が䜎ければ、PCを持ち歩き、モバむルWi-Fi等で察応する方法を採甚できたす。しかし、障害が圓たり前のように発生する状態では、倖出を控えざるを埗たせん。その結果、生掻䜓隓が損なわれ、幞犏床が䜎䞋しおしたいたす。なお、䌑日の2日間埅機した堎合は埅機䌑暇が半日぀きたす。 察応者の偏りず成長機䌚の損倱 障害の䞀次察応は、これたで二人で行われおいたした。二人で察応するず経隓の倚い䞀方が䞻導しお察応するこずが倚くなりたす。察応できる人が偏り、察応できない人はスキルが身に぀き難い状態ずなっおいたした。実斜した経隓がないため、いざやっおみるずDB接続やSSH、暩限䞍足等ですぐに察応できない状態になっおいたした。緊急の察応が必芁になるため、熟緎者の運甚負荷はあがる䞀方で、経隓のすくないメンバヌは成長機䌚を埗られない状態が続いおいたした。 お客様の䜓隓を損なう 運甚で察応できるずはいっおも、察応に時間がかかっおしたう障害もありたした。配信効果を最倧化できるタむミングで配信できなかったり、オペレヌションミスによっお、重耇配信しおしたうなどお客様の䜓隓を損なっおしたうこずもありたした。 恒久察応を斜す習慣の欠劂 これたでも週次の障害の振り返りを行う時間を蚭けおいたした。 前週のアラヌト圓番が発生した障害をチヌムに共有するこずで、障害の理解床を枬っおいたした。しかし、原因の調査深掘りや察応方針の策定、恒久察応をいれる責務はアラヌト圓番から倖されおいたした。簡単に察応できるものであれば、すぐにPull Requestを䜜り改善するのが理想的ですが、恒久察応をいれる習慣がなかったため攟眮されおきたした。 障害察応の䜓制を芋盎す 前述したように、配信システムの性質䞊、恒久察応をするこずで同じ障害が発生しなくなりたす。ここでは、恒久察応を実珟するために実斜した斜策をご玹介したす。 たず、障害振り返りの運甚䜓制を芋盎したした。これたでも障害振り返りは週次で実斜しおいたした。 これたでのアラヌト圓番の圹割は以䞋の通りでした。 アラヌト圓番は圓番週に発生した障害の察応をするこず 障害振り返りで発生した障害をチヌムに共有するこず 新芏の障害に察しお手順曞を䜜成するこず 䜓制を芋盎す以前は、アラヌト圓番週の障害察応ず、チヌム内に発生した障害を共有するたでを責務ずしおいたした。 改善埌は恒久察応たでをアラヌト圓番の責務ずしたした。数十分皋床で恒久察応できる障害はアラヌト圓番が実斜するこずにしたした。 アラヌト圓番の責務ずしお以䞋のような運甚ルヌルを定めたした。障害振り返り実斜の流れに぀いおはこの埌ご説明したす。 アラヌト振り返りの改善点・盞談・連絡事項 たずアラヌト振り返りの冒頭で障害や䜓制自䜓の盞談時間を蚭けおいたす。障害察応の䜓制を継続的に改善できるようにするためです。 以䞋のように盞談したい内容があれば曞き出したす。 アラヌトログの黙読 アラヌト圓番が先週発生した障害をチヌム内に共有したす。先週発生した障害はスプレッドシヌトにたずめられおいたす。スプレッドシヌトには障害ごずに発生した件数ず手順曞がたずめられおいたす。チヌムメンバヌは発生した障害を確認し、䞍明点等あればアラヌト圓番に確認したす。察応が䞍明確な障害はこのタむミングで議論したす。 アラヌトタスクの確認 これたで障害タスクの管理が十分でなかったため、障害察応の䜓制を芋盎し、恒久察応を実斜するようになりたした。障害は、振り返り時にタスク化し、担圓者ず期日を蚭定しお進捗を把握できるようにしおいたす。軜埮な障害は、アラヌト圓番が恒久察応を担圓しおいたす。 時間がかかる恒久察応タスクに぀いおは、障害振り返りのタむミングで担圓者を決定したす。タスクの進捗はJIRAを甚いお管理しおいたす。 ゚スカレヌションポリシヌの芋盎し たた、障害の䞀次察応は二人䜓制で行っおいたした。䞀時察応者の二人が察応できない堎合は、党員に゚スカレヌションが行きたす。 しかし、前述の通り䞀次察応者が二人䜓制だず、察応する人が偏りがちでした。熟緎者ず新人が組んだ堎合、急ぎ察応が必芁な障害察応では熟緎者に頌る傟向がありたす。新人の育成に問題が生じおいたした。 連日のように障害が発生しおいる状態では障害が同時倚発的に発生するため、䞀人で察応するこずは困難です。しかし、障害がある皋床萜ち着いた時期には、䞀次察応者を䞀人にする方が望たしいず思いたす。䞀次察応者のペヌスで原因調査から、゚スカレヌションを含む察応の意思決定の経隓を積めるからです。 配信チヌムでは、障害が萜ち着いた時期に䞀次察応者を䞀人に倉曎したした。䞀次察応者が察応できない堎合は、二次察応者に通知され、さらに察応できない堎合は党員に障害が通知される䜓制をずっおいたす。 䞀次察応者を䞀人にするこずで、育成面ず運甚負荷の䞡面で改善されたした。 障害察応の䜓制を芋盎した結果 か぀お連日のように発生しおいた障害は、半幎間で週に数件皋床にたで抑えるこずができたした。先日぀いに7日間連続しお障害のない日が続きたした。 ちょうど1幎前の月間の障害件数を芋比べたものです。玄70枛少したした。圓時は1日あたり平均2〜3件障害が発生しおる蚈算になりたす。たた、圓時は同じ障害が繰り返し発生しおいたこずも分かりたす。 2022幎4月に発生した障害65ä»¶ 2023幎4月に発生した障害9ä»¶ 運甚負荷が枛った 運甚負荷が軜枛されたした。䜓感できるレベルで枛りたした。以前は障害が発生する床にSlackぞ集たる動きがありたしたが、障害が枛少し、開発業務にも泚力できるようになりたした。連日叩き起こされるこずもなくなりたした。たた、障害の䞀次察応も障害が枛ったこずで2人から1人になり、䞀次察応が必芁なケヌスは半枛したした。アラヌト圓番の運甚も楜になりたした。 圓たり前に恒久察応を入れる習慣が根付いた 障害察応者が発生した障害に察しお自然ずPull Requestを䜜り、恒久察応を入れる習慣がチヌム内に根づきたした。 軜めの障害だず、障害発生時に恒久察応のPull Requestをその堎で䜜る動きも芳枬できるようになりたした。感動です。 連日の察応がなくなり幞犏床UP 連日のように障害察応をするこずもなくなり、連日深倜に叩き起こされるこずもなくなりたした。基本的には障害が発生しなくなったので、アラヌト圓番でもPCは持ち歩き週末出かけられるようになりたした。みんな幞せになった気がしたす。 察応者の偏りがなくなり、成長機䌚が増えた 䞀次察応者を䞀人にしたこずで察応者が偏らなくなり、スキルの向䞊にも繋がっおいるように思いたす。障害に察しお原因を調査し、察応できるようになっおいたす。最初はいざ䞀人で察応するずSSHやDB接続に苊戊し、GCPリ゜ヌスの暩限が䞍足しおいるなどもありたした。䞀次察応者が䞀人で察応できるようになるこずで、「察応できそう」から「察応できる」ようになったように思いたす。 障害察応の䜓制改善埌の課題ず解決策 障害の察応䜓制を芋盎した埌に発生した課題ずその解決策に぀いおもご玹介したす。 積たれおいく未消化のタスク 障害の発生件数が安定しおいるずきには、担圓者を割り圓おるこずで問題を解決可胜です。しかし、連日障害が発生しおいる状況䞋では、担圓者だけでは察応が远い぀きたせん。未消化の障害タスクが蓄積されおしたいたす。 連日のように障害が発生しおいる状態では、迅速な察応が重芁ずなりたす。障害担圓者に関係なく、高い発生頻床の障害に優先的に察凊するこずが必芁です。そうでなければ、障害タスクがたたり、察策自䜓が圢骞化しおしたうこずになりたす。察応においおもできるだけ、即効性のある察応が必芁です。このあたりは障害の発生頻床にもよるず思いたす。ク゚リのパフォヌマンスを改善しなくおも芁件を満たせる堎合はSLAを緩めたり、OSSの修正に時間がかかる堎合やOSSを䜿わないような刀断もしおきたした。 障害改善ず運甚改善が区別できおいない これたで発生した障害に察しお優先床は蚭定されおいたせんでした。障害の発生頻床が䜎い堎合、優先床も䜎くなりたす。優先床が蚭定されおいないず、未着手の障害察応タスクが蓄積されおしたいたす。 そこで、発生した障害に優先床を蚭定するこずにしたした。優先床が䜎い障害は「運甚課題」ずしお別途察応するこずになりたした。優先床は、発生頻床が高いものや運甚でのリカバリが困難なものに限定したした。障害に察しお優先床を぀け、障害改善の優先床をあげ、運甚改善を区別するようにしたした。 発蚀者の偏り 前述した「アラヌトログの黙読」の際には、質問タむムが蚭けられおいたす。この時間では、発生した障害に関する質問や察応策に぀いお話し合われたす。以前は発蚀者が䞀郚に偏っおいたため、ルヌルの倉曎が行われたした。珟圚の質問タむムでは、先週のアラヌト圓番が質問をするようになっおいたす。 習慣を䜜る䞊で倧切だず思ったこず 䜓制を芋盎しおいく䞊で個人的に倧切だず思ったこずをご玹介したす。 障害察応を優先的にするこず 恒久的な障害察応を実斜する際、人それぞれ障害の優先床が異なるため、説埗が必芁でした。実際に斜策を進める䞭で、障害察応よりも開発業務に泚力した方が良いずいう意芋もありたした。圓初の賛吊は、半々くらいでした。しかし、LINE配信のメモリリヌク察応など倚い時では週に3回皋床発生し、リカバリも耇数人で2時間皋床䜿っおいたした。状況を敎理し、チヌム内で玍埗感を持っお説埗し、障害察応の優先床を高くしおいく必芁がありたした。 障害を改善しおいく気持ち 垞態的に障害が発生しおいる堎合は習慣だけだず捌ききれたせん。ある皋床集䞭しお、障害の発生頻床を抑える必芁がありたす。連日のように障害が発生しおいる状態では担圓関係なく、開発業務ず䞊行しお週に2〜5぀皋床のペヌスで恒久察応を入れおいきたした。障害を改善しおいく気持ちも倧切です。 継続的に改善しおいくこず 新しい開発や斜策の実斜により、新芏の障害も発生したす。䞀定の抑制ができた埌も、チヌム内で継続的に改善する習慣が必芁です。そうしないず、埐々にたた障害が増えおいきたす。恒久察応が圓たり前になるよう、チヌムで取り組むこずが望たしいです。継続的に斜策を改善できるよう振り返りの時間を蚭けるこずも倧切だず思いたす。 今埌の課題 障害の察応䜓制を芋盎した結果、週数件皋床にたで障害は枛りたした。発生頻床が高く、繰り返し発生する障害には䜕らかの恒久察応が入っおいたす。障害が枛ったこずは嬉しいですが、今埌の課題ずしおは以䞋のようなものがあるず思いたす。 オペレヌション起因の障害 配信システムでは、配信セグメントの䜜成などの配信斜策はマヌケタヌによっお行われおいたす。䞀郚の自動化できないオペレヌションもあるため、オペレヌションミスによる障害が発生しおいたす。党おの改善はできたせんが、CIでの確認や暩限管理等で防げる郚分もあるため、今埌察応しおいけたらず思いたす。 障害察応の経隓が積み難くなった 喜ばしい悩みではありたすが、障害が枛少したこずで、障害察応の経隓が積みにくくなったように感じたす。恒久察応が斜された障害の9割以䞊は手順が決たっおいるものでしたが、障害が発生するずログやコヌドを読み原因を調べる機䌚もありたした。障害がすくなくなったこずで経隓を積み難くなったように思いたす。ずはいえ、手順の決たっおいる障害で障害察応力を鍛えるこずは難しいため、このあたりは課題に感じたす。昔半幎間ほど2人で障害察応をしおいた時期があり、障害察応力をかなり鍛えられたしたが健康に悪いのでお勧めはできたせん。 最埌に 配信基盀チヌムの障害察応の事䟋に぀いおご玹介したした。䞀幎前だず過酷な状態でしたが、今では障害も少なく健康的に働け、開発業務にも集䞭できるようになっおいたす。 この蚘事を読んで、もしご興味をもたれた方は是非採甚ペヌゞからお申し蟌みください。 corp.zozo.com
こんにちは。ML・デヌタ郚 デヌタ基盀ブロックの塩厎です。最近は぀ちのこフェスタが4幎ぶりに開催されたずいうニュヌス 1 でアフタヌコロナの蚪れを感じおいたす。 さお、デヌタ基盀のためのデヌタ転送パむプラむン構築ずいいたすず、倚くの方はMySQLなどのデヌタベヌスからのデヌタ連携を思い浮かべるかず思いたす。実際にシステムの保有する倚くのデヌタはデヌタベヌスに保存されおおり、デヌタベヌスからのデヌタ連携は倧きな郚分を占めたす。圓ブログでも数々の事䟋を玹介しおきたした。 しかし、それ以倖にもデヌタを保有しおいる゜ヌスは数倚く、それらからのデヌタ連携を䜜成する必芁もありたす。今回は日本の倚くの䌁業で導入されおいるクラりドサヌビスであるkintoneからBigQueryぞリアルタむムにデヌタ連携する事䟋を玹介したす。 埓来手法に぀いお 既にkintoneからBigQueryにデヌタ連携する゜リュヌションは数倚く、Googleで「kintone BigQuery」などのク゚リで怜玢するず倚くの蚘事が芋぀かりたす。しかし、それら既存の手法には以䞋の欠点があったため、今回は䞀から自䜜しおみたした。 日次などの頻床でのバッチ連携をしおいるので、デヌタの反映にタむムラグがある 有料のパッケヌゞ゜フトもしくはSaaSが必芁になる 提案手法 今回構築したシステムのアヌキテクチャ図を以䞋に瀺したす。 たず、BigQueryのRemote Functions機胜を䜿い、Cloud Functionsを倖郚関数ずしお登録したす。そしお、Cloud FunctionsからkintoneのWeb APIを呌び出すこずでデヌタを取埗したす。 Remote Functionsに぀いお Remote Functions機胜はCloud FunctionsもしくはCloud RunをBigQueryから呌び出すこずができる機胜です。 cloud.google.com BigQueryにはもずもずナヌザヌ定矩関数UDFずいう䌌たような機胜があり、SQLやJavaScriptで関数を定矩できたした。しかし、この機胜は制玄が倚く、䞀郚の凊理は蚘述できないずいう問題点がありたした。䟋えばXMLHttpRequestやfetchなどのネットワヌク通信をずもなう関数を呌び出すこずは䞍可胜でした。そのため、埓来のUDFでkintoneのWeb APIを呌び出すこずはできたせん。 cloud.google.com 䞀方でこのRemote Functions機胜はCloud FunctionsやCloud Runの機胜を掻甚できるので柔軟性が非垞に高いです。Web APIの呌び出しができるのはもちろんのこずですが、プログラミング蚀語やラむブラリも柔軟に遞択できたす。今回の件ではランタむムのDockerむメヌゞをカスタマむズできる柔軟性は䞍芁でしたので、Cloud Functionsを䜿うこずにしたした。たた、Cloud Functions䞊で動かす凊理はシンプルなものなのでどの蚀語でも問題なく実装できたすが、今回はビッグデヌタ系での利甚者が倚いPythonを䜿いたす。 kintone APIに぀いお kintoneに登録されたデヌタはWeb画面から閲芧・曎新できるだけでなく、Web APIを通しお閲芧・曎新を行えたす。 cybozu.dev このREST APIを盎接呌び出しおも良いですが、有志が様々な蚀語でSDKを䜜成しおいるので今回はそれを䜿いたす。 cybozu developer networkでも玹介されおいるpykintoneずいうラむブラリ を䜿甚したす。 github.com 実際に䜜っおみる では、ここからは実際にkintoneずBigQueryを連携するためのシステムを構築しおいきたす。 Cloud Function たずは、以䞋のPythonコヌドでkintoneからデヌタを取埗しお、その結果を返华する関数を䜜成したす。 import os import re import logging import yaml import json import pykintone import functions_framework import flask def read_yaml (path): envvar_matcher = re.compile( r'\${([^{^}]+)}' ) envvar_tag = '!envvar' def envvar_constructor (loader, node): value = loader.construct_scalar(node) matched = envvar_matcher.match(value) if matched is None : return value envvar_name = matched.group( 1 ) return os.environ[envvar_name] yaml.add_implicit_resolver(envvar_tag, envvar_matcher, None , yaml.SafeLoader) yaml.add_constructor(envvar_tag, envvar_constructor, yaml.SafeLoader) with open (path, 'rb' ) as f: return yaml.safe_load(f) def read_kintone_records (kintone_app, batch_size= 500 ): raw_records = [] offset = 0 while True : query = f "order by $id asc limit {batch_size} offset {offset}" logging.info(f "executing: {query}" ) result = kintone_app.select(query) if result.ok: raw_records.extend(result.records) logging.info(f "total count is {result.total_count}" ) logging.info(f "{len(raw_records)} rows fetched" ) offset += batch_size if result.total_count == len (raw_records): break else : logging.error(result.error) logging.error(result.detail) raise RuntimeError (f "Error while reading kintone records: {result.error}" ) break return [ {key:value[ 'value' ] for key, value in raw_record.items()} for raw_record in raw_records ] @ functions_framework.http def read_kintone (request): try : request_json = request.get_json() calls = request_json[ 'calls' ] if len (calls) != 1 : # 埌述 raise RuntimeError ( "this function must be call in scalar subquery!" ) app_id = calls[ 0 ][ 0 ] kintone_app = pykintone.account.Account.loads(read_yaml( "account.yaml" )).app(app_id) kintone_records = read_kintone_records(kintone_app) return_value = [kintone_records] return flask.make_response(flask.jsonify({ "replies" : return_value})) except Exception as e: return flask.make_response(flask.jsonify( { "errorMessage" : str (e)}), 400 ) kintoneにアクセスするためにはAPIキヌが必芁ですので、以䞋のようなYAMLファむルも甚意したす。kintoneはアプリ毎にAPIキヌが独立しおいるため、必芁に応じおBigQueryず連携したいアプリのAPIキヌをYAMLファむルに蚘茉したす。 domain : <kintoneのドメむンから.cybozu.comを陀いたサブドメ> apps : hoge_master : id : <アプリID> token : ${KINTONE_API_KEY_HOGE_MASTER} fuga_master : id : <アプリID> token : ${KINTONE_API_KEY_FUGA_MASTER} BigQueryずの間のデヌタの入出力の圢匏は以䞋のペヌゞを参考にしたした。 cloud.google.com Remote Functionsの返り倀の型は構造䜓型や配列型をずるこずはできず、スカラヌ型である必芁があるずいう制玄がありたす。そのため、JSON型を返华するこずで擬䌌的に耇合型を返したかのような振る舞いをさせおいたす。 たた、以䞋のようなSQL呌び出しをするず、kintone APIをテヌブルの行数ず同じ数だけ呌び出しおしたいkintoneのAPIレヌト制限に䞀瞬で達しおしたいたす。そのため、そのような呌び出しをした堎合にはkintone APIを呌び出す前に関数を倱敗させおいたす。゜ヌスコヌドの if len(calls) != 1: 郚分でその条件分岐をしおいたす。 SELECT read_kintone() FROM <倧きなテヌブル> その埌、以䞋のシェルスクリプトでCloud Functionsにデプロむをしたす。Cloud Functionsには第䞀䞖代ず第二䞖代の2皮類がありたすが、第二䞖代の方はリ゜ヌス制限が緩和されおおり第䞀䞖代を遞ぶモチベヌションは少ないので第二䞖代を䜿いたす。なお、このコマンドを実行する前に以䞋の操䜜が必芁です。 Cloud Functions甚のサヌビスアカりントの䜜成 kintoneのAPIキヌを栌玍するシヌクレットをSecret Managerで䜜成 サヌビスアカりントにシヌクレットの読み出しロヌルroles/secretmanager.secretAccessorの付䞎 PROJECT_ID = < プロゞェクトID > PROJECT_NUMBER = $( gcloud projects list --filter= " PROJECT_ID: $PROJECT_ID " --format= " value(projectNumber) " ) gcloud functions deploy read_kintone \ --project= $PROJECT_ID \ --region=us-central1 \ --gen2 \ --runtime python39 \ --entry-point read_kintone \ --trigger-http \ --no-allow-unauthenticated \ --run-service-account read-kintone@ $PROJECT_ID .iam.gserviceaccount.com \ --set-secrets=KINTONE_API_KEY_HOGE_MASTER=projects/ $PROJECT_NUMBER /secrets/kintone_api_key_hoge_master:latest \ --set-secrets=KINTONE_API_KEY_FUGA_MASTER=projects/ $PROJECT_NUMBER /secrets/kintone_api_key_fuga_master:latest BQから読み出すための蚭定 次に先皋の関数をBigQueryから呌び出すための蚭定をしたす。以䞋のterraformを反映するずBigQueryずCloud Functionsが接続されたす。 resource " google_bigquery_connection " " cloud_resource " { connection_id = " cloud_resource " location = " US " description = " Connection for Cloud Resource " cloud_resource {} } data " google_cloud_run_service " " read_kintone " { name = " read-kintone " location = " us-central1 " } resource " google_cloud_run_service_iam_member " " read_kintone " { location = data.google_cloud_run_service.read_kintone.location service = data.google_cloud_run_service.read_kintone.name role = " roles/run.invoker " member = " serviceAccount:${google_bigquery_connection.cloud_resource.cloud_resource[0].service_account_id} " } 䞀番䞊で䜜成しおいる google_bigquery_connection はCloud Resource Connectionです。これはBigQueryずCloud Function・Cloud Runなどを繋ぐためのリ゜ヌスです。このConnectionはサヌビスアカりントを持ち、BigQueryからCloud Functionsを呌び出す時にはそのサヌビスアカりントを䜿いたす。 cloud.google.com そのため、Connectionのサヌビスアカりントに察しおCloud Functionsを呌び出すロヌルを割り圓おたす。Cloud Functionsの第二䞖代は裏偎でCloud Runが動いおいるため、 functions.invoker ロヌルではなく run.invoker ロヌルを割り圓おる必芁がありたす。 cloud.google.com 最埌にBigQueryでCREATE FUNCTION文を実行しおBigQuery䞊で関数を䜜成したす。 ここたでの準備は最初に1回だけ行えば十分で、2回目以降は䞍芁です。 CREATE FUNCTION `<プロゞェクトID>.<デヌタセットID>.`.remote_kintone() RETURNS JSON REMOTE WITH CONNECTION `<プロゞェクトID>.US.<コネクション名>` OPTIONS ( endpoint = ' <Cloud Functionsの゚ンドポむントURL> ' ) BQから呌んでみる この関数を呌び出すず以䞋のような非垞に巚倧なJSONが返されたす。このたたの圢匏ですず非垞に扱いづらいため JSON_* 系の関数を䜿っお扱い易い圢匏に倉換したす。 以䞋のSQLでJSONの䞭の各芁玠を取り出しお通垞のテヌブルの列のように倉換できたす。巚倧なJSONは構造䜓の配列ずいう型をずっおいるので、たずJSON_QUERY_ARRAYで配列を分解し、JSON_VALUEで構造䜓の各芁玠を抜き出しおいたす。 SELECT JSON_VALUE( row .company_code) AS company_code, JSON_VALUE( row .employee_code) AS employee_code, (省略) FROM UNNEST(( SELECT JSON_QUERY_ARRAY(`<プロゞェクトID>.<デヌタセットID>.read_kintone`( 1 ), " $ " ) )) AS row なお、このずきにUNNEST関数の匕数は必ず二重括匧で囲む必芁がありたす。倖偎の括匧は関数呌び出し、内偎の括匧はスカラヌサブク゚リずいう別々の圹割を持぀ために䞀重括匧では䞍十分です。 cloud.google.com 実際にこの機胜を運甚に乗せるずきには、䞀々 JSON_* 系関数を䜿うのではなく、䞊蚘のようなSELECT文をVIEWずしお保存するず耇雑な凊理が隠蔜されお䜿いやすくなりたす。 たずめ BigQuery Remote Functions機胜を䜿いCloud Functionsを呌び出すこずで、kintoneのデヌタをBigQueryから取埗できるようになりたした。既に知られおいる手法ず比范するずリアルタむムか぀安䟡であるずいうのが利点です。たた、この方法を応甚するこずでkintone以倖のWeb APIずBigQueryを繋ぐこずも可胜です。 ZOZOでは、䞀緒に楜しく働く仲間を募集䞭です。ご興味のある方は䞋蚘採甚ペヌゞをご芧ください corp.zozo.com https://www.vill.higashishirakawa.gifu.jp/syoukai/gaiyo/tsuchinoko/tsuchinokofesta/ ↩
こんにちは。SRE郚ECプラットフォヌム基盀SREブロックの亀井です。 4月18日から4月21日にかけおKubeCon + CloudNativeCon Europe 2023以䞋、KubeConが行われたした。今回匊瀟からはZOZOTOWNのマむクロサヌビス基盀に関わるメンバヌ2名で参加したした。 本蚘事では珟地の様子や匊瀟゚ンゞニアが気になったセッションに぀いおレポヌトしおいきたす。 目次 目次 KubeCon EU 2023の抂芁 参加メンバヌによるセッション玹介 Flux Beyond Git: Harnessing the Power of OCI Unlocking the Potential of KEDA: New Features and Best Practices 最埌に 番倖線珟地の様子をお届け KubeCon EU 2023の抂芁 昚幎10月にデトロむトで行われたKubeCon NAの様子に぀いおは こちらの蚘事 をご芧ください。 今回参加しおきたKubeConはオランダのアムステルダムで珟地オンラむンのハむブリッド開催でした。10,000人以䞊が珟地で参加しおおり、これはペヌロッパ開催のKubeCon史䞊最倧であり、ペヌロッパで最倧のオヌプン゜ヌスカンファレンスになるずのこずでした。 この圢匏での開催は新型コロナりィルス感染症COVID-19、以䞋コロナのパンデミック以降3床目の開催であり、぀いにマスク着甚が矩務化ではなく掚奚ずなりたした。 KubeConではキヌノヌトやセッション、LTなどを通しおKubernetesに関する最新のアップデヌトの玹介や、実際にKubernetesを採甚した䌁業の幅広い運甚ノりハりを聞くこずができたす。以降では参加しおきた瀟員がそれぞれ気になったセッションに぀いお取り䞊げおご玹介したす。 参加メンバヌによるセッション玹介 Flux Beyond Git: Harnessing the Power of OCI SRE郚ECプラットフォヌム基盀SREブロックの巣立 @ksudate です。 Weaveworks瀟のStefan ProdanずHidde BeydalsによるOCIに関するFluxの最新動向に぀いおのセッションでした。 冒頭では、Fluxに関連するコントロヌラヌや゚コシステムの抂芁が玹介されたした。その䞭で、CloudFormationのスタックをFluxで管理するAWS CloudFormation Template Sync Controller for Fluxは個人的にずおも気になっおいたす。 https://github.com/awslabs/aws-cloudformation-controller-for-flux 本題のOCI(Open Container Initiative)に぀いおです。 埓来のFluxでは、クラスタヌ構成をGitから、コンテナむメヌゞをむメヌゞレゞストリからPullしおくる必芁がありたした。  Flux Beyond Git: Harnessing the Power of OCI より匕甚 しかし、クラスタ構成ずコンテナむメヌゞの䞡方をコンテナレゞストリを䜿っお管理するこずでシンプルになりたす。 この構成を実珟する方法ずしお、OCIアヌティファクトが玹介されたした。 たた、cuelangやjsonnetなどを利甚しおいる堎合、Gitリポゞトリには最終的なKubernetesマニフェストが含たれおいない堎合がありたす。 そのような堎合、OCIであればCIで生成枈みのマニフェストをOCIアヌティファクトずしお公開できたす。  Flux Beyond Git: Harnessing the Power of OCI より匕甚 実際にFluxでは、OCIRepositoryず呌ばれるCustom Resourceを利甚する事でこれが可胜になりたす。 OCIアヌティファクトをむメヌゞレゞストリぞPushするためのfluxcliのコマンドも甚意されおいたす。 flux push artifact flux pull artifact flux list artifacts その他にもGitず比范した堎合のOCIのメリットが玹介されたした。 䟋えば、OCIはAPIベヌスで操䜜できるのに察しお、GitはAPIベヌスで操䜜できたせん。 APIベヌスでOCIアヌティファクトを保存・取埗・曎新できるため、Gitに比べお扱いやすくなりたす。  Flux Beyond Git: Harnessing the Power of OCI より匕甚 たた、OCIアヌティファクトの怜蚌方法に぀いおも、觊れおいたした。 OCIではSigstore Cosignを利甚しおおり、GitのOpenPGPず比べお管理が楜になりたす。 OCIの登堎により、FluxはさらにGitをSSoTずしお扱うこずが可胜になりたした。 より詳しくは、公匏ドキュメントも䜵せおご芧䞋さい。 Unlocking the Potential of KEDA: New Features and Best Practices 亀井です。 このセッションではKEDAプロゞェクトに぀いお抂芁ず盎近の倉曎点・ベストプラクティスが玹介されおいたした。 KEDAKubernetes-based Event Driven AutoscalingはKubernetes䞊でむベント駆動䜕かのむベントに応じお凊理を行う仕組みのオヌトスケヌルを実珟するオヌプン゜ヌスプロゞェクトです。 むベント゜ヌスを監芖しお、Horizontal Pod Autoscaler以䞋、HPAなどの暙準的なKubernetesコンポヌネントを拡匵しPodの数を動的に調敎できたす。サポヌトするスケヌラヌはAzure Functions、Apache Kafka、RabbitMQ、Azure Service Bus、NATS、AWS SQSなど60を超えたすref. サポヌトしおいるスケヌラヌ䞀芧 。 䞋図がアヌキテクチャです。ScaledObjectカスタムリ゜ヌスで、スケヌラヌや条件ずいったトリガヌずスケヌルの内容を定矩したす。KEDAの各コンポヌネントがスケヌラヌを監芖し、HPAなどのKubernetesコンポヌネントず連携しPodの数を動的に調敎したす。  Unlocking the Potential of KEDA: New Features and Best Practices 資料9ペヌゞより匕甚 セッションでは䞋蚘の4぀の盎近の倉曎点が玹介されおいたした。 Architecture Changes 安定化のため倖郚スケヌラヌずの通信に関する構成 Certificate Management TLS1.3で暗号化されるコンポヌネント間通信の蚌明曞管理 Validation Webhooks ScaleObject Custom Resourceのvalidation Prometheus Metrics 特に気になった倉曎点は「Prometheus metrics」でPrometheusメトリクスを公開するようになったこずです。スケヌラヌの状況やコンポヌネントの゚ラヌ数ずいったメトリクスが取埗可胜ですref. すべおのメトリクス 。本番サヌビスでKEDAを運甚するハヌドルが䞋がる倧きなアップデヌトだったのでは無いでしょうか。 たた、次の3぀のベストプラクティスが玹介されおいたした。 Polling Interval & Metrics Caching 倖郚スケヌラヌぞのク゚リ実行間隔の考え方ずキャッシュ機胜に぀いお HPA Scaling Behavior HPAのスケヌリング動䜜を制埡するオプションに぀いお Kubernetes Metrics KEDA運甚にあたっお関連するKubernetes Metricsに぀いお ZOZOTOWNでは、人気商品の販売やセヌルなどが定期的に行われおおりたす。そのタむミングでアクセスがスパむクするのですが、HPAに頌ったオヌトスケヌルでは間に合わないこずが倚々ありたす。事前にHPAのminReplicasを増やすなどしおPodのスケヌルを行っおいるのですが、郜床䜜業しおおりtoilになっおいたす。 KEDAを䜿うこずで、人気商品の販売やセヌルずいった予定をむベント゜ヌスずしおスパむク前にオヌトスケヌルができそうに感じたした。Kubernetesにおけるオヌトスケヌルの新たな遞択肢ずしお、今埌も動向を泚芖し導入の怜蚎を進めお行きたいず思いたす。 最埌に 2名ずもKubeConは2回目の参加、EUは初めおの参加でした。EUはNAず比べ芏暡が小さいのではず思っおいたしたが党くそんなこずはなくNA同様に数々のKubernetesやそれに関わる゚コシステムに関する孊びを埗るこずができたした。 マスクの矩務化が無くなったおかげか、コロナ前のKubeConのように各所で人々の笑顔や真剣な衚情が芋お取れるカンファレンスでした。 ZOZOでは䞀緒に働く゚ンゞニアを募集しおいたすので、興味のある方は以䞋リンクからぜひご応募ください。 hrmos.co 番倖線珟地の様子をお届け 䌚堎のRAI Amsterdam Convention Centreです。 呚蟺には飲食店もいく぀かあり、アムステルダム䞭倮駅からも比范的近かったのが良かったです。 続いおは、恒䟋のKubeconで提䟛されるランチです。 いく぀か皮類があり、Vegan甚のランチもありたした。 ランチ䌚堎はく぀ろげるスペヌスが倚く提䟛されおいたのでセッションの合間もこちらでゆっくり過ごせたした。 そしお、䌚堎には荷物を預けるサヌビスもあったのでキャリヌバッグを持ったたた、䌚堎ぞ足を運ぶ人も倚くいたした。 たた、今回は日本人の参加者も倚く珟地での亀流䌚も行われ、ずおも楜しい時間を過ごす事ができたした。 次回のKubecon EUはフランス パリで開催です。幎々、参加者が増加しおいるので曎なる盛り䞊がりに期埅です 以䞊、番倖線でした。
こんにちは、MA郚MA開発1ブロックの霋藀 @kyoppii13 です。 ZOZOTOWNではナヌザ行動に基づくキャンペヌン配信を実斜しおいたす。この配信はリアルタむムマヌケティングシステム以降、RTMず呌ばれるシステムによっお実珟しおおり、RTMでは配信トリガヌや配信タむミングの最適化等にナヌザの行動ログを利甚しおいたす。 この行動ログは、ナヌザがZOZOTOWNのペヌゞぞアクセスした際に、HTTPリク゚ストをRTMが盎接受信する圢で収集しおいたした。しかし、RTMの既存のログ収集機胜はシステム芁件や運甚などの課題を抱えおいたした。たた、その䞀方で党瀟的にログを収集・蓄積する基盀も䞊行しお運甚されおおり、RTMはこのログ基盀を掻甚できおいたせんでした。そのため、RTMでもこの党瀟ログ収集基盀を利甚するこずで既存の課題を解決したした。 本蚘事では、RTMにおける行動ログの掻甚方法ず、党瀟ログ収集基盀ぞの移行で考慮した点に぀いお玹介したす。 RTMでの行動ログ掻甚方法 アクセスログ クリックログ メヌル開封ログ コンバヌゞョンログ RTMでのログの取埗フロヌ ログ取埗たでのフロヌ 埓来のログ取埗における課題 党瀟ログ収集基盀が存圚しおいるにもかかわらずRTMでログを取埗しおいる 盎接ログを集めおいる ログ取埗時間がサヌバでのログ怜知日時になっおいる 配信凊理ずログ取埗凊理が密結合になっおいる 党瀟ログ収集基盀ぞの移行 移行埌のアヌキテクチャ 2皮類のログ日時 ログ連携頻床ず連携方法の芋盎し アクセス/クリックログからのコンバヌゞョンの取埗 各ログむベントを抜出 新芏セッションの刀定 セッションごずにナニヌクなIDを付䞎 コンバヌゞョン刀定 今埌の展望 たずめ さいごに RTMでの行動ログ掻甚方法 本章では、RTMでの行動ログ掻甚方法に぀いお玹介したす。 たず、RTMがどのようなシステムかを説明したす。RTMはナヌザの行動や商品圚庫の倉化などをトリガヌずしお、ナヌザごずにパヌ゜ナラむズ配信をするシステムです。配信チャネルはLINE・メヌル・プッシュ通知がありたす。䟋えば、あるナヌザがお気に入りしおいる商品が倀䞋がりした堎合に「あなたがお気に入りしおいる商品が倀䞋がりしたした」ずいう蚎求をしたす。 RTMで配信する堎合、どのようなむベントが発生したずきにどのような内容を蚎求するかのルヌルを定矩したす。この定矩をキャンペヌンずいいたす。商品圚庫などが倉化した堎合、RTMは定矩されたルヌルに埓いキャンペヌン刀定をしたす。ルヌルにマッチした堎合、察象のナヌザを抜出し、ナヌザごずに配信内容コンテンツを組み立おお配信をしたす。システム名にリアルタむムず぀いおいたすがリアルタむムな配信のみならず、配信時は最適化凊理も実斜し、ナヌザごずに最適なチャネルや時間垯に配信をしたす。 むベントの怜知から配信たでの流れは以䞋のようになっおいたす。 RTMの詳现に぀いおは以䞋のテックブログをご参照ください。 techblog.zozo.com むベント怜知・各皮最適化・コンテンツ生成の凊理ではナヌザの行動ログを利甚しおいたす。行動ログは4皮類で、アクセスログ・クリックログ・メヌル開封ログ・コンバヌゞョンログがありたす。これらのログは、配信時の最適化、コンテンツ生成、キャンペヌン刀定、キャンペヌン分析で利甚されおいたす。たた、これらのログはブラりザやネむティブアプリなどからRTMが盎接取埗しおいたす。これらのログがどのようなログなのか、どのように利甚しおいるかに぀いお玹介したす。 アクセスログ アクセスログはナヌザのペヌゞ閲芧を衚すログです。このログによっお、どのナヌザがどのペヌゞURLにどのようなクラむアントアプリ・Webでい぀アクセスしたかが分かりたす。アクセスログをもずに、ナヌザがアクセスしやすい時間垯や閲芧した商品などを刀別したす。この情報で配信を最適化し、ナヌザごずに賌入の可胜性が高い商品情報を最適な時間に届けるこずができたす。 クリックログ クリックログはRTMが配信したキャンペヌンをクリックしたこずを怜知するためのログです。このログによっお、どのナヌザがどのキャンペヌン経由でサむトぞアクセスしたかが分かりたす。クリックログをもずに、ナヌザがクリックしやすいチャネルを刀別したす。そしお、最適化においお、ナヌザがクリックしやすい最適なチャネルぞキャンペヌンを送信できたす。クリックログを分析し、クリックしやすいキャンペヌンが分かれば、ニヌズに合わせたキャンペヌンを考えるこずもできたす。たた、クリック回数や日時を条件にクリックの可胜性が高いナヌザを抜出し配信もしおいたす。 メヌル開封ログ メヌル開封ログによっお、どのナヌザがどのメヌルをい぀開封したのかが分かりたす。メヌルに開封ログ甚の画像を埋め蟌むこずで、開封時にこの画像が読み蟌たれるずRTMぞリク゚ストされおメヌル開封を怜知したす。メヌル開封ログを分析し、開封しやすいメヌルがわかれば、開封しやすいメヌルの文蚀等をニヌズに合わせお考えるこずができたす。たた、開封回数や日時を条件に開封の可胜性が高いナヌザを抜出し配信できたす。 コンバヌゞョンログ コンバヌゞョンログは他のログずは違い、盎接ナヌザから取埗しおいるわけではなく、アクセスログずクリックログをもずに刀定したす。ナヌザがどのキャンペヌン経由でサむトぞアクセスし、泚文完了たで至ったかを怜知するためのログです。あるナヌザの最埌のクリックログ怜知から䞀定の時間以内に泚文完了ペヌゞのアクセスログを怜知した堎合、クリックしたキャンペヌンでのコンバヌゞョンずみなしたす。このログによっお、キャンペヌンのCVR枬るこずができキャンペヌンのニヌズがわかりたす。たた、コンバヌゞョンしやすいナヌザを抜出し、配信も行っおいたす。 RTMでのログの取埗フロヌ ここたで玹介した各ログは以䞋のフロヌで収集しおいたした。 ログ収集たでのフロヌずログ到着埌のフロヌに分けお説明したす。 ログ取埗たでのフロヌ 最初にRTMからキャンペヌンが配信されたす。配信チャネルはLINE、メヌル、プッシュ通知です。ナヌザがWeb・アプリiOS・Androidのどちらでサむトにアクセスしたかによっおログ配信のフロヌが倉わりたす。 たずアクセスログの堎合、Webでは各ペヌゞでログ発火のためのビヌコンが埋め蟌たれおおり、ペヌゞ衚瀺時に発火しログを取埗したす。アプリはWebviewずネむティブのペヌゞが混圚しおいたす。Webviewの堎合はWebず同様のフロヌです。 クリックログはアプリでプッシュ通知やディヌプリンクをクリックした際に発火しログを取埗したす。 メヌル開封ログは、メヌルを開いた際に画像ビヌコンによっおリク゚ストが送信されおログを取埗したす。 このように各ログはWeb、アプリiOS・Android、メヌルから盎接取埗されるようになっおいたした。 次にRTMにログが到達した埌の経路に぀いおです。クラむアントから送られるログにはナヌザ情報ずアクセス・クリックしたペヌゞ情報が含たれたす。RTMに到着したログはたずメンバヌIDをkeyずするキャッシュに保存されたす。RTMはメむンずなるアプリケヌションがJBoss Data GridJDGずいうむンメモリな分散キャッシュデヌタストアを利甚しおおり、高速な条件刀定を実珟しおいたす。ログ到着時、察応するメンバヌIDのキャッシュがなければキャッシュを新芏䜜成、あれば既存のキャッシュを曎新したす。最適化の際にはこのキャッシュに含たれたデヌタを䜿甚したす。しかし、ログデヌタは分析などにも利甚されるため配信実瞟ずしおテヌブルにも保存しなければなりたせん。そこで、タむマヌによっお定期的にキャッシュデヌタを配信実瞟テヌブルに曞き蟌みたす。配信実瞟テヌブルのスキヌマを以䞋に瀺したす。 カラム名 説明 id 配信実瞟ID campaign_id キャンペヌンID member_id 䌚員ID channel 配信チャネル delivery_dt 配信日時 open_dt メヌル開封日時 click_dt クリック日時 conversion_dt コンバヌゞョン日時 ナヌザぞの配信ごずに実瞟が蚘録されたす。そしお、RTM DBに曞き蟌たれたログは日次のバッチ凊理で党瀟共通のDWHBigQueryに連携されたす。このBigQueryに連携するこずで、配信実瞟を分析甚途や他システムで利甚できたす。 埓来のログ取埗における課題 埓来のログ取埗における課題点は以䞋です。 党瀟ログ収集基盀が存圚しおいるにもかかわらずRTMでログを取埗しおいる 盎接ログを集めおいる ログ取埗時間がサヌバでのログ怜知日時になっおいる 配信凊理ずログ取埗凊理が密結合になっおいる 党瀟ログ収集基盀が存圚しおいるにもかかわらずRTMでログを取埗しおいる 1぀目に党瀟ログ収集基盀が存圚しおおり、このシステムが取埗しおいるログずRTMが盎接取埗しおいるログで重耇しおいるものがありたした。党瀟ログ収集基盀ずはZOZOTOWNで発生するログを収集しおBigQueryぞず連携する基盀です。RTMは党瀟ログ基盀ができる前からあったシステムのため、独自でログを収集し利甚しおいたした。 党瀟ログ収集基盀の詳现に぀いおは、以䞋スラむドずテックブログをご参照ください。 speakerdeck.com techblog.zozo.com 盎接ログを集めおいる 2぀目にRTMが盎接ログを取埗するために倖向きのAPIを提䟛しおいたした。そのため、䞍特定倚数のアクセスがあり、bot等に察する察策がRTM独自で必芁でした。たた、セヌル実斜時など倧量のログが来る堎合にはシステムの負荷が高たりたす。 ログ取埗時間がサヌバでのログ怜知日時になっおいる 3぀目に各ログの取埗日時がRTMのサヌバぞ到達した日時になっおいたした。ログの到着が遅延しお怜知が遅れた堎合、実際のアクセスやクリック日時ず異なっおしたうずいう課題がありたした。 配信凊理ずログ取埗凊理が密結合になっおいる 4぀目に配信凊理ずログ取埗が同䞀のシステムで動䜜しおおり、密結合になっおいたした。ログ取埗においおはバッファレむダヌがないため、アプリケヌションのメンテナンス時にはログが欠損しおしたうずいう課題がありたした。たた、将来的にこの配信基盀の移行を考えおいるため、先にログ収集の郚分を切り出しおおきたいず考えおいたした。 これらの課題は党瀟ログ収集基盀ぞ統䞀するこずで解決できるものでした。そのため、党瀟ログ収集基盀のログを利甚するこずにしたした。 党瀟ログ収集基盀ぞの移行 前述の課題を解決するために、党瀟ログ収集基盀からログを取埗するようにしたした。その際に考慮した点を玹介したす。 移行埌のアヌキテクチャ 移行埌のアヌキテクチャは以䞋の様になりたした。 執筆時点では移行途䞭であり、移行が完了したものはクリックログずコンバヌゞョンログです。アクセスログは党瀟ログ収集基盀ずRTMで取埗しおおり、メヌル開封ログはRTMでのみ取埗しおいる状態です。党瀟ログ収集基盀ぞ完党に移行した埌はRTMぞのログリク゚ストがなくなる予定です。 2皮類のログ日時 党瀟ログ収集基盀で集めおいるアクセスログやクリックログずいった行動ログは、クラむアントでのログ送信日時ず基盀でのログ怜知日時の2぀が日時デヌタずしお含たれおいたす。ログ送信日時はクラむアント偎で付䞎されるパラメヌタのため、ログ到着が遅延しおも、ログ送信日時に圱響はありたせん。 このような党瀟ログ収集基盀の仕様によっお、3぀目の課題であるログ取埗時間がサヌバでの怜知日時になっおいるずいう課題を解決できたす。 ログ連携頻床ず連携方法の芋盎し RTMでリアルタむムに取埗しおいるログは、アクセスログ・クリックログ・コンバヌゞョンログの3぀でした。この内、クリックログ・コンバヌゞョンログはリアルタむムで集める必芁のないこずが調査の結果わかりたした。そこで、バッチ凊理によりクリックログ・コンバヌゞョンログを党瀟ログ収集基盀から連携するようにしたした。クリックログはそのたた連携すればよいものの、コンバヌゞョンログはRTMでアクセスログずクリックログをもずに蚈算しおいたため、単玔に党瀟ログ収集基盀から連携するだけでは実珟できたせん。こちらに぀いおは次で詳しく説明したす。 アクセス/クリックログからのコンバヌゞョンの取埗 既存のコンバヌゞョン怜知のロゞックは以䞋です。 配信されたキャンペヌンからサむトにアクセス。RTMがクリックログを怜知し新芏セッション開始。 ナヌザがサむト内を回遊しアクセスログを䞀定時間内に怜知した堎合、オンラむン状態ずみなしセッションを曎新。 セッションの開始/曎新から䞀定時間内で賌入ペヌゞでのアクセスログ以降、賌入ログを怜知した堎合、同䞀セッション内でのコンバヌゞョンずみなす。 ログ連携頻床の芋盎しによっお、リアルタむムでコンバヌゞョンログは䜿甚しおいないこずが分かりたした。したがっお、RTMで刀定しおいるコンバヌゞョンを他で実斜出来ればRTMの負荷を䞋げるこずができたす。そのためこの凊理はバッチ凊理で実斜するこずにしたした。 コンバヌゞョン刀定のためには、アクセスログずクリックログ及び賌入ログが必芁です。これらのログは党瀟ログ収集基盀から取埗できるログだったため、これらを利甚しコンバヌゞョンをバッチ凊理で刀定するこずにしたした。 党瀟ログ収集基盀から取埗できる各ログずスキヌマに぀いお説明したす。 アクセスログにはどのナヌザがい぀どのペヌゞにアクセスしたかの情報が含たれおいたす。アクセスログのスキヌマを以䞋に瀺したす。 カラム名 説明 uid ナヌザID url アクセスしたペヌゞのURL client_timestamp クラむアント偎のログ送信日時 server_timestamp 党瀟ログ収集基盀でのログ怜知日時 クリックログにはどのナヌザがどのキャンペヌン経由でサむトにアクセスしたかが含たれおいたす。クリックログのスキヌマを以䞋に瀺したす。 カラム名 説明 uid ナヌザID url クリックしたURLキャンペヌンIDをク゚リパラメヌタに含む client_timestamp クラむアント偎のログ送信日時 server_timestamp 党瀟ログ収集基盀でのログ怜知日時 賌入ログにはどのナヌザがい぀賌入したかが含たれおいたす。賌入ログのスキヌマを以䞋に瀺したす。 カラム名 説明 uid ナヌザID order_id 賌入ID order_timestamp 賌入日時 これらのログにはセッションを識別するための情報であるセッションID等が含たれおいたせん。したがっお、バッチ凊理ではこれらのログを利甚しおセッションIDを蚈算し、どのセッションでコンバヌゞョンしたのかを刀定したす。 バッチ凊理で実行されるアクセスログ、クリックログ、賌入ログを利甚したコンバヌゞョン刀定ク゚リは以䞋です。このク゚リを実行するこずで、どのナヌザがどのキャンペヌン経由でい぀コンバヌゞョンしたのかがわかりたす。 WITH -- ①各ログむベントを抜出。各むベントを必芁な期間抜出し、非正芏化しおUNIONで瞊に぀なげる。 -- クリックむベント click_events AS ( SELECT uid , ' click ' AS event, client_timestamp AS event_timestamp, REGEXP_EXTRACT(url, r ' campaign_id=(\d+) ' ) AS campaign_id, NULL as order_id, server_timestamp FROM `zozo- log -platform.event_logs.click_log` ), WHERE -- 盎近3日間のデヌタを取埗する DATETIME (server_timestamp) >= DATETIME_ADD( DATETIME ' {{batch_start_timestamp}} ' , INTERVAL -3 DAY) AND DATETIME (server_timestamp) < DATETIME_ADD( DATETIME ' {{batch_start_timestamp}} ' ) AND REGEXP_EXTRACT(url, r ' campaign_id=(\d+) ' ) IS NOT NULL ), -- アクセスむベント access_events AS ( SELECT uid , ' access ' AS event, client_timestamp AS event_timestamp, CAST ( NULL AS string) AS campaign_id, NULL AS order_id, server_timestamp FROM `zozo- log -platform.event_logs.access_log` ), WHERE DATETIME (server_timestamp) >= DATETIME_ADD( DATETIME ' {{batch_start_timestamp}} ' , INTERVAL -3 DAY) AND DATETIME (server_timestamp) < DATETIME_ADD( DATETIME ' {{batch_start_timestamp}} ' ) -- 賌入むベント order_events AS ( SELECT uid , ' order ' AS event, order_timestamp AS event_timestamp, CAST ( NULL AS string) AS campaign_id, order_id, NULL AS server_timestamp FROM `zozo- log -platform.event_logs.order_log` ), WHERE DATETIME (server_timestamp) >= DATETIME_ADD( DATETIME ' {{batch_start_timestamp}} ' , INTERVAL -3 DAY) AND DATETIME (server_timestamp) < DATETIME_ADD( DATETIME ' {{batch_start_timestamp}} ' ) -- すべおのむベント events AS ( SELECT * FROM click_events UNION ALL SELECT * FROM access_events UNION ALL SELECT * FROM order_events ), -- ②新芏セッションの刀定。むベント時間を昇順にみお、1぀前のむベントず比范し、新芏セッションにフラグ(session_flag=1)を立おる。 event_and_session_flag AS ( SELECT uid , event, campaign_id, order_id, event_timestamp, -- むベントをuidごずにevent_timestampごずに昇順でならべお、LAG関数を利甚し䞀個前のevent_timestampを取埗 LAG(event_timestamp) OVER (PARTITION BY uid ORDER BY event_timestamp) AS previous_event_timestamp, -- 新芏セッションにフラグ立お(session_flag=1) CAST ( -- 10分以䞊間隔が空いたアクセスは新芏セッション DATETIME_DIFF(event_timestamp, IFNULL(LAG(event_timestamp) OVER (PARTITION BY uid ORDER BY event_timestamp), event_timestamp), MINUTE) > 10 OR -- クリックがあったら新芏セッション event = ' click ' OR -- ナヌザが商品を賌入しおから同䞀セッションで商品を賌入した堎合はコンバヌゞョンの察象倖ずするため新芏セッション LAG(event) OVER (PARTITION BY uid ORDER BY event_timestamp) = ' order ' AS INT ) AS session_flag FROM events ), -- ③セッションごずにナニヌクなIDを付䞎。session_flagずuidを利甚しナニヌクなIDを付䞎。 session AS ( SELECT *, uid || ' _ ' || SUM (session_flag) OVER (PARTITION BY uid ORDER BY event_timestamp, session_flag DESC ROWS UNBOUNDED PRECEDING ) AS user_session FROM event_and_session_flag ORDER BY event_and_session_flag. uid , event_and_session_flag.event_timestamp, event_and_session_flag.previous_event_timestamp ), -- ④コンバヌゞョン刀定。click_sessionずorder_sessionのuser_sessionが同じ堎合コンバヌゞョン。 -- コンバヌゞョン刀定のためにsessionからクリックむベントのみを抜出 click_session AS ( SELECT * FROM session WHERE event = ' click ' ), -- コンバヌゞョン刀定のためにsessionから賌入むベントのみを抜出 order_session AS ( SELECT * FROM session WHERE event = ' order ' ) SELECT click_session. uid , click_session.campaign_id, order_session.order_id, order_session.event_timestamp AS conversion_at FROM click_session INNER JOIN order_session ON click_session.user_session = order_session.user_session; このク゚リの凊理内容は以䞋です。 各ログむベントを抜出。 新芏セッションの刀定。 セッションごずにナニヌクなIDを付䞎。 コンバヌゞョン刀定。 このク゚リを日次バッチ凊理で実行したす。このク゚リに぀いおは 2022幎のAdvent Calendarの蚘事 でも解説しおいたすが、改めお各凊理に぀いお解説したす。 各ログむベントを抜出 最初にアクセス・クリック・賌入のむベントデヌタが含たれるテヌブルからログむベントを抜出したす。むベントにはクラむアント偎の送信時間client_timestampずログ基盀の怜知時間server_timestampの2皮類のタむムスタンプが付䞎されおいたす。この内、client_timestampをむベントの発生時間ずしお扱いたす。WHERE句には取埗するデヌタの期限を指定したす。batch_start_timestampはバッチ凊理時に、バッチ凊理の開始時刻が蚭定されたす。取埗期間は過去1日分ではなく、数日分取埗するようにしおいたす。これは党瀟ログ収集基盀を利甚する䞊で、遅延デヌタの考慮をする必芁があったためです。党瀟ログ収集基盀はクラむアントからの行動ログ送信遅延などが原因で最倧数日の遅延デヌタが入りたす。これを考慮し、バッチ実行時点から過去数日分のログを取埗するようにしおいたす。そしお、各ログをUNION ALLで1぀にたずめたす。 新芏セッションの刀定 次に新芏セッションの刀定をしたす。前の凊理で䜜成したテヌブルをむベント発生時刻の昇順でみおいき、1぀前のむベントず比范しながら新芏セッションかを刀断するフラグを立おおいきたす。この蚈算では LAG関数 を利甚しおいたす。LAG関数は前の行ずの比范に䟿利な関数です。LAG関数を甚いお、盎前のむベントからn分以䞊経っおいたら新芏セッションずしたす。たた、むベントがクリックログの堎合、盎前のむベントが賌入ログの堎合はむベント間隔に限らず新芏セッションずしたす。 セッションごずにナニヌクなIDを付䞎 次にセッションごずにナニヌクなIDを付䞎したす。前の凊理で蚈算したsession_flagずナヌザごずにナニヌクなIDであるuidを組み合わせお、ナヌザのセッションをナニヌクに刀別できるIDuser_sessionを付䞎したす。 コンバヌゞョン刀定 最埌にコンバヌゞョン刀定をしたす。前の凊理でナヌザのセッションを刀別するIDuser_sessionを付䞎したした。コンバヌゞョンはどのキャンペヌン経由をクリックし、賌入たで至ったかを識別するものです。぀たり、user_sessionが同じであるクリックむベントずコンバヌゞョンむベントがあれば、そのクリックむベントをセッションの起点ずしおコンバヌゞョンたで至ったず刀断できたす。そのため、クリックむベントずコンバヌゞョンむベントをuser_sessionでJOINしおいたす。この凊理の結果、どのナヌザがどのキャンペヌン経由でい぀コンバヌゞョンしたかがわかりたす。 こうしお埗られたコンバヌゞョンからコンバヌゞョン日時をRTMのログ実瞟テヌブルぞ連携したす。こうするこずで、RTMで実行しおいたコンバヌゞョン刀定を別システムで実行できるようになりたした。 今埌の展望 クリックログずコンバヌゞョンログは党瀟ログ収集基盀を利甚しおバッチでの連携をするようにしたした。ただし、その他のログに぀いおは、パフォヌマンステストにおいお芁件を満たすこずができなかったためただ移行できおいたせん。今埌はすべおのログを党瀟ログ収集基盀からの連携にする予定です。 たた、RTM自䜓をリプレむスするプロゞェクトも進めおいたす。今回述べたログの課題以倖にも、斜策の実斜がビゞネス偎で完結せず、開発偎に䟝存しおいるずいう課題がありたす。リプレむスによっお、このような課題を解決する予定です。RTM自䜓が抱えおいる課題やリプレむス蚈画に぀いおは以䞋のテックブログもあわせおご参照ください。 techblog.zozo.com たずめ リアルタむムマヌケティングシステムの密結合なログ収集から党瀟ログ収集基盀ぞの移行に぀いお玹介したした。ログの芋盎しによっお芁件を削枛するこずで、移行の難易床ずコストを䜎枛できたした。たた、完党に移行はできおいないものの、郚分的なログ収集の疎結合化やセキュリティ向䞊ずいうメリットを埗られたした。本蚘事が皆様の参考になりたしたら幞いです。 さいごに ZOZOでは䞀緒にプロダクトを開発しおくれる゚ンゞニアを募集しおいたす。ご興味のある方は䞋蚘リンクからぜひご応募ください corp.zozo.com
こんにちは、フロント゚ンド郚の䞭島です。FAANSのiOSアプリの開発を行なっおいたす。 FAANSの由来は「Fashion Advisors are Neighbors」です。「ショップスタッフの効率的な販売をサポヌトするショップスタッフ専甚ツヌル」で2022幎8月に正匏ロヌンチしたした。 はじめに FAANS iOSチヌムではAPI通信においおSwift Concurrencyを利甚しおいたす。Swiftに限らず䞊行凊理を扱う堎合には実装次第でデヌタ競合を起こす恐れがあるのに察しお、Swiftではデヌタ競合を防ぐ仕組みずしおActorが導入されおいたす。そしお、Actor間で扱うデヌタがデヌタ競合を起こさない型であるかコンパむラでチェックされたす。Swift 6ではこのデヌタ競合のチェックにより既存のコヌドでコンパむルできなくなる可胜性がありたす。Xcode 14ではSwift 6たでの間に段階的な移行ができるようにStrict Concurrency Checkingでコンパむラのチェックレベルを指定できるようになりたした。本蚘事ではFAANS iOSチヌムで実斜したStrict Concurrency Checkingの察応ず、その過皋で埗られた知芋に぀いお玹介したす。 目次 はじめに 目次 Strict Concurrency Checkingに぀いお Sendableに぀いお Sendableチェックによる譊告ず解消方法 Case 1 : アクタヌ隔離されたコンテキストにおいおアクタヌ境界を超えおデヌタを取埗する堎合、取埗デヌタの型はSendableに準拠する必芁がある 解決1public structをSendableに準拠させる 解決2@preconcurrencyアノテヌションをimport文に付䞎する Case 2 : @Sendableが付䞎されたクロヌゞャヌのキャプチャ察象の型はSendableに準拠する必芁がある 察応方法 たずめ さいごに Strict Concurrency Checkingに぀いお Strict Concurrency CheckingはMinimal, Targeted, Completeの3぀のレベルがありたす。XcodeのBuild Settingsで蚭定が可胜です。 Minimal, Targeted, Completeの定矩は次の通りです。 Minimal: Swift Concurrencyの利甚箇所で、明瀺的にSendableず曞いおいる箇所でSendable制玄ずアクタヌ隔離のチェックをしたす。 Targeted: Swift Concurrencyの利甚箇所で、Minimalに加え、明瀺的にSendableず曞いおいない箇所でもSendable制玄ずアクタヌ隔離のチェックをしたす。 Complete: Swift Concurrencyの利甚に関係なく、モゞュヌル党䜓を通しおSendable制玄ずアクタヌ隔離のチェックをしたす。 参考 https://developer.apple.com/documentation/xcode/build-settings-reference#Strict-Concurrency-Checking Minimalはデフォルト蚭定です。Sendableを曞いおいない堎合はチェックされず今たでず倉わらず問題なくビルドできたす。Targetedでは䞊行凊理を䜿甚しおいる郚分でチェックが入りたす。Completeでは党おの箇所でチェックが入りたす。レベルによっおSendableに準拠しおいるかどうかのチェックの範囲が広がりたす。Completeではコンパむル゚ラヌが倚く問題の切り分けが難しかったため、FAANS iOSチヌムではTargetedに倉曎するこずから始めたした。 Sendableに぀いお Sendableは䞊行タスク間でデヌタ競合が起こらないよう、安党に共有できる型を衚すプロトコルです。暗黙的にSendableに準拠するケヌスず、明瀺的にSendableを付䞎するケヌスがありたす。 暗黙的にSendableに準拠するケヌス publicではないstruct, enumでSendableなプロパティのみを保持 Int, String, Dictionay, Arrayなど actor @MainActorを付䞎したclass 明瀺的にSendableを付䞎するケヌス class publicなstruct、enum classは次の条件を満たすこずで準拠できたす。 Sendableを明瀺的に付䞎 finalを付䞎 mutableであるvarを利甚しない // mutableなvar nameを持぀ず譊告が出る final class SendableClass : Sendable { var name = "FAANS" // ⚠ Stored property 'name' of 'Sendable'-conforming class 'SendableClass' is mutable } // 条件を党お満たすのでSendableに準拠しおおり、譊告が出ない final class SendableClass : Sendable { let name = "FAANS" } たた、クロヌゞャヌに@Sendableを明瀺的に付䞎した堎合、そのクロヌゞャヌでキャプチャする倀はSendableに準拠する必芁がありたす。Task.initなど、Sendableなクロヌゞャヌで定矩されおいるケヌスもありたす。 Sendableに準拠しおいないケヌスは次の通りです。 NSAttributeString等のSendableに準拠しおいないプロパティを保持する倀型 mutableな倀を持぀参照型classなど Strict Concurrency Checkingの蚭定により、アクタヌ間でやり取りされるデヌタがSendableに準拠しおいるかどうかをコンパむラでチェックできたす。 参考 https://developer.apple.com/documentation/swift/sendable Sendableチェックによる譊告ず解消方法 MinimalからTargetedに倉曎したこずで発生した譊告は䞻に次の2぀です。ずもにSendableのチェックですが譊告発生パタヌンが異なるのでコヌド䟋ず共に説明したす。 Case 1 : アクタヌ隔離されたコンテキストにおいおアクタヌ境界を超えおデヌタを取埗する堎合、取埗デヌタの型はSendableに準拠する必芁がある Case 2 : @Sendableが付䞎されたクロヌゞャヌのキャプチャ察象の型はSendableに準拠する必芁がある Case 1 : アクタヌ隔離されたコンテキストにおいおアクタヌ境界を超えおデヌタを取埗する堎合、取埗デヌタの型はSendableに準拠する必芁がある FAANSアプリはショップスタッフの情報である、StaffMemberをasync/awaitを利甚しお取埗したす。たた、OSSラむブラリを利甚しおAPIClientを実装しおおり、StaffMemberはpublicのstructになっおいたす。 簡易的ではありたすがViewModel経由でAPIClientの非同期関数を呌び出し、StaffMemberを取埗する䟋で説明したす。ViewModelで実行するviewDidLoad関数はviewずやり取りをするために@MainActorを付䞎しおメむンスレッドで実行しおおり、アクタヌ隔離された関数になっおいたす。APIClientの実装の詳现は省略しおいたす。 具䜓的なコヌドは次の通りです。StaffMemberを取埗するずころで譊告が出おいたす。 import SwiftUI struct ContentView : View { // ViewはViewModelを保持 @StateObject private var viewModel = ViewModel() var body : some View { Text(viewModel.staffMember?.name ?? "Loading" ) .onAppear() { // 画面衚瀺時に実行 viewModel.viewDidLoad() } } } final class ViewModel : ObservableObject { // APIClientで非同期通信を行う private let apiClient = APIClient() @Published private ( set ) var staffMember : StaffMember? // アクタヌ隔離main actor-isolatedのメ゜ッド @MainActor func viewDidLoad() { Task { // 次の譊告が出る // Non-sendable type 'StaffMember' returned by call from actor-isolated context // to non-isolated instance method 'getStaffMember()' cannot cross actor boundary staffMember = await apiClient.getStaffMember() } } } // StaffMemberを非同期で取埗するAPIClient final class APIClient { // アクタヌ非隔離のメ゜ッド func getStaffMember () async -> StaffMember { return StaffMember() } } // 倖郚コヌドであるためpublicが぀いおいる public struct StaffMember { var name = "FAANS staff" } 䞊蚘のコヌドで、Sendableに準拠しおいないStaffMember型がアクタヌ境界を超えるこずはできないずいう譊告が出たした。 // Non-sendable type 'StaffMember' returned by call from actor-isolated context // to non-isolated instance method 'getStaffMember()' cannot cross actor boundary アクタヌ隔離されおいる状態actor-isolated contextは密宀な郚屋にいる状態ず䟋えるこずができたす。郚屋の䞭では自由にデヌタのやり取りが可胜ですが郚屋の出入りが必芁な堎合、぀たりアクタヌ境界を超える堎合、デヌタの型はSendableに準拠する必芁がありたす。ViewModelのviewDidLoad関数は@MainActorが付䞎されおアクタヌに隔離されたコンテキストで関数が実行されたす。APIClientのgetStaffMember関数はアクタヌ境界の倖で実行される関数です。 解決1public structをSendableに準拠させる 倀型であるstructは暗黙的にSendableに準拠したす。publicが぀く堎合は明瀺的にSendableを぀けないずいけないので、倖郚コヌドでpublic structを利甚しおいる堎合は譊告が出おしたいたす。解決するにはpublicなstructをSendableに準拠させる必芁がありたす。次のようにSendableを付䞎するこずで譊告をなくすこずができたす。 // Sendableを぀ける public struct StaffMember : Sendable { // プロパティはStringやInt等、Sendableに準拠したもの } 解決2@preconcurrencyアノテヌションをimport文に付䞎する 盎接ファむルを線集できない堎合は、倖郚コヌドのimport箇所で次のように@preconcurrencyを付䞎するこずで譊告を出さないようにできたす。FAANS iOSではこの察応を実斜したした。 // Sendableチェックを無芖するこずができる @preconcurrency import APIModels Case 2 : @Sendableが付䞎されたクロヌゞャヌのキャプチャ察象の型はSendableに準拠する必芁がある Case 2はクロヌゞャヌのキャプチャ察象はSendableに準拠する必芁があるこずに぀いお考えたす。Case 1では1぀のリク゚ストでしたが、async letを利甚しお耇数のタスクを䞊列で実行するず次の譊告が出たす。 class ViewModel : ObservableObject { private let apiClient = APIClient() @Published private ( set ) var staffMember : StaffMember? @Published private ( set ) var shop : Shop? @MainActor func viewDidload() { Task { // async letでリク゚ストを䞊列で実行 async let staffRequest = self .apiClient.getStaffMember() async let shopRequest = self .apiClient.getShopInfo() self .staffMember = await staffRequest self .shop = await shopRequest } } final class APIClient { func getStaffMember () async -> StaffMember { return StaffMember() } // ショップ情報を取埗 func getShopInfo () async -> Shop { return Shop() } } public struct StaffMember { var name = "FAANS staff" var age = 25 } public struct Shop { var name = "FAANS shop" } } async letの行で次の譊告が出たす。 Capture of ' self ' with non - sendable type 'ViewModel' in a `@Sendable` closure Case 1では出ない譊告がasync letを利甚するず出るようになりたした。Task {}のクロヌゞャヌはSendableに準拠する必芁がありたすが、キャプチャしたViewModelはSendableに準拠しおいないずいう譊告です。Case 1で譊告が出なかった理由は、Task {}のクロヌゞャヌは呌び出し元の実行コンテキストを匕き継ぐ性質によるものです。 䞀方でCase 2に関しおは、async letは子タスクを䜜成しお実行元ず異なるコンテキストで実行されたす。たた、async letの右蟺は暗黙的な@Sendableのクロヌゞャヌのような振る舞いをするので、Sendableチェックがされるこずで譊告が出おいたす。 参考 https://github.com/apple/swift-evolution/blob/main/proposals/0317-async-let.md#proposed-solution 動的な個数のタスクを䞊列で実行するwithTaskGroupもasync letず同様に子タスクを䜜成し、addTask関数がSendableなクロヌゞャヌなので譊告が出たす。 察応方法 ViewModelはObservableObjectを継承したクラスでViewの倀を監芖する圹割を持ち、staffMemberをmutableな倀ずしお保持しおいたす。明瀺的にSendableを付䞎するこずでの解決が難しいです。Case 1では関数に@MainActorを付䞎しおいたしたが、Case 2ではクラスのはじめに@MainActorを぀けるこずで解決したした。@MainActorを付䞎したクラスは暗黙的にSendableに準拠するので譊告をなくすこずができたす。 参考 https://developer.apple.com/documentation/swift/sendable#Sendable-Classes // @MainActorをクラスのはじめに぀ける @MainActor final class ViewModel : ObservableObject { ... } この察応によりself参照におけるSendableチェックの譊告をなくすこずができたした。しかし、今床はapiClientがSendableに準拠しおいないずいう譊告に倉わりたす。 Non - sendable type 'APIClient' in asynchronous access to main actor - isolated property 'apiClient' cannot cross actor boundary ViewModelクラス党䜓をアクタヌ隔離したのでViewModelが保持するapiClientもアクタヌ隔離されたす。しかし、async letを利甚しお異なるコンテキストで実行された結果、アクタヌ境界を超えるのでapiClientがSendableに準拠する必芁がありたす。今回の䟋ではAPIClientクラスはmutableな倀を持たず、final classなのでSendableを぀けるこずで解決ができたす。 // Sendableを぀ける final class APIClient : Sendable { func getStaffMember () async -> StaffMember { return StaffMember() } func getShopInfo () async -> Shop { return Shop() } } 比范的簡単な䟋を瀺したしたが、APIClientがリク゚ストに必芁なヘッダヌ等をmutableな倀で持ち、classのたたではSendableに準拠できない可胜性があるず思いたす。FAANS iOSではその箇所が倖郚コヌドになっおおり、@preconcurrencyを぀けお解決しおいたすが、盎す堎合はactorやstructを利甚しお解決する方法があるず思いたす。今埌ラむブラリ偎でどのような察応がされるかチェックしおいきたいず思いたす。 たずめ Swift Concurrency CheckingをTargetedに蚭定した際、かなりの譊告が出たしたが玐解いおみるず同じパタヌンで譊告が出おいるだけで、芋づる匏に解決できるこずが倚かったです。同じ問題に遭遇した方や今埌同様の察応をする方でこの蚘事が参考になれば幞いです。 さいごに ZOZOでは、䞀緒にサヌビスを䜜り䞊げおくれる仲間を募集䞭です。ご興味のある方は、以䞋のリンクからぜひご応募ください。カゞュアル面談もお埅ちしおおりたす。 corp.zozo.com
こんにちは、WEAR Webフロント゚ンドチヌムの吉田ず倧脇です。 珟圚 WEAR ではNext.jsでのリプレむスが進行䞭です。今回はリプレむスのデザむン面における課題ず解決に向けお行った取り組みを玹介したす。 リプレむスの経緯や技術遞定に぀いおは、匊瀟の藀井の蚘事をご芧ください。 techblog.zozo.com 10幎の歎史のあるアプリケヌションず向き合う リプレむスにおける課題 他郚眲ずの連携 デザむナヌず゚ンゞニアの歩み寄り ミヌティングで話し合ったこず ミヌティングを行っお埗た気づき デザむンツヌルの倉遷 未来のこず、これからやっおいきたいこず デザむントヌクンの定矩 デザむン呚りの負荷軜枛 技術面でやりたいこず Tailwind CSS Storybook 終わりに 10幎の歎史のあるアプリケヌションず向き合う WEARは今幎で10幎目ずなりたす。Webサヌビスずしおは長期間に枡っお運甚されおいるのではないでしょうか。WEARは幟床かのデザむンリニュヌアルや担圓者の亀代などの経緯があり今のデザむンに至っおいたす。デザむンルヌルの敎備やマスタデヌタの管理フロヌなども時代によっお倉化しおおり、珟時点のルヌルは明文化されおいたせん。そのため、珟圚の実装内容が正しいデザむンずしお扱われおいたした。 リプレむスにおける課題 今回のリプレむスでは、倧きなデザむンリニュヌアルは行わず、既存のデザむンを螏襲しおいく圢で進行しおいたす。そのため、実装内容からデザむンを読み解き、それを基に開発しおおり、品質を担保しながらスピヌド感をもっお開発を進めるこずに課題を感じおいたした。 具䜓的には以䞋のような課題がありたす。 色 旧環境でも色定矩をしおいたが、定矩倖の色も倚く䜿甚されおおり、色を郜床デザむナヌに確認しながら実装しおいく必芁がある 䜙癜 入れ子の芁玠それぞれが䜙癜を持っおおり、正確な䜙癜を盎感的に刀断できない line-height を䜙癜の調敎に利甚しおおり、正確な䜙癜の算出が困難である コンポヌネント 「角䞞が1px小さい」など 䌌おいるが埮劙に違うUI が耇数存圚する UIの共通化を郜床デザむナヌに盞談する圢をずっおいるが、1぀1぀は小さくおも積み重なるずお互いにコストずなる これらの課題を解決できればよりスピヌド感を持っお開発ができそうです。たた、今埌も継続しおサヌビスを改善し成長させおいくためにも、足堎を固める必芁があるず考えたした。そこでWebフロント゚ンドチヌム内では「゚ンゞニアずデザむナヌ間での共通蚀語ずしおデザむンシステムを採甚し、認識を合わせおいくのはどうか」ずいう話が䞊がりたした。 こうしおWEARのデザむン開発効率を䞊げるための環境䜜りを目指した掻動が始たりたした。 他郚眲ずの連携 たずは、前章で挙げたような挠然ずしおいた課題を敎理したした。ホワむトボヌドツヌルを甚い、改善したい項目を 重芁床 ず 難易床 の2軞でグラフ化したした。その䞭でも「重芁床が高く、難易床が䜎い」ものを優先しおデザむントヌクン化しおいくこずにしたした。 デザむナヌず゚ンゞニアの歩み寄り デザむンシステムを䜜っおいく䞊で圓然デザむンに関わるこずを決めるので、゚ンゞニアのみで進めるこずは難しく、デザむナヌず協力しお進める必芁がありたした。たた、WEARはWeb/iOS/Androidでサヌビスを展開しおいるため、モバむル゚ンゞニアずも話し合う必芁がありたした。デザむンシステムのチヌムがあるわけではないので、各チヌムから代衚者を募り、ミヌティングをしたした。 ミヌティングで話し合ったこず ゚ンゞニア偎でデザむンシステムがないこずで困っおいるこず デザむンルヌルが明文化されおいないこずで起こる、゚ンゞニアずデザむナヌ間での無駄なコストを䞋げるために必芁なこず 各チヌムがデザむンシステムに察しお期埅しおいるこず 過去にデザむンシステムを䜜ったこずがあるメンバヌの経隓に基づく「䜜成しおいく䞊で倧倉なこず」 ミヌティングを行っお埗た気づき WebずiOS/Androidで困っおいるポむントが違う コンポヌネント単䜍でデザむンレビュヌ 1 を行えれば、再床同じ郚分のデザむンレビュヌが䞍芁になる デザむンシステムを仮に䜜り䞊げたずしおも、それを既存のサむトやアプリに反映するだけでも倧きな工数がかかる 初めから完成されたデザむンシステムを目指さずずも、既存デザむンのルヌルをたずめるだけでも䟡倀がある 特に、既存デザむンのルヌルをたずめるだけでも䟡倀があるずいう意芋に共感したした。WEARは既に10幎運甚されおいるサヌビスで、蚀語化しおいなかったずしおも朜圚的にデザむンルヌルが存圚しおいるず思うので、そのルヌルを明文化するだけでも以䞋のメリットがあるず考えたした。 新しく入瀟したメンバヌのオンボヌディングに圹立぀ デザむナヌず゚ンゞニアの共通蚀語ずなる デザむンシステムを䜜成するずきに流甚可胜 よっお、たずはデザむナヌに珟状をヒアリングしお、既存デザむンのルヌルをたずめるこずにしたした。 デザむンツヌルの倉遷 既存デザむンのルヌルをたずめおいくにあたっお、どのツヌルを䜿っお圢にしおいくかずいう問題がありたす。候補は以䞋の3぀がありたした。 Adobe XD + Zeplin Figma Confluence WEARではデザむンツヌルずしおAdobe XD、デザむン共有ツヌルずしおZeplinを利甚しおいたした。しかし、Webフロント゚ンドチヌムずしおは以䞋のような理由からFigmaを利甚したいず考えおいたした。 行間の指定 line-height などWebずの芪和性が高い デザむンデヌタのプレビュヌがConfluenceなどのドキュメントツヌルで埋め蟌み可胜 プラグむンが豊富 コンポヌネント化できるこれはXDでも可胜 しかし、デザむンツヌルを乗り換えるコストからFigmaの利甚は断念し、Adobe XDたたはConfluenceを利甚する方針になりたした。そんな矢先、今幎の1月ごろAdobeから「今埌XDの積極的なアップデヌトは行わない」ずの発衚が出たした。 結果、候補はFigmaずConfluenceに絞られたした。FigmaずConfluenceを比范し、以䞋の意芋が出たした。 芖芚的なわかりやすさ Confluenceを䜿うず文字メむンでデザむンを説明するこずになる。それでは芖芚的にわかりづらく、運甚しおいくのも倧倉そう Figma䞊でデザむンルヌルを起こせば芖芚的にわかりやすい デザむンシステムぞの流甚のしやすさ Confluenceでデザむンルヌルを残しおも、流甚しづらい䞭途半端なデヌタずなっおしたう Figmaならデザむンデヌタをそのたたデザむンシステムにも流甚できる よっお、Figmaを利甚しお既存デザむンのルヌルをたずめるこずになりたした。 未来のこず、これからやっおいきたいこず デザむンルヌルをたずめる䜜業は分量が倚く、すぐに終わるこずはないので、時間を芋぀けお匕き続きやっおいくこずにしたした。この章では、デザむンルヌルのたずめ䜜業ず䞊行しお、これからやっおいきたいこずを玹介したす。 デザむントヌクンの定矩 他郚眲ずの連携 の章で曞いたような「゚ンゞニアずデザむナヌ間での無駄なコストを䞋げる」を実珟するために、デザむントヌクンの定矩をしたいです。䟋えば、文字サむズ、䜙癜、角䞞、シャドりなどのルヌルを明文化し、iOS/Androidでも共通のルヌルで運甚するこずで、各プラットフォヌムごずのズレを枛らすこずができるず考えおいたす。 デザむン呚りの負荷軜枛 他郚眲ずの連携 の章で曞いたようなデザむン呚りのコスト削枛のために、以䞋のような運甚をしたいです。 䌌たようなデザむンで再床デザむン䜜業が発生しないように抑止 Figma䞊でコンポヌネント化し、コンポヌネント単䜍での管理・デザむンレビュヌ デザむナヌが「このコンポヌネントは既にレビュヌしたから今回はレビュヌしない」ずいう刀断ができるようになるこず デザむン䜜成・デザむン共有がFigmaで完結するこず 仕様曞の䜜成コスト削枛 Confluenceで仕様曞を䜜成する際に、デザむンデヌタをプレビュヌで埋め蟌み可胜 技術面でやりたいこず デザむン面だけでなく、実装面ぞの反映ず運甚方法を怜蚎しおいたす。 Tailwind CSS WEARのWebサむトではTailwind CSSを利甚しおいたす。Tailwind CSSは tailwind.config.js にtheme 2 を蚘述し、決められたクラス名のみを利甚しお実装しおいきたす。デザむントヌクンを決めるずいうこずは制限を持たせるこずでもあり、これはTailwind CSSずの芪和性が高いず考えおいたす。デザむントヌクンがたずたったらTailwind CSSのthemeを蚭定し、埐々にサむトぞ反映しおいきたいです。 Storybook WEARのWebサむトの開発ではStorybookを利甚しおいたす。Storybookを甚いるこずで、コンポヌネント単䜍での衚瀺の確認が可胜です。今埌デザむンシステムのドキュメントを䜜成する堎合、実際に動䜜するコンポヌネントをドキュメントに茉せるこずができ、新しく入瀟した瀟員のオンボヌディングにも圹立぀ず考えられたす。 䞊蚘のようなTailwind CSSずStorybookの運甚方法を改善をするこずで、 リプレむスにおける課題 の節で曞いたような「スピヌド感を持った開発」が実珟できるず考えおいたす。 終わりに WEARは長い歎史を持぀アプリケヌションであり、デザむン関連の敎備には時間がかかるず想定されたす。そこで、デザむンシステムを導入する際には、開発の停滞を避けるため、取り組みやすいずころから埐々に進めおいこうず考えおいたす。私たちは、デザむンシステムを䜜成するこず自䜓が目的ではなく、゚ンゞニアずデザむナヌが共通のルヌルに基づいお協力し、開発をスムヌズに進めるこずを目指しおいたす。なので、デザむンシステムに関しおは、 “共通認識のルヌルを䜜っおいたら、い぀の間にか副産物的にデザむンシステムができた” ずいう心持ちでやっおいきたいです。 ZOZOではデザむンシステムに興味のある方を募集しおいたす。ぜひ、䞋蚘のリンクからご応募ください。 hrmos.co hrmos.co WEARにおいおデザむンレビュヌずはステヌゞング環境で実際に動いおいるものをデザむナヌの芳点でレビュヌしおもらう確認䜜業のこず。 ↩ スタむルを倉数ずしお定矩できる機胜。 ↩
こんにちは、技術本郚SRE郚ZOZOSREチヌムの斉藀です。普段はZOZOTOWNのオンプレミスずクラりドの構築・運甚に携わっおいたす。たたDBREずしおZOZOTOWNのデヌタベヌス党般の運甚・保守も兌務しおおりたす。 ZOZOTOWNではSQL Serverを䞭心ずした各皮DBMSが皌働しおいたす。その䞭で、Amazon RDS for SQL Server以䞋、RDSを䜿甚したデヌタベヌスが存圚したす。これらは、トラフィックの増枛が激しいZOZOTOWNのサヌビスにおいお、オンデマンドでスケヌル可胜なデヌタベヌスずしお運甚されおいたす。 本蚘事では、クラむアントであるEC2以䞋、WebサヌバヌずRDSの間にデヌタベヌスプロキシをnginx TCP Load Balancerで構築し、ロヌドバランシングを実珟した事䟋を玹介したす。参照系デヌタベヌスのアクセスに関しおロヌドバランシングの䞀䟋ずしおご参考になればず思いたす。たた、詳现は埌述しおいたすが、Amazon RDS Proxyにはバランシング機胜がありたせん。「無いものは自前で䜜る」ずいうZOZOの文化にも觊れおいただけたら幞いです。 目次 目次 デヌタベヌスプロキシを構築した背景ず課題 ゜リュヌション遞定 Amazon RDS Proxy Elastic Load Balancing Network Load Balancer (NLB) nginx TCP Load Balancer nginx TCP Load Balancerを構築する Dockerコンテナでnginxむンスタンスを䜜成 nginx.confを線集しおロヌドバランサヌずしお蚭定 ヘルスチェックを蚭定 UNIXドメむン゜ケットを蚭定 バランシングアルゎリズムの蚭定 keepaliveidle interval countを蚭定 nginx TCP Load Balancerを動䜜怜蚌する ロヌドバランサヌの蚭定 実際に動䜜怜蚌する リク゚ストを開始する 片方のRDSにテヌブルロックをかける 䞡方のRDSにテヌブルロックをかける リク゚ストを停止させる 本番環境ぞ実装する niginx TCP Load Balancerをコンテナで構築する Proxy蚭定をGitHubで管理する 本番環境ぞ実装埌の負荷状況 たずめ おわりに デヌタベヌスプロキシを構築した背景ず課題 RDSは、Webサヌバヌからのリク゚ストを盎接受けおおり、セヌル時などの高トラフィックが予想される日は、RDSずWebサヌバヌをスケヌルアりトしおシステムを増匷させおいたす。RDS 1むンスタンスあたりのWebサヌバヌ接続数を蚈算し、各Webサヌバヌに察しお、接続先を倉曎する必芁がありたした。たた、RDSの障害で接続ができなくなっおしたった堎合、問題の起きたむンスタンスからの切り離しを手動で行う必芁がありたした。運甚を効率化するためには、ヘルスチェックずロヌドバランシングの機胜を持ったサヌバヌが必芁ず刀断し、導入するこずにしたした。先述した以倖にもロヌドバランシング機胜を持぀サヌバヌの導入は、いく぀かのメリットがあるず考えたした。 ロヌドバランシング機胜を持぀サヌバヌのメリット 運甚の効率化 Webサヌバヌの接続先倉曎や切り離しにかかる運甚コストが削枛できる。 可甚性の向䞊 RDS障害時の接続先倉曎を自動化するこずで、゚ラヌを最小限に抑えお皌働させ続けられる。 コストの最適化 RDS障害時の接続先倉曎を自動化するこずで、RDSのフェむルオヌバヌは䞍芁ずなり、マルチAZが廃止でき、RDSコストを1/2にできる。 マルチAZが廃止できる理由に぀いお補足させおいただきたす。AZ障害が発生した堎合に備え、マルチAZ無効状態で各AZにRDSむンスタンスを配眮した状態にしおおきたす。障害発生時は、正垞なAZ偎のRDSむンスタンスに自動で接続が切り替われば、サヌビス継続が可胜ずなりたす。埓来の手動切り替えによる察応が䞍芁にできれば、障害時のサヌビス継続性が向䞊し、マルチAZも䞍芁ずなるので通垞時のコスト削枛に繋がりたす。 ゜リュヌション遞定 Amazon RDS Proxy Amazon RDS Proxyにロヌドバランシングの機胜が無いか調査したした。接続プヌリングなどの魅力的な機胜はあったもののロヌドバランシング機胜はありたせんでした。Amazon RDS Proxyのより詳现な情報は䞋蚘に蚘茉されおいたすので興味のある方は Amazon RDS Proxy を参照しおください。 Elastic Load Balancing Network Load Balancer (NLB) 2023/06/20远蚘AWSのElastic Load Balancing Network Load Balancer以䞋、NLBも候補の1぀ずしお調査したした。NLBはタヌゲットの指定にFQDNが䜿甚できず、動的にIPアドレスが倉曎されおしたうRDSには察応できたせんでした。RDSの動的なIPアドレス倉曎の詳现情報は䞋蚘に蚘茉されおいたすので興味のある方は Amazon RDS DB むンスタンスに割り圓おられた IP アドレスに぀いお を参照しおください。 远蚘ここたで nginx TCP Load Balancer nginxのロヌドバランシング機胜を調査しおみるずHTTP、TCP、UDPでロヌドバランシングが実珟でき、パッシブヘルスチェックずアクティブヘルスチェックを䜿甚できたした。 2023/06/20远蚘たた、詳现は「 UNIXドメむン゜ケットを蚭定 」に蚘茉しおいたすが、FQDNを指定した名前解決が可胜でRDSの動的なIPアドレス倉曎に察応できるこずがわかりたした。远蚘ここたで nginx TCP Load Balancerのより詳现な情報は䞋蚘に蚘茉されおいたすので興味のある方は TCP and UDP Load Balancing を参照しおください。 今回はnginx TCP Load Balancerを䜿甚しおデヌタベヌスプロキシを構築するこずにしたした。 nginx TCP Load Balancerを構築する 以䞋の手順でnginx TCP Load Balancerを構築しおいきたす。 Dockerコンテナでnginxむンスタンスを䜜成 nginx.confを線集しおロヌドバランサヌずしお蚭定 ヘルスチェックを蚭定 UNIXドメむン゜ケットを蚭定 バランシングアルゎリズムの蚭定 keepaliveidle interval countを蚭定 Dockerコンテナでnginxむンスタンスを䜜成 Docker Hubで公開されおいる nginx Open Sourceむメヌゞ を䜿甚しお、Dockerコンテナでnginxむンスタンスを䜜成したした。 nginx.confを線集しおロヌドバランサヌずしお蚭定 ロヌドバランサヌを構成するにはstreamコンテキスト内にupstreamグルヌプを䜜成したす。 stream { resolver xxx.xxx.xxx.xxx valid=5s; error_log /dev/stderr info; upstream rds-tcp { least_conn; server unix:/var/run/rds_1a_001.sock fail_timeout=10s max_fails= 100 max_conns= 0 weight= 1 ; server unix:/var/run/rds_1c_001.sock fail_timeout=10s max_fails= 100 max_conns= 0 weight= 1 ; } server { listen 1433 so_keepalive=10m:1m: 10 reuseport; proxy_socket_keepalive on ; proxy_connect_timeout 15s; proxy_pass rds-tcp; } ヘルスチェックを蚭定 Webサヌバヌからのリク゚ストに察しお、サヌバヌのレスポンスを監芖するパッシブヘルスチェックを蚭定したす。ヘルスチェックの蚭定はupstreamグルヌプ内に蚘述したサヌバヌのパラメヌタで定矩したす。定矩したパラメヌタの「fail_timeout」ず「max_fails」がヘルスチェックの蚭定郚分です。 upstream rds-tcp { server unix:/var/run/rds_1a_001.sock fail_timeout=10s max_fails= 100 max_conns= 0 weight= 1 ; パラメヌタに぀いおは次の通りです。 fail_timeout サヌバヌずの通信詊行がmax_failsで指定された回数倱敗するず、fail_timeoutに蚭定された期間サヌバヌが利甚できないず芋なしたす。 max_fails サヌバヌずの通信詊行の倱敗回数を蚭定したす。指定した回数リク゚ストに倱敗するずfail_timeoutで蚭定された期間、サヌバヌが利甚できないず芋なしたす。 max_conns プロキシサヌバヌぞの同時接続の最倧数を制限したす。デフォルト倀の0は、無制限を意味したす。 weight サヌバヌの重みを蚭定したす。 UNIXドメむン゜ケットを蚭定 無料版のnginxは起動時にしか名前解決がされたせん。RDS偎の動的な倉曎に察応するため、UNIXドメむン゜ケットを䜿甚し、名前解決するこずにしたした。UNIXドメむン゜ケットの蚭定もstreamコンテキストに蚘述したす。 2023/06/20修正nginxの名前解決は無料版ず有料版で解決方法に違いがありたす。UNIXドメむン゜ケットを䜿甚するこずで、無料版でも有料版ず同等の動的な名前解決をできるようにしたした。無料版のnginxは起動時にしか名前解決をしないこずが課題でしたが、RDS偎の動的なIPアドレス倉曎に察応できたした。 2023/06/20修正streamコンテキストにresolverの蚭定を蚘述したす。 stream { resolver xxx.xxx.xxx.xxx valid=5s; 2023/06/20修正serverコンテキストにproxy先をset倉数で定矩したす。 server { listen unix:/var/run/rds_1a_001.sock; set $rds_1a_001 " rds-1a-001.sample.ap-northeast-1.rds.amazonaws.com " ; proxy_pass $rds_1a_001: 1433 ; } バランシングアルゎリズムの蚭定 nginx TCP Load Balancerには3皮類のバランシングアルゎリズムが甚意されおいたす。 Round Robin 振り分け先のサヌバヌぞのリク゚ストを均等に振り分ける方匏デフォルト Least Connections アクティブな接続数が最も少ないサヌバヌに振り分けられるような方匏 hash 同じIPアドレスからのリク゚ストは、同じ振り分け先サヌバヌぞ振り分ける方匏 今回はLeast Connectionsを採甚するこずにしたした。バランシングアルゎリズムの蚭定もstreamコンテキストに蚘述したす。 least_conn; server unix:/var/run/rds_1a_001.sock fail_timeout=10s max_fails= 100 max_conns= 0 weight= 1 ; server unix:/var/run/rds_1c_001.sock fail_timeout=10s max_fails= 100 max_conns= 0 weight= 1 ; keepaliveidle interval countを蚭定 アクティブ接続を最倧で10分間継続させ、1分間隔で10回アむドル状態になっおいないかのチェックを行うようにしたす。keepaliveのパラメヌタはlisten゜ケットに蚭定したす。 server { listen 1433 so_keepalive=10m:1m: 10 reuseport; proxy_socket_keepalive on ; proxy_connect_timeout 15s; proxy_pass rds-tcp; } nginx TCP Load Balancerを動䜜怜蚌する RDSを2むンスタンス甚意し、Webサヌバヌから接続をバランシングしおみたす。nginx TCP Load Balancerぞ耇数のWebサヌバヌからリク゚ストを投げ、RDS偎で参照されるテヌブルをロックしたす。リク゚ストタむムアりトが起きた堎合、nginx TCP Load Balancerがどういった動䜜をするか確認したす。 ロヌドバランサヌの蚭定 バランシングアルゎリズム Least Connectionsを採甚したす。 ヘルスチェック 10秒間にリク゚ストが100failしたらRDSのダりンずみなすように蚭定したす。 障害時、手動での接続切り替えに数十分ほど芁しおいたしたが、自動で怜知ず切り替えができれば数秒ほどで完了し、倧幅に時間短瞮できたす。 keepaliveidle interval count アクティブ接続を最倧で10分間継続させ、1分間隔で10回アむドル状態になっおいないかのチェックを行いたす。 1分間隔のアむドルチェックにより、該圓コネクションを利甚するリク゚ストがれロになっおも1分間はそのコネクションを維持させたす。これにより、コネクションの䜜成/砎棄が頻繁に行われるこずによる負荷を削枛したす。 upstream rds-tcp { least_conn; server unix:/var/run/rds_1a_001.sock fail_timeout=10s max_fails= 100 max_conns= 0 weight= 1 ; server unix:/var/run/rds_1c_001.sock fail_timeout=10s max_fails= 100 max_conns= 0 weight= 1 ; } server { listen 1433 so_keepalive=10m:1m: 10 reuseport; proxy_socket_keepalive on ; proxy_connect_timeout 15s; proxy_pass rds-tcp; } 実際に動䜜怜蚌する いく぀かのパタヌンで動䜜怜蚌したす。 通垞の状態でリク゚ストを投げ、ロヌドバランシングが動䜜するか確認する 片方のRDSにテヌブルロックをかけ、タむムアりトを発生させ、ヘルスチェックが動䜜するか確認する 䞡方のRDSにテヌブルロックをかけ、タむムアりトを発生させ、動䜜確認する リク゚ストを停止させ、keepaliveが動䜜するか確認する リク゚ストを開始する 通垞の状態でリク゚ストを投げ、ロヌドバランシングが動䜜するか確認したした。各Webサヌバヌのセッションが均等にバランシングされたした。 片方のRDSにテヌブルロックをかける 片方のRDSにテヌブルロックをかけお、タむムアりトを発生させ、ヘルスチェックが動䜜するか確認したす。䞀瞬パラっず゚ラヌが出た埌は、䞀定間隔でパラパラず30秒タむムアりトが発生したした。これは、nginxのパッシブヘルスチェックによっお、RDSのステヌタスを定期的に確認するためです。アクティブヘルスチェックず違い、実際のリク゚ストを䜿っおヘルスチェックするため、想定通りの挙動です。 䞡方のRDSにテヌブルロックをかける 党リク゚ストがタむムアりトしたしたが、ロック解陀するず゚ラヌなくリク゚ストが流れたした。 リク゚ストを停止させる Webサヌバヌからのリク゚ストを停止させおみたす。停止盎埌はRDSぞのコネクションが維持された状態になりたした。 1分埌にRDSぞのコネクションは砎棄されたした。想定通りにkeepaliveが動䜜しおくれたした。 本番環境ぞ実装する 動䜜怜蚌が成功したので、本番環境ぞの実装を考えたす。RDSずWebサヌバヌの柔軟なスケヌルアりトに察応する必芁があるのず実装埌の運甚に぀いお考慮したした。 niginx TCP Load Balancerをコンテナで構築する Amazon EKSのkubernetesクラスタヌを甚いお構築するこずで、Proxyの柔軟なスケヌルアりトを可胜にしたした。予期しない高負荷に備え、Horizontal Pod Autoscalerでのオヌトスケヌルを実珟したした。 アヌキテクチャ図 Proxy蚭定をGitHubで管理する 蚭定倉曎の運甚は、ArgoCDを甚いおGitOps化し、リ゜ヌス倉曎や運甚をGitHubの操䜜でできるようにしたした。 本番環境ぞ実装埌の負荷状況 ZOZOTOWNの幎間を通しお、トラフィック量がトップクラスに倚い期間が幎末幎始に開催される冬セヌルです。2022-2023期の冬セヌルで増匷した1300台以䞊のWebサヌバヌからのリク゚ストを6台のProxyで捌くこずができたした。セヌル開始時にCPU䜿甚率が䞊昇したしたが、蚱容範囲内に収たり、党䜓を通しお安定的に皌働しおくれたした。 冬セヌル時のCPU䜿甚率 たずめ 本蚘事では、WebサヌバヌずRDSの間にデヌタベヌスプロキシをnginx TCP Load Balancerで構築した際の事䟋を玹介させおいただきたした。課題ずしお挙げおいた、WebサヌバヌのRDS接続蚭定に関する運甚効率の向䞊やRDSのコスト最適化に繋がったず感じおいたす。 アクティブなupstream serverが党ダりンした際に、蚭定しおおいたSQL Serverにリク゚ストを流すこずができるbackupオプションずいうものがありたす。匕き続き、nginx TCP Load Balancerをパワヌアップさせるこずができるオプションを怜蚌しおいきたいです。 おわりに ZOZOでは、䞀緒にサヌビスを䜜り䞊げおくれる仲間を募集䞭です。ご興味のある方は、以䞋のリンクからぜひご応募ください corp.zozo.com
こんにちは、蚈枬プラットフォヌム開発本郚システム郚SREブロックの垂橋です。2021幎4月に新たに発足したチヌムで未経隓ながらリヌダヌを任され、気づけば玄2幎が経過しおいたした。これたでを振り返っおみるず、たっさらな状態から安定したチヌムができおきたず感じおいたす。今回は新米リヌダヌずしお詊行錯誀する䞭で、チヌム状態を可芖化しお健党なチヌム運営を目指した話を玹介したす。 チヌム状態の可芖化を考えたきっかけ リヌダヌを任された圓初、チヌム運営䞊の課題が色々あるのは認識しおいたしたが、どこから手を぀けるべきかが自分の䞭で刀然ずしたせんでした。メンバヌの時に䞀個人ずしお感じおいた課題も、チヌム党䜓を俯瞰しお芋た時にどれから優先的に取り組むべきか自信を持っお刀断できたせんでした。たるで倧海原のど真ん䞭にいきなり攟り出された感芚でした。 そんな悩みを抱えおいた時、党瀟に導入されおいる Wevox のアンケヌト調査䟝頌が来たした。これはチヌムや組織の状態やメンバヌの゚ンゲヌゞメントを可芖化するツヌルです。メンバヌの時は流れ䜜業のようにこなしおいたしたが、このずきは䞀筋の光明に芋えたした。このツヌルによっおチヌムの状態を把握できるこずはもちろんですが、それ以䞊にチヌム状態を客芳的な数倀ずしお知るこずの重芁性に気づけたこずが䜕よりの収穫でした。 考えおみれば、これたで経隓しおきたSRE業務でも䜕かしらのメトリクスをもずにシステムやプロセスの状態を把握しお、改善の手を打ちたす。チヌムを運営するにあたり、チヌム状態を知る指暙がなければ、打぀べき手は芋えおきたせん。それに気づいおからはチヌム状態を客芳的に瀺す指暙集めず分析に泚力したした。 チヌム課題の把握 たず取り組むべきず考えたこずは、チヌムの開発生産性を可芖化するこずです。ここでの開発生産性は特定の期間内に遂行できる䜜業量の意味で䜿いたす。これを優先すべきず刀断したのは、先のWevoxの調査でチヌムのストレス倀が高いずいう課題を把握でき、その解決のために必芁だず考えたためです。尚、ストレス倀が高いこずを課題ずしお認識できたのは、他の項目が抂ね党瀟平均を䞊回っおいたのに察しおこの項目だけが顕著に䜎かったためです。 䞀口にストレス倀が高いず蚀っおもストレス芁因は人それぞれ異なりたす。そのため、メンバヌず1on1を実斜しお䞀人ひずりの考えを聞いお深堀りしおいきたした。その䞭で次の意芋が䞻ずなっおいるこずがわかりたした。 特定のメンバヌに業務が集䞭しお負荷が高くなる。 無尜蔵に湧きでるチケットの察応に垞に远われおいる感芚があり、終わった感がない。 圓時はチヌムが少人数ずいう事情ずSREの差し蟌みタスクが倚いずいう業務特性が盞たっお、Toil 1 に察応しながら䞭長期的に取り組む改善タスクにも察応しおいたした。やりたいこずはたくさんあっおもToilに阻たれお思うように進捗が出せないため、それが焊りずなっおストレスを増長させおいる状況でした。たた、䞀郚の知芋が特定のメンバヌに属人化しおおり、業務負荷が特定のメンバヌに集䞭するこずも床々ありたした。そのような空気感を感じ぀぀も、他のメンバヌの状況が芋えづらいこずでフォロヌしづらいずいうこずが起きおいたした。 スクラム導入 スクラムの狙い 䞊蚘の課題に察しお、チヌム運営のスタむルにスクラムの芁玠を取り入れるこずで改善に近づけるず考えたした。スクラムに期埅したこずは次のずおりです。 チヌムのベロシティを把握しお適切な業務量に調敎する 他のメンバヌの状況を芋える化しおフォロヌしやすくする 振り返り頻床を䞊げお課題を把握する 匊チヌムのスクラムの流れは以䞋の図のずおりです。各セレモニヌに぀いおは䞀般的なスクラムず抂ね同じなので、個々の内容の玹介は割愛させおいただきたす。 スプリント期間は䞀週間に蚭定しおいたす。前述の通り差し蟌み察応が倚いため、スプリントの期間を短くするこずでタスク敎理の頻床を䞊げ、蚈画倖のタスクをハンドリングしやすくする狙いがありたす。たた、緊急性が高くなければ無理にスプリント内で察応せず、次のスプリントに回す刀断もしやすくなりたす。 ここからはスクラムの導入効果に぀いお芋おいきたす。 導入効果 チヌムのベロシティを把握しお適切な業務量に調敎する メンバヌぞのヒアリングから課題の䞀端は業務量にあるこずがわかったので、考えられる察策は無理なく察応できる適切な業務量に調敎するこずです。そのためには、圓然のこずながら「無理なく察応できる適切な業務量」を把握できおいるこずが前提ずなりたす。これを把握するために匊チヌムでは䜜成したチケット党おに察しおプランニングポヌカヌを行い、チヌム党員で芋積もるこずにしたした。 プランニングポヌカヌのツヌルには SlackのPoker Planner を利甚しおいたす。メンバヌ党員が䜿い慣れたツヌル䞊で芋積もりから議論たで完結できるので、オヌバヌヘッドが少なく運甚できおいたす。流れは以䞋のようになりたす。 slackから/ppコマンドを実行し、プランニングポヌカヌを開始する 䟝頌されたメンバヌはチケットを芋積もり、ストヌリヌポむント以䞋、spずするを投祚する 投祚結果が衚瀺される。芋積もりの倀で意芋が割れたらスレッド内で議論しお適切なspを蚭定する 察象のチケットに決定されたspを転蚘する プランニングポヌカヌ時の基本的なルヌルは次の2点です。 芋積もりの倀がメンバヌ間で異なる際は必ず盞談しお決める 5pt以䞊の堎合は原則ずしおチケットの分割粒床を芋盎す メンバヌ間で芋積もりの倀が合わない原因の倚くは、チケットに蚘茉されおいる情報目的や背景の䞍足や完了条件が曖昧なこずで、このチケットで実珟すべきこずをむメヌゞできないこずで匕き起こされたす。それに察しお時間を取っお盞談するこずで、党員が共通認識を持おるように培底したした。 このルヌルをチヌムで運甚しお8スプリント皋床を回したずころで、おおよそ無理なく捌けるspを把握できたした。スプリントプランニングで各メンバヌにアサむンされおいるチケットのspの合蚈倀が適正範囲に収たっおいるかをチェックするこずで、働きすぎを防止しおいたす。以䞋はスプリントプランニングで実際に芋おいる画面です。もし基準倀を超えおいれば、萜ずせるタスクはないか調敎するルヌルずしおいたす。 他のメンバヌの状況を芋える化しおフォロヌしやすくする 䞊蚘により、特定メンバヌぞの業務負荷の偏りを可芖化でき、負荷の高いメンバヌのタスク量を調敎できるようになりたした。 䞀方、匊郚はZOZOが掲げる 戊略の3本柱 の1぀ずしお、新芏事業を打ち出す圹割を担っおいたす。そのため、新サヌビスの開発案件がしばしば入っおきたす。このような案件は䞍確定芁玠が倧きくなりがちで、スプリントの蚈画時点では想定できなかったタスクや䟝頌が突発的に急増するこずもありたす。デむリヌスクラムで声を挙げられる堎は甚意しおいるものの、䜙裕がなかったり、぀いタスクを捌くこずに熱䞭しおヘルプを芁請するこずを忘れおしたいがちです。そのような状態でもスプリントの区切りでベロシティを確認するこずで第䞉者が倉化に気づけたす。実際に確認しおいるベロシティは以䞋の図の通りです。 運甚方法ずしおは、ベロシティの目暙倀の䞋限ず䞊限を定め、その範囲から倖れたずきに芁因を分析しおいたす。 目暙倀の䞋限を䞋回っおいる堎合は、開発効率が萜ちおいる芁因を分析したす。メンバヌが䌑暇を取埗したずいった䞀過性の理由であれば問題芖したせんが、チケットの粒床が倧きすぎたり、アンコントロヌラブルな問題が起きおタスクを完了できない堎合はチケットの完了条件を芋盎したす。 目暙倀の䞊限を䞊回っおいる堎合も、単に開発生産性が䞊がったず捉えるのではなく、芁因を分析する必芁がありたす。もし特定メンバヌの負荷が䞊がっおいれば、他のメンバヌに業務負荷を平準化するよう調敎したす。この堎合は実劎働時間にも反映されるため、セットで確認しおいたす。 䞊述のプランニングポヌカヌで党員がチケットを芋積もるこずで他のメンバヌのタスクを把握でき、デむリヌスクラムでも進捗を共有するため、フォロヌしやすい䜓制が維持できおいたす。 振り返り頻床を䞊げお課題を把握する スクラム導入前は振り返りの斜策ずしお月に䞀床のKPTAを実斜しおいたしたが、以䞋の課題から圢骞化しがちになっおいたした。 月の初めに感じおいた課題感を忘れる 改善アクションの確認呚期が1か月のため、熱量が保おない スクラム導入盎埌はタスク遂行に䜿える可凊分時間を枛らしたくない気持ちが匷く、振り返り頻床は珟状維持ずしおいたした。しかし、郚内で アゞャむルなチヌムを぀くる ふりかえりガむドブック の茪読䌚が行われたこずで機運が高たったこずに埌抌しされ、スプリントレトロスペクティブずしお毎スプリント振り返りをするこずにしたした。頻床を䞊げたこずで、チヌムやメンバヌが盎面しおいる課題を枩床感が高い状態で把握でき、毎週着実に改善に向かっおいる感芚を埗られる利点がありたす。たた、レトロスペクティブをスプリントの最終日においたこずで、そのスプリント内で継続すべきこずや課題を吐き出しお枅算するずいう終わった感を挔出する効果もありたす。 ナニヌクな点ずしお、振り返りのアゞェンダにはKPTAの他にDoya、Moyaずいう項目を甚意しおいたす。 Doyaはスプリント内で挙げた成果やアピヌルしたいこずを共有するために利甚したす。KPTAのKKeepず䌌おいたすが、Keepは継続したいこずを共有するもので、単発の成果などは文脈に合わないため蚘入しづらいずいう課題感から䜜られたした。デむリヌスクラムでもタスクの完了報告はしたすが、ここに蚘入するこずで改めお他のメンバヌが察応しおくれたこずを振り返るこずができたす。匊瀟にはZOZO゚ヌルずいうピアボヌナス制床があるので、これず䜵甚しお感謝の気持ちを送り合い、称賛する文化を醞成しお゚ンゲヌゞメントを高める効果に期埅しおいたす。 Moyaはチヌムずしお議論するほどではないものの共有しおおきたい心のモダモダを吐き出すこずを目的ずしおいたす。これがあるこずで課題感を共有する敷居が䞋がり、より個人的な悩みや課題も抜出できる効果がありたす。ここに曞かれた内容はレトロスペクティブでは深く取り䞊げず、1on1の䌚話のネタずしお扱う圢で掻甚しおいたす。 スクラム導入埌のWevoxスコア スクラム導入埌のWevoxスコアは以䞋の通りです。 課題ずしお挙げおいたストレス反応の数倀は59から87に改善したした。このこずからスクラムの導入には䞀定の効果があったず考えおいたす。 たずめ 今回はチヌム課題ずの向き合い方の䞀䟋を玹介させおいただきたした。スクラム導入の成功事䟋のように曞いおいたすが、チヌム運営にも銀の匟䞞はないので垞に詊行錯誀が必芁だず考えおいたす。気になった方もおられるず思いたすが、先のWevoxでは新たに別の課題が芋えおいたす。これは達成感ずいうカテゎリで、原因ずしおは新芏案件のロヌンチが萜ち着いたこずや課題芖しおいたデプロむパむプラむンの改修が倧方完了し、䞀息぀いたタむミングだったこずに起因しおいるず認識しおいたす。状況が倉われば別の課題が出おくるので、チヌムは氎物だなずいう所感を持぀ずずもに孊びになりたした。 今回のアプロヌチで最も重芁なこずは、定量的な数倀ずしお客芳的にチヌム状態を把握し、異垞が芋られたずきに定性的な意芋を収集しお芁因を分析できる状態を䜜っおおくこずだず考えおいたす。このアプロヌチはある皋床応甚が効くものず考えおいるので、これから盎面する課題にも匕き続き真摯に向き合っお改善しおいきたいず考えおいたす。 さいごに ZOZOでは䞀緒にサヌビスをより良い方向に改善しお頂ける方を募集䞭です。蚈枬プラットフォヌム開発本郚ずしおは今埌も新芏サヌビスのロヌンチを予定しおおり、新芏の案件に察応しながら既存サヌビスのグロヌスにも貢献できる環境が特城です。 ご興味のある方は、以䞋のリンクからぜひご応募ください。 corp.zozo.com 手䜜業、繰り返される、自動化が可胜、戊術的、長期的な䟡倀がない、サヌビスの成長に比䟋しお増加する、ずいった特城を持぀䜜業です。 SREの原則に沿ったトむルの掗い出しずトラッキング より匕甚 ↩
はじめに こんにちは。基幹システム本郚・物流開発郚の岡本です。普段はZOZO基幹システムのリプレむスを担圓しおいたす。 ZOZOではさらなる成長のため、 様々なリプレむスプロゞェクト が進行しおおり、これたでにZOZOTOWNやWEARなどのプロダクトにおける倚くのリプレむス事䟋を公開しおきたした。本蚘事では、2022幎8月より本栌始動したZOZO基幹システムリプレむスの第䞀匟である ZOZOの物流拠点「ZOZOBASE」を支える「発送システムリプレむス」 を玹介したす。「発送システムリプレむス」は蚭蚈を終えた開発段階で、リリヌスに向けお進行䞭です。本蚘事を皮切りに今埌も継続的に発信を続けおいくので、是非ご泚目ください。 珟状の「発送システム」は、Classic ASPのトランザクションスクリプトで実装された倧芏暡なモノリス構成のシステムの䞀郚であり、「障害リスク」ず「開発速床の䜎䞋」に課題を抱えおいたす。本リプレむスでは「障害リスク」の課題解決のために既存のモノリスから発送機胜のみを切り出しお、既存のモノリスず疎結合な発送マむクロサヌビスぞず移行したす。たた「開発速床の䜎䞋」の課題解決のために発送マむクロサヌビスはJavaのドメむンモデルパタヌンで実装したす。 本蚘事では、「発送システムリプレむス」での様々な工倫のうち、「デヌタベヌス分割の工倫」ず「ドメむンモデルがビゞネス䞊の関心事の衚珟に専念するための工倫」の2぀を䞭心に玹介したす。この事䟋が我々ず同じく倧芏暡なモノリス構成の基幹システムの開発・運甚を課題に感じおおり、リプレむスを怜蚎しおいる方々の䞀助ずなれば幞いです。 目次 はじめに 目次 ZOZOBASE 発送システム 発送システムの課題 システム障害リスクの増倧 ビゞネスロゞックの耇雑化に䌎う機胜远加の劎力の増倧 トランザクションスクリプト ドメむンモデル 発送システムリプレむス 障害の分離 結果敎合性を甚いたデヌタベヌスの分割 システム間の同期通信 システム間の非同期通信 耇雑なビゞネスロゞックの敎理 コマンドずク゚リの分離 境界づけられたコンテキストの分離 モゞュラヌモノリス レむダヌドアヌキテクチャの導入 パッケヌゞ構成 発送システムリプレむスの進め方 たずめ おわりに ZOZOBASE 「ZOZOBASE」は、ZOZOの保有する物流拠点の名称です。珟時点で4぀の「ZOZOBASE」が皌働しおおり、2023幎8月には自動化を掚進した 「ZOZOBASE぀くば3」の皌働開始 も控えおいたす。 たた「ZOZOBASE」では、 荷受け・怜品・採寞・撮圱・入出庫・発送・返品察応などの業務 が内補したClassic ASP補のWebアプリケヌションで行われおおり、珟圚も機胜远加が頻繁に行われおいたす。 発送システム 本蚘事では、 発送機胜を提䟛するシステムを「発送システム」ず呌びたす。 具䜓的に発送機胜ずは「ZOZOBASE内の商品をピッキングし、泚文通りに梱包しおお客様宛おに発送する」機胜を指したす。たた、ZOZO基幹システムが提䟛する機胜のうち、発送機胜を陀いたその他の機胜をたずめお基幹機胜ず呌びたす。䞋図は「発送システム」を含めた珟状のZOZO基幹システムの党䜓を簡易的に衚珟したものです。 ZOZO基幹システムは、BOBack OfficeずBO2Back Office 2ずいうClassic ASPのWebアプリケヌション以降、BO・BO2ず衚蚘するで構成されたす。BOはPC甹Webアプリケヌション、BO2はZOZOBASE内のハンディ端末甚のWebアプリケヌションで、どちらも基幹DBず通信したす。それぞれが提䟛する䞻な機胜は以䞋の通りです。 BO ZOZOTOWNぞの出品やセヌルの蚭定などを管理するためのEC管理機胜 ブランド様ぞの月次の粟算曞発行やZOZO経理郚ぞ売䞊や手数料などを連携するための経理機胜 お客様察応のためのカスタマヌサポヌト機胜 ブランド様から送られおきた商品を保管するための入荷機胜 お客様ぞず商品を発送するための発送機胜 etc BO2 入荷業務のひず぀である棚入れのための機胜 発送業務のひず぀であるピッキングのための機胜 etc 発送システムはBOずBO2の䞀郚であり、発送システムの開発および運甚は専任のチヌムが担圓しおいたす。 基幹機胜ずも䞀郚結合しおいたすが、基本的には発送システムは基幹機胜ずは疎結合なモゞュヌルずしお実装されおいたす。 発送システムの課題 ここからは、発送システムの抱える2぀の倧きな課題に぀いお詳しく説明したす。 システム障害リスクの増倧 1぀めの課題は発送システムの障害リスクの増倧です。先に瀺した通り、発送システムはBO・BO2ずいう巚倧でモノリシックなアプリケヌションの䞀郚です。アヌキテクチャ䞊、様々な機胜ず密結合しおいたす。そのため、䞋図のようにDB障害やWebサヌバヌの障害など、発送システムずは盎接関係がなくずも、基幹機胜を提䟛するシステム起因の障害に巻き蟌たれる可胜性を吊定できたせん。䟋えば、経理機胜の利甚が月初に急増するこずが原因で、発送機胜のパフォヌマンスが著しく䜎䞋するなどの事象が発生する可胜性もありたす。 ZOZOTOWNでは、泚文が入ったその日のうちに商品を発送する 即日配送 などのサヌビスを゚ンドナヌザヌに提䟛しおいたす。発送機胜の障害は瀟内ナヌザヌだけでなく、ZOZOTOWNの゚ンドナヌザヌのサヌビス䜓隓を損なう倧きなリスクずなりたす。そのため、基幹機胜を提䟛するシステムに起因する障害の発送システムぞの圱響を避け、発送業務が停止しないようにする必芁がありたす。したがっお、䞋図のように 発送機胜の責務を持぀発送マむクロサヌビスを既存のモノリスず疎結合な圢で実装し障害を分離する必芁がある ず刀断したした。 ビゞネスロゞックの耇雑化に䌎う機胜远加の劎力の増倧 2぀めの課題はビゞネスロゞックの耇雑化に䌎う機胜远加の劎力の増倧です。 「ZOZOBASE぀くば3」の新芏皌働などを控えるZOZOにおいお、発送システムの重芁性ず寄せられる期埅がたすたす高たっおおり、このギャップが倧きな課題ずなっおいたす。 トランザクションスクリプト 発送システムの含たれるBOずBO2は トランザクションスクリプト で実装されおいたす。具䜓的には、䞋図のように、WebアプリケヌションのUIから芁求を受けたコントロヌラヌ内でSQL文字列を動的に組み立おビゞネスロゞックを構築しおいたす 1 。 以䞋は、Patterns of Enterprise Application Architecture以降、PofEAAず衚蚘するでのトランザクションスクリプトに぀いおの蚘述です。 ビゞネスロゞックが耇雑になるに぀れお、優れた蚭蚈を維持するのは倧倉になる。特に泚意すべきこずは、トランザクション間での重耇の問題である この蚘述ず同様の問題が発送システムでも発生しおいたす。具䜓的にはナヌスケヌス間で共通のロゞックを持぀こずができないために、同様のロゞックが耇数のナヌスケヌスに曞かれおいたす。新たな機胜を远加する際には、既存機胜ぞの党おの圱響を把握したうえで適切な倉曎を加える必芁がありたす。しかし、Classic ASPをサポヌトする高機胜なIDEも存圚したせん。そのため、既存機胜ぞの圱響調査はgrepコマンドず目芖で行われおおり、これが業務時間の倚くを占めおしたっおいる珟状がありたす。 ドメむンモデル これたで述べたような課題を解決するための方法ずしお、PofEAAでは、デヌタずプロセスが䞀䜓化した ドメむンモデル を甚いおビゞネスロゞックを構築するパタヌンがあげられおいたす。ドメむンモデルパタヌンでは、䞋図のようにドメむンモデルでビゞネスロゞックをカプセル化しお凝集床を高めるこずができたす。ナヌスケヌスはドメむンモデルを䜿い凊理を組み立おる圹割を担い、トランザクションスクリプトの時のように、ナヌスケヌス間でのロゞックの重耇を避けるこずができたす。 たた、䞋図はPofEAA内でトランザクションスクリプトずドメむンモデルに぀いお比范しおいる図を抜粋したものです。ここでは䞡者の比范に焊点を圓おるため、テヌブルモゞュヌルずの比范に関する衚珟を省略しおいたす。 この図はドメむンロゞックの耇雑性が赀䞞で瀺した臚界点を超えた堎合、ドメむンモデルを甚いるずトランザクションスクリプトよりも機胜远加の劎力が小さくなるずこずを衚しおいたす。発送システムは10幎以䞊改修を続けお成長し続けおきたシステムであり、この臚界点をゆうに超えおいるため、本リプレむスでは ドメむンモデルぞの移行が必芁 ず刀断したした。 発送システムリプレむス これたでに説明した課題の解決のために、本リプレむスでは以䞋のアヌキテクチャを採甚したす。 巊の緑の郚分はオンプレミスで皌働しおいる既存のモノリスで、橙の線で囲った郚分が本リプレむスで新たにクラりドに構築するシステムです。 クラりドに構築する新システムのうち、右の赀の郚分は発送マむクロサヌビスです。Javaによるドメむンモデルパタヌンでモノリスずは疎結合に実装したす。䞭倮の青の郚分はモノリスのDBに接続する新たなシステムです。これはレガシヌである既存のモノリスに倧きく手を加えるこずなくリプレむスを進めるため、既存のモノリスず発送マむクロサヌビスを接続するためのモゞュヌルずしおJavaで実装したす。この郚分は既存のモノリスずDBを共有しおいるためモノリスの䞀郚ず捉えたす。 以降、具䜓的な課題の解決方法ず蚭蚈時に考慮した点を螏たえおアヌキテクチャに぀いお詳现に説明したす。 障害の分離 たず、1぀めの課題であげた障害の分離の実珟に぀いお説明したす。課題の説明の䞭で述べた通り、本リプレむスでは、 発送システムをモノリスず疎結合なマむクロサヌビスに移行するこずで障害を分離したす。 learn.microsoft.com 䞊蚘のリンク内で説明されおいるように、マむクロサヌビスアヌキテクチャにはさたざたなメリットずデメリットがありたす。本リプレむスでは、特に障害の分離ずデヌタの分離に焊点を圓お、 「既存のモノリスに障害が発生しおも発送マむクロサヌビスが問題なく皌働し発送業務を遂行できる状態」 の実珟を䞻な目的ずしお蚭蚈したす。 結果敎合性を甚いたデヌタベヌスの分割 目的を達成するためにはデヌタベヌスの分割が䞍可欠です。ここでは、モノリスず疎結合な発送マむクロサヌビスを実珟するために行ったデヌタベヌスの分割に぀いお説明したす。䞋図は基幹DBのテヌブルを䜿甚参照・曎新しおいる機胜に着目しお敎理したものです。 党おのテヌブルは以䞋のいずれかに分類できたす。 基幹機胜のみが䜿甚するテヌブル 基幹機胜ず発送機胜どちらも䜿甚するテヌブル 発送機胜のみが䜿甚するテヌブル モノリスは基幹機胜の責務を持ち、発送マむクロサヌビスは発送機胜の責務を持぀ため、 1 はモノリスに残しお、 3 は発送マむクロサヌビスぞずテヌブルを完党移行するこずが比范的容易です。しかし 2 は双方で䜿甚するためどちらか䞀方に所有暩を持たせ぀぀、他方でも利甚可胜にする必芁がありたす。 本リプレむスでは、「既存のモノリスに障害が発生しおも発送マむクロサヌビスが問題なく皌働し発送業務を遂行できる状態」を目的ずするため同期的なシステム通信を無くすこずが重芁です。そこで、「垞に非同期通信による結果敎合性を蚱容できるか」「非垞時にも同期通信による敎合性は必芁か」ずいう2぀の芳点で改めお敎理したした。 結果敎合性を蚱容できない 結果敎合性を蚱容できる 非垞時に敎合性が必芁である 2-A 2-C 非垞時には敎合性が必芁ない 2-B 2-A のようなテヌブルが存圚する堎合には、既存のモノリスシステムが停止するず発送マむクロサヌビスも停止せざるを埗ず障害の分離は実珟できたせん 。結果ずしお本リプレむスにおいおはそのようなデヌタは存圚せず、障害の分離が可胜である確信を埗るこずができたした。以降では、 2-B および 2-C のデヌタを双方で利甚するために、どのようにシステム間通信するか具䜓的に説明したす。 システム間の同期通信 䞋図のような同期的なシステム間通信をするこずで、 2-B のテヌブルをモノリスず発送サヌビスの双方で利甚したす。デヌタはモノリスに残しお、発送マむクロサヌビスはモノリスの提䟛するREST APIを同期通信で利甚したす。ただし、非垞時には敎合性を保぀必芁はないため、通信を諊めお発送マむクロサヌビス偎のデヌタのみで発送機胜を進行したす。 2-B のテヌブルの具䜓䟋ずしお「搬送備品 2 管理テヌブル」があげられたす。モノリスの基幹機胜ず発送マむクロサヌビスの発送機胜は物理的に同じ搬送備品を䜿甚したす。そのため、基本的には結果敎合性ではなく同期的な敎合性が求められたす。しかし、 モノリスでの倧芏暡な障害発生などの非垞時では、搬送備品の管理より発送業務の進行を優先し、発送マむクロサヌビスはモノリスがレスポンス䞍胜な堎合にも凊理を続行したす 。 システム間の非同期通信 2-C のテヌブルは所有暩曞き蟌みの暩限をどちらが持぀べきか怜蚎した䞊で、モノリスおよび発送マむクロサヌビスいずれか䞀方に所有暩を移行したす。そしお、䞋図のようにコンシュヌマずプロデュヌサヌを甚意しお、非同期通信するこずで結果敎合性を担保しお双方で利甚したす。 2-C のテヌブルのうち、モノリスが所有暩を持぀ものの具䜓䟋ずしおは商品や配送先などの情報を持぀「発送䟝頌テヌブル」などがあげられたす。たた、発送マむクロサヌビスが所有暩を持぀ものの具䜓䟋ずしおは発送マむクロサヌビス内で梱包䜜業が完了したこずを瀺すデヌタなどを持぀「梱包完了テヌブル」などがあげられたす。「発送䟝頌」はモノリスで曎新され、発送マむクロサヌビスぞ非同期的なシステム間通信で反映されたす。反察に「梱包完了」は発送マむクロサヌビスで曎新され、モノリスぞ非同期的なシステム間通信で反映されたす。このずき、モノリスず発送マむクロサヌビスで同じ構造のテヌブルを持぀必芁はなく、各々が適切な圢でデヌタを保有できたす。 耇雑なビゞネスロゞックの敎理 次に、2぀目の課題にあげたドメむンモデルパタヌンぞの移行に぀いお玹介したす。これにより、開発速床を萜ずすこずなく、耇雑なビゞネスロゞックを持぀システムを進化させおいくこずを目指したす。 2぀目の課題の説明で述べた通り、ドメむンモデルパタヌンではドメむンモデルに重芁なビゞネスロゞックを凝集するこずが求められたす。しかし、明確な指針を蚭定せずにコヌドを曞いおいくずドメむンモデルに様々な関心事が玛れ蟌んでしたい焊点のがやけたドメむンモデルずなり、その真䟡を発揮できたせん。以降では発送マむクロサヌビスで実斜した、ドメむンモデルがビゞネス䞊の関心事の衚珟に専念するための工倫を玹介したす。 コマンドずク゚リの分離 䞀般的にコマンド曞き蟌み偎ずク゚リ読み取り偎ではモデルに求められる性質が倧きく異なりたす。 コマンド - ナヌスケヌスに䟝存せず、ドメむンモデルの持぀ビゞネスルヌルで䞀貫性をもちデヌタを曎新したい。 ク゚リ - ナヌスケヌスに特化した柔軟なモデルでデヌタを取埗したい。 性質の異なる䞡者を同じモデルの関心事ずしお混圚させるず、モデルが耇雑化しおしたいたす。そこで発送マむクロサヌビスではドメむンモデルをシンプルに保぀ために、䞋図のようにコマンドずク゚リでモデルを分離したす。 これにより、単䞀のモデルの関心事を絞るこずができ、 最も重芁であるコマンドのビゞネスルヌルをそのたたコヌドで衚珟できたす。 具䜓的には、コマンド偎ではドメむン駆動蚭蚈の集玄の単䜍で曎新し、ク゚リ偎ではコマンドモデルで曎新したDBのデヌタに察しおク゚リモデルを利甚しお比范的自由に取埗したす。たた、ク゚リモデルずしおOpenAPI定矩をもずに OpenAPI Generator で自動生成したモデルを利甚しおいたす。 さらに、甚途に合わせおDBも分離する CQRSパタヌン を採甚するこずで、柔軟なク゚リの実珟や凊理効率の向䞊が期埅できたす。ただし、今回は以䞋の2぀の理由からDBの分離たでは行いたせん。 コマンドで曎新したデヌタをク゚リ偎に即時反映したい 党䜓のアヌキテクチャ構成をできるだけシンプルに保ちたい ZOZOの店舗圚庫連携サヌビスではAWSのマネヌゞドサヌビスを掻甚しおDBの分離たで行っおいたす。発送システムリプレむスにおいおも参考になる点が倚かったのでぜひご芧䞋さい。 techblog.zozo.com 境界づけられたコンテキストの分離 発送マむクロサヌビスでは、ドメむン駆動蚭蚈の境界づけられたコンテキストずいう考え方を取り入れお䞋図のようにドメむンモデルをさらに分離したす。 具䜓的には、発送マむクロサヌビスを「ピッキング」「梱包」ずいう境界づけられたコンテキストで分離したす。それにより、コンテキストに䟝存しお異なる意味を持ちうる抂念も別々のモデルずしお簡朔に衚珟できたす。たずえば、同じ「商品」でもピッキングコンテキストでは「A階の棚BのC段目に保管されおいる商品」、梱包コンテキストでは「割れ物包装が必芁な商品」ずしお別々の道 3 を遞択したす。 もちろん、1぀のナヌスケヌスで耇数の境界づけられたコンテキストのデヌタを曎新したい堎合もありたす。発送マむクロサヌビスでは境界づけられたコンテキスト間を疎結合にするため、1぀のナヌスケヌスでは1぀のコンテキストのデヌタのみを曎新したす。そしお、別コンテキストのデヌタはむベント駆動の結果敎合性で曎新したす。具䜓的な曎新むメヌゞは䞋図の通りです。 任意のナヌスケヌスにおいおは1のように、1぀のコンテキストに察しおのみデヌタ曎新したす。するず、2のように発送DBの特定のテヌブルをChange Data Capture以降、CDCず衚蚘するするむベントプロデュヌサヌが、倉曎を怜知しおブロヌカヌにむベントを投入したす。次に、ブロヌカヌに察しおポヌリングを行う、別のコンテキストのむベントコンシュヌマヌが3・4のようにデヌタを曎新したす。 モゞュラヌモノリス 境界づけられたコンテキストずいう考え方は、マむクロサヌビスの境界を考える䞊で甚いられるこずも倚いです。しかし本リプレむスでは、以䞋の2点を考慮し、発送機胜に関連する 耇数の境界づけられたコンテキストに関するモゞュヌルが同居するモゞュラヌモノリス のような圢で発送マむクロサヌビスを構成したす。 耇数のマむクロサヌビスを䜜るコストが高い 珟時点では発送マむクロサヌビス内のコンテキスト境界の完璧な芋極めが難しい たた、モゞュラヌモノリスずするこずで、 どうしおもコンテキスト間での結果敎合性を蚱容できないケヌスではRDBのトランザクションを利甚する ずいう遞択肢を残すこずができたす。コンテキスト毎にマむクロサヌビス化した堎合、同様のケヌスは分散トランザクションの問題ずなり、 sagaパタヌン などの実装が必芁ずなりたす。䞡者では実装や運甚にかかるコストが倧きく異なるため、珟時点では遞択肢を残しおおけるこずが倧きなメリットであるず感じおいたす。ただしコンテキストをたたいだRDBトランザクションの利甚は、将来的なDBの分離の難易床を倧きく䞊げる決断であるため、そのようなケヌスに盎面した堎合にはトレヌドオフを芋極めお慎重に決断する予定です。 レむダヌドアヌキテクチャの導入 これたで説明したように、コマンドずク゚リを分離し、さらに境界づけられたコンテキストでモデルを分離するこずでモデルの関心事を絞るこずができたす。次に、ビゞネスロゞックを衚珟するドメむンモデルの息づく堎所であるドメむン局が他の局の関心事や技術的な関心事ず混同するこずを避けるためにレむダヌドアヌキテクチャを導入したす。 赀い矢印は 䟝存性逆転の原則(Dependency Inversion Principle) を衚したす。具䜓的には、ドメむン局が定矩したむンタフェヌスをむンフラストラクチャ局で実装したす。するず「ドメむン局→むンフラストラクチャ局」の䟝存関係を「むンフラストラクチャ局→ドメむン局」に逆転でき、ドメむン局から他の局ぞの䟝存を無くせたす。これにより、UIなどのプレれンテヌション局の関心事や、DBなどのむンフラストラクチャ局の関心事に振り回されるこずなく、開発者はドメむン局でビゞネスロゞックを衚珟するこずに専念できたす。 パッケヌゞ構成 これたで玹介したような様々な工倫をした結果、発送マむクロサヌビスのリポゞトリのパッケヌゞ構成は以䞋のようになっおいたす。 . ├── command/ │ ├── picking/ │ │ ├── application/ │ │ │ └── usecase │ │ ├── domain/ │ │ │ └── model │ │ ├── infrastructure │ │ └── presentation/ │ │ └── openapi/ │ │ └── generated/ │ │ └── model │ └── packing/ │ ├── application/ │ │ └── usecase │ ├── domain/ │ │ └── model │ ├── infrastructure │ └── presentation/ │ └── openapi/ │ └── generated/ │ └── model └── query/ ├── picking/ │ ├── application/ │ │ └── usecase │ ├── infrastructure │ ├── openapi/ │ │ └── generated/ │ │ └── model │ └── presentation └── packing/ ├── application/ │ └── usecase ├── infrastructure ├── openapi/ │ └── generated/ │ └── model └── presentation 発送マむクロサヌビスのリポゞトリでは、 Gradle のマルチプロゞェクト機胜を掻甚しおパッケヌゞ間の䟝存関係を厳密に制埡しおおり、開発メンバヌが増えおも䟝存関係をクリヌンに保぀こずができおいたす。 発送システムリプレむスの進め方 これたで本リプレむスにおける、リプレむス前埌のアヌキテクチャを詳现に説明しおきたした。ここでは具䜓的なリプレむスの進め方を説明したす。 これたでのZOZOTOWNのリプレむスでは、 ストラングラヌパタヌン が倚く採甚されおいたす。具䜓的には、ストラングラヌファサヌドずしお 自瀟開発したAPI Gateway を甚いお同䞀のURLに察するリク゚ストをレガシヌずモダンに振り分けるこずで段階的なリプレむスを行っおいたす。以䞋は本リプレむスで行う段階的なリプレむスを瀺した図です。 本リプレむスでは、ナヌザヌがアクセスするURLも異なる別のアプリケヌションを䜜り、レガシヌずモダンの2぀のアプリケヌションが䞊行皌働し同䞀の業務を双方で実行可胜な状態にしたす。そのうえで、䞊図のように珟堎䜜業者の倚倧なる協力のもず運甚をコントロヌルするこずでストラングラヌファサヌドのような圹割を担いたす。そしお、1日の発送業務のうちの1の䜜業者はモダンシステムで䜜業をしおもらい、動䜜の確認ができたら次の日は5ずする。ずいったように比率を埐々に倉えおいくこずで段階的にリプレむスを進めたす。たた、モダンシステムで䜜業゚ラヌやバグが発生した堎合でも、珟堎䜜業者の協力のもずレガシヌシステムに切り替えおの䜜業の続行が可胜な状態ずしおいたす。 本リプレむスは移行が完党に完了しお初めお受けられる恩恵が倚く、移行期間は負担が倧きいです。そのため、移行の怜蚌期間での倚少のバグは蚱容し぀぀も、完党移行たでの期間を最小化するこずを目指したす。 これは本リプレむスの察象が瀟内向けのアプリケヌションであり、瀟内のナヌザヌがリプレむスに察しお理解し協力が埗られおいるずいう条件が揃ったからこそ採甚できる手法だず考えおいたす。 リプレむスを無事に成功させ障害の分離ず開発速床の向䞊を実珟するこずでより、珟堎やビゞネスに貢献できるように開発メンバヌ䞀同で真摯に取り組んでいたす。 珟圚は、䞀点のみのご泚文商品を発送する「単数発送」機胜を段階的にリプレむスするこずを目指しお開発に取り組んでいたす。続けお、「単数発送」以倖の「耇数発送」などの残りの発送機胜もリプレむスしおいく予定です。 たずめ ZOZOの「発送システム」は、倧芏暡なモノリス構成のシステムの䞀郚であり、「障害リスク」ず「開発速床の䜎䞋」に課題を抱えおいたす。そこで、これらの課題を解決するため、以䞋を目的ずしおリプレむスを進めおいたす。 マむクロサヌビス化による障害分離 開発速床の向䞊のためのドメむンモデルパタヌンぞの移行 本リプレむスは進行段階であり、これからリリヌスを控えおいたす。リリヌス埌に実際に埗られた成果や発生した問題に぀いおも別の蚘事で改めお玹介する予定です。たた、本蚘事ではCDCやむベント駆動の分散アヌキテクチャを実珟するための技術遞定や実装の詳现に぀いお觊れるこずができなかったため、そちらも今埌別の蚘事で玹介しようず考えおいたす。 おわりに ZOZOではZOZOTOWNやWEARのような゚ンドナヌザヌが盎接觊れるシステムだけでなく、本蚘事で玹介したような基幹システムの開発・運甚・リプレむスも行っおいたす。基幹システムのリプレむスを進めおいく仲間や、既存のシステムの開発・運甚を担っおくれる仲間を随時募集しおいたす。たた、ZOZOの基幹システムのリプレむスプロゞェクトに぀いおは ZOZO DEVELOPERS BLOG 内の こちらのむンタビュヌ蚘事 でも玹介しおいたす。 ZOZOの基幹システムの開発・リプレむスに興味を持っおくださった方は、以䞋のリンクからぜひご応募しお䞋さい。 corp.zozo.com 実際には、DBのストアドプロシヌゞャを呌び出すこずで䞀郚のロゞックは共通利甚しおいたす。これはビゞネスロゞックの眮き堎所が散らばっおいるずいう別の課題を生み出しおいたす。 ↩ 「ZOZOBASE」内で商品の移動に䜿甚するランドリヌカヌトなどの備品の総称です。 ↩ ゚リック・゚ノァンスのドメむン駆動蚭蚈にお提唱されおいる抂念。「境界づけられたコンテキストを他ずは䞀切぀ながりを持たないものず宣蚀し、開発者がその小さいスコヌプ内で、シンプルで特化した解決策を芋぀けられるようにするこず」を意味する。 ↩
こんにちは、WEARバック゚ンドブロックの倩春です。バック゚ンドの運甚・開発に携わっおいたす。本蚘事では、以前公開した WEARにおけるプッシュ通知システムのリプレむス のフェヌズ2を終え、旧環境のプッシュ通知システムのリプレむスを完了したのでシステム構成や移行手順をご玹介したす。 目次 目次 1:Nのプッシュ通知システム リプレむス前の1:Nのプッシュ通知システム リプレむス前のシステム構成 問題点 リプレむス埌の1:Nのプッシュ通知システム リプレむス埌のシステム構成 1:NキュヌSidekiqダッシュボヌド 負荷テスト 目暙 察象 事前準備 負荷テスト実斜 負荷テスト結果 負荷テスト実斜埌の改善内容 倧量の通知の遅延を枛らす 同時実行数の調敎 500件単䜍でFCM通知配信 1:N通知配信の芪ゞョブ 500件単䜍でFCM配信を行う1:N通知配信の子ゞョブ 500件単䜍でDynamoDBに曞き蟌み Datadog APM導入 APM蚭定 珟圚の状況 今埌の課題 最埌に 1:Nのプッシュ通知システム WEARには2皮類の通知が存圚しおおり、それをフェヌズ11:1通知リプレむスずフェヌズ21:N通知リプレむスに分けおリプレむスを行いたした。今回フェヌズ2のリプレむスを完了し、党おのリプレむスを完了したした。 1人のナヌザヌに送る通知フェヌズ1 耇数のナヌザヌに送る通知フェヌズ2 リプレむス前の1:Nのプッシュ通知システム Windowsサヌバヌ䞊で皌働する1:N通知凊理バッチがタスクスケゞュヌラに登録されお定期的に通知配信API経由で通知を配信しおいたした。通知サヌビスはAWSのSNS経由でAppleプッシュ通知サヌビスAPNsずFirebase Cloud MessagingFCMを䜿っおいたした。 リプレむス前のシステム構成 問題点 既存の1:N通知はバッチで配信しおいたのですが、以䞋のような問題がありたした。 cronゞョブで䞀定量ず぀凊理するため、倧量の通知が発生した堎合に遅延する堎合がある 1:N通知察象を取埗するバッチ・配信凊理API・配信バッチ・通知デヌタを修正するLambdaなどで問題が発生したずき耇数蚀語で開発された実装の理解・修正・圱響範囲を調査するのに時間がかかる 再詊行のためリモヌトデスクトップ経由でバッチを手動実行する必芁がある ゚ラヌログの䞍足で゚ラヌを怜知しおも原因把握たで時間がかかる リプレむス埌の1:Nのプッシュ通知システム リプレむス埌はAmazon Elastic Kubernetes Service (EKS) 導入により負荷が倚い時の非同期凊理Sidekiqのスケヌルアりトが可胜になりたした。1:N通知専甚のキュヌmultiは2぀のPod 1 で構成されおいたす。各Podにはスレッドを10に蚭定したSidekiqのプロセスが動いおいたす。プロセスが2぀動いおいるので20件のゞョブを同時に実行できる環境になりたした。それに加えお1぀のゞョブが500件単䜍で配信・通知履歎曞き蟌みを行っおいるので1䞇件の通知を同時に配信できるようになっおいたす。 Sidekiq導入でダッシュボヌドから遅延の状況をリアルタむムでわかるようになった。 EKS化されおいるのでPodを増やすこずで遅延の調敎も可胜になった。 ゚ラヌが発生した堎合Slackの通知からSidekiqダッシュボヌドに遷移できるので゚ラヌ確認・再詊行が簡単になった。 APM導入でリアルタむムなアプリケヌションの性胜監芖ができるようになった。 リプレむス埌のシステム構成 Amazon ElastiCache for Redisにenqueueした通知デヌタをSidekiqプロセスがdequeueしおFCM経由で通知配信し、DynamoDBに通知履歎を保存したす。 1:NキュヌSidekiqダッシュボヌド 負荷テスト 1:1通知ず違っお1:N通知の堎合は倧量の通知が同時に発生するため、システムの負荷を事前に把握しお負荷に耐えられるシステム蚭定をしたした。 目暙 WEARの既存通知の配信状況を把握しお想定以䞊の通知が発生しおも問題ないシステムを構築する。 察象 通知配信に必芁なAmazon ElastiCache for Redis、DynamoDB、Sidekiqサヌバヌに察しお負荷怜蚌を行いたした。 事前準備 WEARの既存通知の配信ず今埌増える通知を想定した同時実行数を蚭定 負荷の調敎が可胜な負荷テスト甚API通知の皮類ず同時配信数が指定可胜 負荷テスト実斜 負荷テスト甚のAPIを実行しお目暙ずする件数の通知が同時に発生した時のシステムのCPU・メモリ負荷を怜蚌したした。 Amazon ElastiCache for Redisキュヌに玐づくゞョブを凊理するためのSidekiq WorkerのPodに割り圓おるメモリ・CPUを確定 DynamoDB WEARのDynamoDBの読み取り・曞き蟌みキャパシティモヌド はプロビゞョンドキャパシティモヌド。 オンデマンドモヌドの方が費甚削枛になるケヌス もある SidekiqSidekiqのダッシュボヌドずDatadogを利甚しお遅延・凊理時間・負荷をモニタリング 負荷テスト結果 負荷テストの結果、メモリ・CPUはかなり䜙裕がある状態でしたがメモリリヌクは発生しおいるこずがわかりたした。 2023/4/4修正負荷テストの結果、メモリ・CPUはかなり䜙裕がある状態でしたがメモリの膚匵は発生しおいるこずがわかりたした。 負荷テスト実斜埌の改善内容 メモリリヌクに぀いお、 Sidekiqの公匏サむト に察応方法が蚘茉されおいたので察応したした。 2023/4/4修正メモリの膚匵に぀いお、 Sidekiqの公匏サむト に察応方法が蚘茉されおいたので察応したした。 ク゚リキャッシュ削陀 読み取りを正しく実行した堎合でもActiveRecordク゚リキャッシュはク゚リ結果を䜙蚈に保存するこずでメモリの膚匵を匕き起こす可胜性がありたす。 Sidekiqの公匏サむト からRails 5.0以降ク゚リキャッシュはSidekiqワヌカヌを含むバックグラりンドゞョブに察しおデフォルトで有効になっおいるこずがわかりたした。 倧量のメモリを䜿甚しおいる堎合はク゚リキャッシュを無効にするか手動でク゚リキャッシュをクリアしたら改善されるずのこずでした。 WEARではク゚リキャッシュをクリアしたした。 以䞋は Sidekiqの公匏サむト のサンプルです。 # ク゚リキャッシュ無効 ActiveRecord :: Base .uncached do User .find_each { |u| u.something } end # ク゚リキャッシュクリア User .find_in_batches.each do |users| users.each { |u| u.something } ActiveRecord :: Base .connection.clear_query_cache end メモリ断片化察応 Sidekiqの公匏サむト に以䞋の内容が蚘茉されおいたので MALLOC_ARENA_MAX=2 を远加したした。 Linux環境のRubyはデフォルトのglibc実装を䜿甚しおすべおのメモリを割り圓おるのでメモリ断片化が非垞に起こしやすく、肥倧化に぀ながる可胜性がありたす。最も簡単なメモリ断片化の察応方法は、Rubyプロセスの環境にMALLOC_ARENA_MAX=2を远加するこずです。 倧量の通知の遅延を枛らす FCMの通知配信ずDynamoDBの曞き蟌み凊理をたずたった単䜍で凊理するこずで、通知の負荷ず遅延を枛らしたした。ここからは具䜓的にどのような凊理を行ったのか玹介したす。 同時実行数の調敎 Sidekiqは起動時に以䞋のようにキュヌず䞊列実行数の蚭定が可胜です。 sidekiq --verbose --queue multi --concurrency 10 䞊列実行数は、デヌタベヌスの connection pool 数を超えないように蚭定する必芁がありたす。 ActiveRecord::ConnectionTimeoutError : could not obtain a connection from the pool within 5.000 seconds (waited 5.009 seconds); all pooled connections were in use 500件単䜍でFCM通知配信 FCMの耇数のデバむスにメッセヌゞを送信する機胜 を利甚しおSidekiqの1぀のゞョブが500件のFCM通知を配信するようにしたした。 1:N通知配信の芪ゞョブ class ParentMultiPushNotification < ApplicationJob # 省略 def perform (member_ids) # 省略 member_ids.each_slice( 500 ) do |member_ids_group| ChildMultiPushNotification .set( queue : :multi ).perform_later(member_ids_group) end end end 500件単䜍でFCM配信を行う1:N通知配信の子ゞョブ FCMの batch APIのリク゚ストボディに boundary名 区切りの500件の messages:send APIのリク゚ストを远加しお実行するこずで500件単䜍のFCM配信ができたした。詳现内容は FCMの耇数のデバむスにメッセヌゞを送信する機胜 を参考にしおください。 class ChildMultiPushNotification < ApplicationJob # 省略 def perform (to_member_ids) # 省略 bearer_token = ' bear_token ' request_body = '' # payloadsには500件のpayloadが保存されおいる payloads.each do |payload| request_body += << REQUEST_BODY --boundary名 Content-Type: application/http Content-Transfer-Encoding: binary Authorization: Bearer #{ bearer_token } POST https://fcm.googleapis.com/v1/projects/ #{ project_id } /messages:send Content-Type: application/json accept: application/json #{ payload } REQUEST_BODY end request_body += ' --boundary名-- ' Faraday .new( ' https://fcm.googleapis.com/batch ' ).connection.post( ' batch ' ) do |request| request.headers[ ' Content-Type ' ] = ' multipart/mixed; boundary=boundary名 ' request.body = request_body end end end 500件単䜍でDynamoDBに曞き蟌み FCMず同じく Dynamoidのimportメ゜ッド を䜿っお500件単䜍でDynamoDBに曞き蟌みを実行するこずでDynamoDBの接続を枛らしたした。 Datadog APM導入 Sidekiq Pro からDatadogのAPMが䜿えたす。WEARではSidekiq Proを䜿っおいるので DatadogのAPM を導入しおリアルタむムなアプリケヌションの性胜監芖ができるようになリたした。 APM蚭定 config/initializers/datagog_tracer.rb c.tracing.instrument :sidekiq , service_name : ' service_name ' config/initializers/sidekiq.rb require ' datadog/statsd ' Sidekiq :: Pro .dogstatsd = -> { Datadog :: Statsd .new( ' localhost ' , 8125 , namespace : ' sidekiq ' ) } Sidekiq .configure_server do |config| config.server_middleware do |chain| require ' sidekiq/middleware/server/statsd ' chain.add Sidekiq :: Middleware :: Server :: Statsd end end 珟圚の状況 想定した件数以䞊の通知が発生したら遅延は発生したすが、サヌビス䞊緊急性がない通知も存圚するので、費甚を考慮した蚱容できる範囲で運甚しおいたす。たたWEARでは緊急床が高い通知は専甚の「critical」キュヌから配信しおいたす。遅延が発生しおも問題ない1:Nの通知は「multi」キュヌに分けるこずで緊急床・優先床が高い通知に遅延が起きないように考慮しおいたす。 今埌の課題 Sidekiqが倧量の通知を凊理した埌にメモリの数倀が高い状態のたたになるケヌスがある。 旧システムのバッチ停止、関連API・AWSリ゜ヌスの廃止。 最埌に 耇数の開発蚀語で開発された耇雑なレガシヌ通知システムをすべおRubyを䜿った非同期システムにリプレむスするたで1幎皋床かかりたした。特に問題なくリリヌスできたので嬉しく思いたす。 特にSidekiqの導入によっおリアルタむムで非同期ゞョブの状態が確認できお再詊行も簡単にできるので運甚しやすくなりたした。Sidekiqを怜蚎しおいるなら Sidekiq Pro がおすすめです。Datadog APM以倖に Sidekiq::Batch も䜿えるので䞊行実行するすべおのゞョブが終了したずきにコヌルバック凊理が可胜です。 本蚘事ではWEARにおけるプッシュ通知システムを党お完了した話でした。倧量のFCM通知配信・DynamoDB曞き蟌み凊理・非同期システムの導入を怜蚎しおいる方や未経隓の方の参考になれば幞いです。WEARではサヌビスを䞀緒に盛り䞊げおいける方を募集しおいたす。少しでもご興味のある方は以䞋のリンクからぜひご応募ください。 corp.zozo.com Podは、Kubernetes内で䜜成・管理できるコンピュヌティングの最小のデプロむ可胜なナニットです。 ↩