メドピア開発者ブログスマートフォン用の表示で見るメドピア開発者ブログ集合知により医療を再発明しようと邁進しているヘルステックリーディングカンパニーのエンジニアブログです。Tumblr11月に入社したCTO室SREの@sinsokuです。やっていることはRailsアプリのレビューや開発環境の改善です。v6への移行CircleCIの実行時間の短縮、稀に落ちるテストの修正その他の細かい改善このうち、CircleCIについて知見が溜まったので技術ブログで紹介します。CircleCIで気をつける点CircleCIの実行時間を短くするにはいくつかコツがあります。JavaScriptのテストはnpmのキャッシュだけ復元するワークフローを図にするとこんな感じです。CircleCIのキャッシュキーには複数のキーを指定することができます。gem-cache-v1キャッシュキーを複数指定すると上から順番にキャッシュを探します。bundle-installで全てのgemをインストールしないで済むようになります。部分キャッシュを使っているとgemが増え続け、キャッシュのリストアに時間がかかる問題が起きます。すれば良いのですが、少しずつ遅くなるため気づき辛い問題です。LateちなみにYarnは自動的に不要なnpmを消してくれます。Orb上記の点を気にしながら、社内のRailsアプリで横断的に対応するのは大変なので、誰でも良い感じに設定できるOrbを作りました。github.com実際に社内のいくつかのRailsプロジェクトに導入しています。registryを参考にしてください。許可する必要があります。gemやnpmのキャッシュについてGitリポジトリのデフォルトブランチをキャッシュキーに含めることでキャッシュヒット率を上げています。}}一般的なジョブを提供CircleCIの設定を簡単にできるように、以下4つのジョブを提供しています。した直後なら以下の設定で良い感じにCircleCIが動きます。rails/assetsジョブとコマンドを組み合わせるOrbの提供するrb-depsジョブとbundle-installコマンドを組み合わせると、RuboCopを実行するジョブなども短く書けます。記述をかなり減らせるかなと思います。Orbを試してみてください。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。Tumblr10km40分切りが2020年の目標、メドピア長距離部の小宮山です。CSSはご存知でしょうか。tailwindとは「追い風」を意味します。最高に気持ちよく走れるコンディションですね。CSSがもたらしたものまとめサービス概要まずは今回新たに立ち上げたサービスの紹介です。「MedPeerスポット×リクルートメディカルキャリア」という医師向けスポット求人マッチングサービス(以下、本サービス)が11月にリリースされました。medpeer.co.jpログインや応募などサービスのコアな部分はMedPeer医師会員限定になってしまいますが、サイトの雰囲気自体は非会員でも十分に味わえますので是非ともサイトを開いてみてください。医師向けスポット求人という普段なかなか見ることができない世界を覗くこともできます。本サービスの技術的な構成をざっと紹介していきたいと思います。Rails(以下、Rails)です。最新ほやほやの6.0です。自分はフロントエンド畑な人間でそれほどRailsに精通しているわけでもなくそのすごさをちゃんとは理解していませんが、きっとすごいことなんだと思います。以前のMedPeerなら、「Railsを使っています、以上です。」で終わってしまっていたところですが、なんと今回はもうひとつ目玉技術があります。Nuxt.js(以下、Nuxt)です。ja.nuxtjs.org実はNuxtの利用自体はMedPeerでは初めてではありませんでした。静的サイトのジェネレータとしてシンプルなLPを作成した実績はすでにあります。かかりつけ薬局化支援サービス「kakari」のLPがまさにそれにあたります。kakari.medpeer.jpではなぜ再度Nuxtを強調し直すのか。なんと今回は、Nuxtを本番運用してSSR(サーバーサイドレンダリング)するというMedPeer初の挑戦だったのです。バックエンド(Rails)とフロントエンド(Nuxt)をリポジトリもAWSリソースも分離してしまうというMedPeer初だらけの野心的な技術構成となっていました。非常に申し訳ないのですが、今回伝えたいのはNuxtのことでもフロントエンド分離のことでもありません。開発プロセスやVue.jsの話題ですらありません。CSSのことを伝えたいモチベーションが高すぎました。新大陸の大地そのものよりも、そこに吹いていた追い風にこの心を掴まれてしまったのです。CSSとは何かtailwindとはずばり「追い風」を意味します。今日の走りは絶好調だと思ったら折返しで現実に引き戻されるやつですね。公式サイトの文言をここに引用します。override.一応CSSフレームワークという分類になりそうですが、BootstrapやVuetifyといった所謂一般的なCSSフレームワークとはやや毛色が異なります。CSSが提供してくれるのはスタイルを便利に指定するためのツール類だけです。CSSでは用意されたツールを組み合わせてダイアログというコンポーネントを自力で作らなければいけません。CSSが提供してくれるのは自動車本体ではなく、その名前が意味する通り、自力で走る者たちへの追い風です。HTMLとCSSWebページを構成する3要素といえば、HTML・CSS・JavaScriptです。HTMLはWebページの構造を、CSSはレイアウトや装飾を、JavaScriptは何かをしてくれます。(今回はJavaScriptについてはあまり掘り下げません。)ja.wikipedia.org文書の構造と体裁を分離させるという理念を実現する為に提唱されたスタイルシートの、具体的な仕様の一つ。令和を生きる私はCSSが登場したころの状況を実際には知らないのですが、どうやらCSSはHTMLからスタイルを分離させることを目的に生み出されたもののようです。実際に、文章構造とスタイルやHTMLとCSSそれぞれに切り出して役割分担させるのがWebの作法だという認識が広く広まっていたと思います。}なんの変哲もない例を出すとこんな感じでしょうか。HTML側ではタグにclassを付け、CSS側ではそのclassをセレクタとしてスタイルを付与しています。一方でこんなHTMLはどうでしょうか。2</li></ul>スタイルをCSSに分離することなく、タグにそのまま書き込んでいます。所謂インラインスタイルです。基本的に、インラインスタイルが歓迎されることはほとんどないと思われます。実際に私もこのコードだけを見たらレビューコメントを入れる手を止めることはできないでしょう。歓迎されないのでしょうか?実のところ「そういうもの」で終わらせてしまうことが多く、あまり深く考えたことはありませんでした。実際にCSSは誕生目的からして「そういうもの」なのですが、改めてその問題点をざっと考えてみました。HTMLが冗長になるclass指定が不要とはいえ、styleで書き込むので文字数の絶対量は増えてしまいます。スタイルの共通化ができないタグ毎にstyleを書き込むしか無いので、リストアイテムなどには大量に同じstyle属性を書き込まないといけません。HTML冗長化をさらに後押しします。CSS関連のエコシステム(Sass、PostCSS、Stylelintなど)が使えない最近のフロントエンド開発ではこれが相当のデメリットではないでしょうか。(もしかしたらインラインスタイルにもいい感じに変換やlintを当ててくれる仕組みがあるかもしれません。)レスポンシブ対応が難しい若干思いつきベースですが、インラインスタイルでレスポンシブなスタイルって作れるんでしょうか?やるなら下記のようになりそうですが、試そうと思ったこともなさすぎて効くのかどうかは未検証です。</ul>HTMLとCSSとコンポーネント時代は変わりつつあります。変わってしまっているのかもしれません。その変化の主役となる存在がコンポーネントです。HTMLとCSSは文章構造とスタイルという役割で分離するのが当たり前だったのに、コンポーネントという新たな区切がより重視されるようになってきています。従来のWebページ構成コンポーネント指向なWebページ構成その1コンポーネント指向がもたらす大きなメリットは、コンポーネントという明確な区切りが生まれることです。今までのHTML・CSS・JavaScriptという3層では、互いに分離されてはいるものの、その層内はほぼグローバル空間と化していました。良く言えば自由で手軽に作れてすぐに変更可能、悪く言えば分かりにくく壊れやすくて変化に弱い。コンポーネントという区切りは、この3層のより上位の区切りとしてWebページのグローバル空間を分割します。コンポーネント内でHTML・CSS・JavaScriptの3層が存在するようなイメージです。双方ともにメリットとデメリットは様々にありますが、今のフロントエンド界隈はコンポーネント指向が勢い付いていると見て間違いないでしょう。シンプルに考えると、Webページがコンポーネントによってまず区切られ、コンポーネント内部では従来の3層区切りがそのまま再現されているだけです。確かにコンポーネントが注目されだした当初はそうだったかもしれません。変化はそこで留まってはくれませんでした。コンポーネントという閉じた安全な世界が作られたことで、その内部ではかつて常識と考えられていた壁の存在感が薄れだしました。コンポーネント指向なWebページ構成その2コンポーネントとして完結していて、外部との連携で求められるインタフェースが満たされてさえいれば、コンポーネント内部の実装が多少暴れん坊になっていても大した問題にはなりません。いや問題ではあるんですが、コンポーネント内部はいわばprivateな部分なので大した問題ではないことがほとんどです。コンポーネントという区切りが明確に存在しているので、どうしようもなくなったら丸ごと取り替えてしまうことだって可能です。してコンポーネントの躍進により、HTMLとCSSの境界は薄れだしました。完全になくなったわけではありませんが、CSSが生まれた目的そのものであったHTMLとスタイルの分離は、コンポーネントというより大きな関心事にかき消されつつあります。コンポーネントと雑なセレクタコンポーネントという概念によってHTMLとCSSの間にあった従来の区切りは曖昧になり、両者の距離はぐっと近づいています。区分けされた両者を繋ぐために必須だったclassを軽視する、こんな手抜きスタイル指定も横行しているのではないでしょうか。}階層を指定しているとはいえ、セレクタとしてliを直で指定しています。従来のHTML・CSS分離の方針に従うなら真っ先にレビュー指摘が入りそうなコードです。実際に私もこんなコードを素の状況で見つけたらおそらく指摘を入れます。このコードがコンポーネントに関するものであれば話は別で、CSSにHTMLの構造が漏れ出していてもそれほど気にしません。1つはスタイルがコンポーネント内に閉じているからです。liという豪快なセレクタ指定がされていようと、そのセレクタがコンポーネント内のHTMLに限定されているのなら、コンポーネント外の予期せぬ箇所でスタイルが崩れることはありません。(セレクタのパフォーマンス観点については今回は立ち入りません。)もう1つは、classを丁寧に付けるほうがむしろコストが高くなるかもしれないからです。classを増やせば当然HTMLの文字数は増えて汚れます。適切なclass名を文書構造と翻訳サイトを往復しながら考えなくてはなりません。HTMLとCSS間の視点移動だって必要です。単発では小さな手間かもしれませんが、それを数百数千回と繰り返すのはとても面倒くさいのです。早く家に帰って走りたいんです。コンポーネントとインラインスタイルコンポーネントという箱庭の存在によって、雑なセレクタが許されるようになってきたとしましょう。エンジニアというのはどこまでも怠惰を求める生き物らしいです。一度雑になったセレクタ指定はどこまでも雑になっていきます。雑になればなるほどHTMLとCSSは近づいていきます。行き着く先に何があるかというと、インラインスタイルの再登場です。2</li></ul>コンポーネント指向という前提に立ち、改めて先に挙げたインラインスタイルのデメリットを再評価してみます。HTMLが冗長になる基本的には従来と同様だと思います。スタイルの共通化ができないコンポーネント単位での共通化という大きな道が開けます。利用するフレームワーク依存となりますが、v-forやjsxなどで効率的に記述する手段も多くあります。CSS関連のエコシステム(Sass、PostCSS、Stylelintなど)が使えない基本的には従来と同様だと思います。レスポンシブ対応が難しいコンポーネントごと表示を切り替えるなどの手段が生まれてきそうですが、ほぼ従来と同様と思われます。JSなどのJavaScript側からのアプローチによって解決は可能かもしれません。CSSばかり使っているのでこのあたりの動向には疎いです。)場面によってはインラインスタイルで済ませてしまうことも十分に可能ですが、大勢をそれだけでなんとかするのはやはり厳しい印象です。CSS系のエコシステムとの相性と、レスポンシブ対応のやりにくさは致命的です。インラインでベンダープレフィックスを付けていく作業以上の苦痛はなかなか見つからないと思います。人生は短いし早く帰って走りたいんです。CSSの話題にたどり着きました。CSSで書くとこうなります。2</li></ul>ご理解いただけたでしょうか。CSSの良さへと至る道筋です。CSSはスタイルとほぼ1対1に対応したclassを提供してくれます(一部複合的なスタイル付きclassがあったり、独自で拡張することも可能です)。実質class方式でスタイルを指定しているだけなので、autoprefixerやpurgecssなどとの連携が容易です。lg:)が用意されているので、レスポンシブ対応に苦慮する必要もありません。PCサイズ以上でだけ横並びにしたい場合はこう書けます。インラインスタイルの良さを残しつつ悪さを緩和する、絶妙な具合ではないでしょうか。2</li></ul>HTMLが冗長になるという点に関しては、インラインスタイルよりは改善されているものの、class形式でスタイルを当てていくときほどには軽量にならないです。スタイルとclassがほぼ1対1になっているので、この点は受け入れるしかないというのが現時点の評価です。CSSは決して強力なCSSフレームワークではありません。フレームワークと呼んで良いのか怪しいくらいに具体的なものは何も提供してくれません。公式サイトに掲げられている通り、utility-firstなツールを提供してくれるだけです。ツールをどう使うか、何を作るかは完全に使い手に委ねられています。そこに心地よさを感じかどうか個々人で評価が分かれるかもしれません。CSSを使い続けた私の感想としては、「次もまた使いたい」し「発展に貢献したい」です。その理由を端的に表現するのはとても難しいです。具体的にこれが良いと言い切れるようなものではなく、この記事で述べてきたように、Webフロントエンドの潮流の中で味わう様々な要因が組み合わさって到達した良さだからです。その良さを実感しているのは事実です。CSSの使い方自体はほとんど紹介できませんでしたが、最近は利用事例も増えて情報も多く出ているので探すのに困ることはないと思います。CSS利用事例として改めて記事化するかもしれません。CSSの巻き起こす追い風、みなさんも是非味わってみてください。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。Tumblrみなさんこんにちは!10月からメドピアにジョインしました、サーバサイドエンジニアの福本です。メドピアでは日常業務から離れ、業務改善や技術研鑽のための開発合宿を定期的に開催しております(ちなみに、前回の開発合宿の様子はこちら↓)。tech.medpeer.co.jp私は今回が初めてのメドピア開発合宿だったのですが、すごく楽しくて有意義な時間だったので、合宿の様子をみなさんにもお届けできればと思います。成果発表LTさいごに開発合宿のプログラム今回の開発合宿は、11月20日(水)~22日(金)の2泊3日で行われました!開発するテーマは自由なのですが、当日に内容に悩んで時間を無駄にしないように、事前に内容をエンジニアのメンバーに事前に決めてきてもらいます(前もって準備をしてきてもOK)。時間を掛けて取り組んだ内容なので、開発合宿でのアウトプットを各自が発表する場を設けているのですが、合宿中に発表まで行ってしまうと開発に使う時間が短くなったり、資料に掛けられる時間が少なくなってしまいます。そのため、成果発表は合宿が終わった次の週にオフィスで開催することにし、合宿中は思う存分開発に集中できるようにしました。待ちに待った開発合宿は、お昼過ぎからの開始!熱海駅に1名を除きエンジニアメンバー全員で集合し、会場まで向かいました。ちなみに、今回予約した宿泊所はこちらです↓www.airbnb.jpAirbnbで予約したのですが、地下1階から5階まで広く自由に使えますし、屋上で足湯に入りながら開発できたり、地下にはゲームやカラオケがあるなど、みんなで楽しみながら(後述)開発できるような環境になっています。館内のWi-Fiも速く、チーム開発も問題なく進められるのでオススメです。会場で全員集合して、各メンバーが取り組む開発テーマを改めて発表し、その後は各自で自由に開発を行います。足湯に入ってリラックスながら開発をするメンバー、机と椅子でバッチリ集中して開発をするメンバーと、色々な開発スタイルが見られて面白かったですね。夜は熱海...ということもあり、休憩がてら近くのスパや温泉を楽しみに行くメンバーも。ちなみに、夜ご飯は参加メンバー全員で近くのお店に食べに行きました。周辺に飲食店が充実しているというのも、熱海のいいところ。部屋に戻ってからは、各自で開発に戻りました。2日目2日目は移動がないので、朝起きてから夜遅くまで、ひたすら開発に集中できる貴重な一日でした(そのためブログに書くことも少ない....)!とはいえ気分転換も大事。朝食がてら朝日を拝みに行ったり、お昼ごはんは、みんなで海鮮を食べに行ったりしました。そんな2日目ですが、なんとRailsに送ったPRをマージされたメンバーがおりました...(すごい)。合宿で取り組んでいるメインのテーマではなかったようですが、目に見える形でわかりやすく成果が出るのは非常に嬉しいですね!画像にもURLありますが、マージされたPRは以下。よろしければご覧ください。楽しい開発合宿も最後ですが、合宿所の予約日時の都合上、3日目は場所を近くのコワーキングスペースに移して開発を進めました。場所は同じく熱海にありますnaedocoさん。naedoco.jpnaedocoさんは2016年にできたばかりのコワーキングスペースで、今までの開発場所とはまた違った雰囲気の中、リフレッシュして開発をすすめることができました。プログラム上は15時に解散、その後は各自自由に帰宅・開発という流れだったのですが、集中するあまりコワーキングスペースの開場時間ギリギリまで開発をするメンバーも居ました。そんなこんなで、3日間の開発合宿は終了。各メンバーがどこまで進捗したのかはお互いあまり知らない状態だったので、成果発表がすごく楽しみです。成果発表LT厳密には合宿...ではないのですが、土日のお休みを挟み、開発合宿の成果発表会を社内LTのような形で実施いたしました!合宿に参加できなかったエンジニアはもちろんですが、参加したエンジニアも合宿中には自分たちの開発内容に没頭していたこともあり、開発合宿の進捗や得た学びを改めて確認する場になりました。開発テーマの内容は本当に様々で、例えば人工知能系のチャットボットについてがっつり調査を進めてくれたメンバーも居れば...「心拍数を測り、PCのカメラから撮影した動画の顔に計測結果を写す」という、日常のWeb開発の業務ではなかなか触れることのない技術を使いこなしてアウトプットをしたメンバーも。参加したエンジニア全員が発表したところで、開発合宿は完全に終了!改めて振り返ってみると、単純に楽しかったというのはもちろんですが、参加したエンジニア全員がしっかりと開発テーマを進捗させて、アウトプットできる形まで持っていっている、という部分が印象に残る合宿でした。触れる技術をテーマにしていたエンジニアも多い中で、適度に楽しみながらも、集中すべきところはしっかりと集中してやり切る姿に、メドピアのエンジニアの底力を見たように思います。さいごに最後までお付き合い頂き、ありがとうございました!メドピアという会社が、事業も開発チームも大きくなっていく中で、多くのエンジニアメンバーが3日間も集中できる時間を頂けるということは決して当たり前のことではなく、会社全体の理解と協力があってのことで、非常にありがたいことだと改めて感じました。今後も開発合宿という文化を継続して開催していけるよう、事業に技術でしっかり貢献していきます。今回の合宿で培われた技術によってメドピアのサービスが改善されたり、あるいは新しい事業やサービスを産みだされることで、より多くの医師を支援し患者を救うことに繋がればと考えております。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。Tumblr涙の数だけ強くなるフロントエンドエンジニア村上(@pipopotamasu)です。熱海に行ってきたので、そこで作ったVue.jsのテストライブラリ「vue-function-tester」を紹介したいと思います。github.comatamivue-function-testerとはvue-function-testerは、Vue.jsの「メソッドの単体テストライブラリ」として作りました。watchをテストするためのライブラリです。started長々と解説するよりは実際のコードをみて行きましょう。テスト対象のコンポーネントとして、簡単な検索ボックスのサンプルコードを用意しました。jest依存ライブラリなのでjestのテストコードになります。余談ですがメドピアのフロントエンドはjestでバシバシテストコードが書かれています。});});上記のようにsearchメソッドに対するテストを書くことができます。hooksやcomputedのテストに関しては以下をご覧ください。https://github.com/pipopotamasu/vue-function-tester#computedvue-function-testerを作った背景ただなんとなく作りたかっただけです。深い理由はありません。(上記だとブログの長さ的に寂しくなりすぎるので、理由を無理やり捻り出しました。)vue-function-testerを使わなくてもVueコンポーネントのメソッドのテストを書くことは可能です。...vue-test-utilsを使用するメソッド内の処理を別の関数として切り出してそちらをテストするVueオブジェクトもしくはVueコンストラクタからメソッドの参照を取り出しテストするざっと考えついただけ3つの方法がありますが、どれも良い面もあればそうでない面もあり、個人的にときめくものではありませんでした。vue-test-utils皆さんご存知の通り、既存のVue.jsのテストライブラリとして「vue-test-utils」という有名なライブラリがあります。便利なライブラリで私ももちろん愛用させてもらっています。これは「Vueコンポーネント」のテストにフォーカスしたライブラリです。常日頃開発している身としては、「Vueコンポーネントのメソッド」にフォーカスしてテストを書きたい時にちょくちょく出くわします。その場合、vue-test-utilsだとテストコードが冗長になってしまうことがあります。hooks用の設定etc...等々、Vueコンポーネントのメソッドテストをときめく感じに書きたいという欲を満たすことができませんでした。VueオブジェクトもしくはVueコンストラクタからメソッドの参照を取り出しテストする上記のサンプルコードを例にすると以下のような感じになります。});});テスト自体は普通に書けるのですが、若干冗長さを感じてしまいます。上記のコードで冗長と感じるのは以下の2点です。thisのcontextを別変数で宣言->メソッド実行->context変数の検証と、3ステップ踏まないといけない。$emitのモック作成。methodsやcomputedのsetterのテストを書いているとそこそこの頻度で$emitが出てくるのに毎回モックするのが面倒。Vueコンポーネントのメソッドにフォーカスするという部分では問題ないのですが、上記の点でときめかない点が残りました。メソッド内の処理を別の関数として切り出してそちらをテストするそもそも別関数として切り出すというやり方です。関数のインターフェースをcontextベース(this)ではなく、引数・返り値ベースでinput/outputを実装すればとてもテストしやすい関数になります。}});簡単すぎる例ですが、上記のsum関数なら容易にテストできます。ロジックが長くなってくると、Vueコンポーネントのメソッドとして定義しておくとコンポーネント全体の視認性が悪化するので上記のような分割はとても良い手段です。そもそも切り出すのがめんどくさいですし、切り出した関数のテストが正でも対象のVueコンポーネントのメソッドが正常に動くとは限りません。こちらの手段も若干ときめかない部分が残りました。vue-function-testerのときめくポイントではどうすればときめくのか、それはコードを短く書くことではないかと自問自答し、以下のコードを短くするときめきポイントを実装しました。メソッドチェーン以下のようにメソッドチェーンで繋げることで、ワンライナーで検証できるようにしました。aliasさらにaliasを貼り文字数を削減。}).$emit).toBeCalledWith("search","test");終わりにこのようにメドピアの開発合宿では自作のライブラリを作ったり、他のOSSにPRを送ったり、使ったことのない技術を使って何かしらのサービスを作ってみたり等々、参加者それぞれが自由に課題を設定しコードを書きます(年2回、水木金の2泊3日)。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。今回は、Rubyアップデートを行った際にハマった箇所について紹介と解説をしてみたいと思います。類似の内容で発表もさせていただいておりますので、合わせてご一読いただければと思います。今回紹介するハマりどころは、ActiveSupport::DurationのバグとSidekiqの安全な再起動についてです。signalというエラーが発生し、CIのRSpecが途中で終了してしまう事象に遭遇しました。3並列目でメモリを食いつぶし移行のテストが失敗している調査を進めると、あるテストで異常な処理時間がかかっていることがわかりました。これによりCircleCIのメモリが枯渇し、残りのテストも軒並み失敗していることが判明しました。メモリを枯渇させるほどの異常な処理の原因も調査を進めていくと、Range#stepにActiveSupport::Durationを渡す箇所でパフォーマンスが著しく低下しており、こちらが原因であることを突き止めました。どの程度、処理速度に差があるのかを確認するため、以下のコードで検証します。2.6系を使うと圧倒的に処理速度が悪化していることがわかります。11.550944こちらの事象が発生する組み合わせは、下記の通りです。※issueも上がってました。🔍なぜ計算量が指数関数的に増加してしまったのかRange#stepとActiveSupport::Durationの挙動が怪しそうなので、検証していきます。下記のようなベンチマークコードを利用して、処理速度を計測していきましょう。time:#{time}"end計測結果は下記の通りです。2.6系ではステップ数が増加するごとに、著しく処理時間が増加していることがわかります。2.6からEnumerator::ArithmeticSequenceを返すように修正されました。2.5までは、Enumeratorを返してました。2.6の変更では、Enumerator::ArithmeticSequenceすなわち等差数列が返り値となることを期待しています。15))))のようなネストが深いデータ構造で渡ってしまう場合、再帰処理も相まって処理時間が指数関数的に増加していました。5.2系の組み合わせで、当該事象を回避するにはIntegerとして値を渡せば大丈夫です。Restartする必要が生じます。Restartしてしまうと、実行中のJobが正しく完了しない場合や2重に実行されてしまうなどの問題が起きる可能性があります。このため、Jobサーバーを安全に再起動する必要が生じました。Enterpriseを利用していますので、こちらの安全な再起動について解説します。以下の手順でSidekiqを安全に再起動させていきます。Jobを新規実行しないようにする実行中のJobが終わるまで待つSidekiqのプロセスを停止einhornのプロセスからSidekiqのプロセスを起動まず、現在実行中のJob数を確認します。sidekiqどうやら3つのJobが実行中のようですね。USR1)シグナルを送りkillします。12864Sidekiqのプロセスをkill後に、プロセスを確認するとstoppingとなり徐々に実行中のJobが減っていきます。sidekiqその後、すべてのJobが実行済みになることを確認します。sidekiqこれで、後続のJobが実行されない状態になりました。次に、TERMシグナルを送りプロセスをkillします。12864しばらくするとeinhornのプロセスが、自動的にSidekiqのプロセスを起動してくれます。これは、einhornのPIDを親プロセスに持つ子プロセス(Sidekiqのプロセス)がkillされると、einhornが新しいプロセスを立ち上げます。新しく起動したプロセスの様子を確認してみましょう。2.5が使われているようですね。einhornの自動再起動では新しいRubyを読み込んでくれないようです。こんな時は、Sidekiqを明示的に再起動してあげましょう。2.6を使って起動していることがわかります。これで、長時間Jobの実行を止めることなくSidekiqを安全に再起動することができました。当該手順を実行する前に、Jobのスケジュールを確認し再起動できそうな時間帯などを確認しておくと良いでしょう。これから前々回のRubyアップデートから約9ヶ月を経て、最新のRubyで「MedPeer」が動作するようになりました。「MedPeer」の開発メンバーも増えてきましたので、もう少し早い周期でRubyのアップデートができそうですね。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。SREの侘美(たくみ)です。普段はRails/Vue.js/terraform/Lambdaなどを書いています。趣味は飼い猫と遊ぶことで、生傷が絶えません。入社してから約半年間、Railsのプロジェクトで実装をしつつ、合間に開発環境の改善をいろいろとやってきました。けっこうな分量となったので、紹介したいと思います。本記事で扱う開発環境とは下記2つを指すこととします。主な改善対象である、「MedPeer」サービスの特徴をご紹介します。Macを利用コンテナ数は旧システム、認証システムを入れて32個ソースコードはdocker-syncを使ってローカルとコンテナ内で同期RSpecやrubocopの実行もコンテナ内で実行システム構成としては、ざっくり下図のようになります。私が入社した当時、MedPeerサービスの開発環境には下記のような課題がありました。立ち上げていると電池をモリモリ消費する状態でした。開発環境のコンテナ群を立ち上げっぱなしで、ミーティングに出ると、だいたい、2時間程度で電池が切れてしまうくらいでした。コマンドでプロセスのCPU使用率を確認したところ、SpringのプロセスがCPUを常に利用していることを特定できました。consoleやRSpecの実行等、ロードを伴うコマンドの実行を高速にしてくれます。MedPeerサービスではロード対象となるファイル数が多いためか、常時それなりのCPUを消費していました。Springには、DISABLE_SPRINGという環境変数を指定することで、無効にする機能があります。この環境変数を設定して一律で無効にすることもできるのですが、電池消費よりもスピードを優先するエンジニアも当然いますので、任意に設定できるように対応しました。medpeer.jpの開発環境にはdirenvが導入されているので、これを利用し、各エンジニアのローカルで指定した環境変数に応じて、コンテナ内にDISABLE_SPRINGを引き渡すように設定しています。起動が不安定対策メモリ消費が多いこともあり、開発環境を落とすことが多いのですが、起動しようとすると、Railsの起動や依存コンテナの起動に失敗することが多々ありました。原因は様々あったのですが、大半は下記のように起動順序によるものでした。gem、npmの依存ライブラリのインストール量で起動順序が変わっているDBやElasticsearch、fluentd等依存コンテナの起動より先にこれらに接続するコンテナが立ち上がり、エラーとなるもちろん、docker-composeによるlinksやdepends_onを指定し、コンテナの起動順序は担保しているのですが、これらではコンテナ内のプロセスがreadyになったことまでは担保してくれません。そこで、コンテナ内のプロセスが応答するまでwaitしてくれるufoscout/docker-compose-waitを利用することにしました。似たツールはいくつもあるのですが、複数のコンテナ/ポートへの疎通チェックができる点で、docker-compose-waitを採用しました。マウントし、環境変数と起動時のcommandを設定することで、指定したコンテナの指定したポートに疎通することをチェックしてから、任意のコマンドを実行することが可能です。サンプルのdocker-compose.ymlは以下のようになります。elasticsearchコンテナの設定この設定により、依存モジュールのインストール状況等で、各コンテナの起動スピードが多少変化しても、コンテナの起動順序を担保し、起動に失敗し辛い設定することができました。脱docker-syncdocker-syncが不安定であることもエンジニア内で問題視されていました。異常にCPUを消費するときがある(暴走状態!)ホスト-コンテナ間の同期が遅いときがあり、ソースコードの変更が反映されていないときがある(突然の死!)(上記の問題はdocker-syncが利用しているunison起因であることまでは確認しています)docker-syncはネイティブなDocker環境ではないMac等の環境において、ホスト-コンテナ間のファイルの参照を高速化されるために開発されたツールです。MacはVM上でLinuxを起動し、そのLinux上のDockerを利用する形なので、ホスト-コンテナ間のファイルの参照(同期)が遅いものとして有名です。volumeのcacheオプションを利用することで、docker-syncをやめて安定/高速なソースコード同期を実現しようとチャレンジしてみました。dockerでは、volumeマウントのdelegatedオプションを利用することで、ホスト-コンテナ間でのファイルの参照の遅延を許容し、高速な参照を実現することが可能です。./app:/app:delegated結論から言うと、脱docker-syncはできませんでした。開発環境を立ち上げ、複数のページの表示速度を計測した結果、いずれも約2.3倍程度表示スピードが劣化し、ページの表示に2~3秒かかるようになってしまいました。この数値はしばしば見かける「docker-syncで約2倍程度高速になる」という記述とも合致しており、確からしい結果となりました。Macで開発を行う場合、docker-syncが最も高速であるという知見を得ました。volumeのcacheオプションだけでも開発環境のページ描画はそこまで遅くならないので、安定性を重視してdocker-syncの利用は不要だと思います。docker-syncを利用していないプロジェクトにdelegatedオプションを導入したところ、jestの実行が数倍高速になるといった副次効果を得ることもできました。依存モジュールをコンテナ内に閉じ込めるこちらは、docker-syncを導入していないプロジェクトに導入した設定です。Macのvolumeマウントはとても遅いので、ホスト側のファイルを参照する量が増えるほど、コンテナ内でのRailsの挙動は遅くなります。上記の課題を解決するため、下記のような対策を採りました。現状、vendor/bundle配下のファイルがホスト側で必要になるシーンが特にないため、vendor/bundle以下のファイルはコンテナ内のみに存在するように構成を変更します。volumeを使い、コンテナのライフサイクルとは別に永続化しています。具体的なdocker-compose.ymlファイルは下記のようになります。{}図で示すと、下図のようになります。beforeafterこの構成により、コンテナ内から参照するホスト側のファイル数を減らし、Railsの動作を高速化することができます。Linux対応MedPeerサービスの一部のテスト環境は、Ubuntu上に構築した開発環境で動作しています。過去にdocker-sync導入後、Ubuntu上の環境は別ブランチで構築する構成となっていたため、メンテナンスされず放置される傾向となっていました。OSでも開発環境が構築できるように修正しました。具体的な方法としては、docker-composeのoverride機能を利用します。upコマンドで環境を立ち上げます。OS上では、環境変数にCOMPOSE_FILE=docker-compose.yml:linux.ymlを設定し、docker-compose.ymlに加え、linux.ymlを読み込み、設定を一部上書きします。upコマンドで環境を立ち上げます。mountの仕組みでソースコードをマウントするように設定します。OSでも環境が立ち上がるように構築されています。認証機能の有無の切り替えわれわれのプロジェクトの中には既存の認証サービスと連携するものがいくつかあります。開発環境でも本番環境と同等の認証の仕組みを動かそうとすると、認証に関係するコンテナだけで、13個ものコンテナを追加で起動する必要があります。これでは明らかにローカルマシンのリソース消費が増えてしまうため、開発環境では認証機能をダミーに変更し、これら13個のコンテナを削除したい気持ちになります。一方で認証機能の検証を行いたいシーンもあります。そこで、新しいいくつかのプロジェクトでは開発環境で認証機能の有無を切り替えられるようにし、認証機能無しで開発環境を立ち上げた場合は、余計なコンテナが起動しないように設定しています。こちらに関しても、docker-composeのoverride機能で実現しています。認証機能を利用しない構成でdocker-compose.ymlファイルを用意し、これに認証サービスを追加するためのauth.ymlファイルを用意します。これらのdocker-composeで利用するファイルは、USE_AUTH環境変数をdirenvを利用して設定し、Makefile中でCOMPOSE_FILE変数に設定することで制御しています。Rails内部の実装でも認証をダミーの実装に切り替える必要があるため、USE_AUTH環境変数をRailsのコンテナに渡し、実装が切り替わるようにしています。認証機能を利用しない構成の場合は、ベースとなるdocker-compose.ymlのみで構築されます。認証機能を利用する場合は、Makefileにて環境変数COMPOSE_FILE=docker-compose.yml:auth.ymlを設定することで、認証機能を追加します。COMPOSE_FILEに指定したことで、下記のauth.ymlでdocker-compose.ymlがoverrideされ、下の図のような構成でコンテナが立ち上がります。認証機能を利用する構成このような切り替え機構を導入することで、ほとんどの開発シーンでは認証機能を省略し、省リソースでの開発環境を実現することができました。「MedPeer」サービスの開発環境には、一部の機能の動作を検証すためのコンテナや、モバイル版含めてすべてのサービスが動作するように下記のコンテナも含まれています。デフォルトはmaximumな構成となっています。起動しているコンテナの中には、下記のようなものも含まれています。メールの内容を確認するためのmailcatcherコンテナS3へのファイルアップロードを検証するためのminioコンテナモバイル版等、一部の機能で利用するAPI用コンテナこういったコンテナはすべての開発者の環境で必要となるわけではないので、不要なコンテナを停止させることで、開発環境のリソース消費量を削減しておきたいです。一から構築する場合は、何度か紹介したdocker-composeのoverrideを利用し、デフォルトをminimumな構成とし、特定の開発時に必要なコンテナは別ファイルで定義し、環境変数で切り替えるのが良いでしょう。今回はすでにデフォルトがmaximumな構成となっているため、docker-composeのoverride機能を利用し、不要なコンテナは起動後即終了するように設定することで、コンテナの起動数を減らす方向としました。こちらも最小構成で十分な開発者は、direnvを利用し環境変数にCOMPOSE_FILEを設定することで、構成の切り替えをできるようにしています。disabled']個人ごとのカスタマイズを可能に上記を応用することで、自分のみのコンテナを追加したり、逆に特定のコンテナを起動しないようにするといったカスタマイズも可能になっています。docker-compose.custom.ymlのようなファイルを作成し、任意の設定を追加します。.git/info/excludeに設定しコミットから除外します。環境変数のCOMPOSE_FILEを設定し、作成したdocker-compose.custom.ymlを読み込むようにすることで切り替えを実現できます。まとめ今までの半年間で行ってきた開発環境の改善を振り返ってみました。単純なものからちょっとテクニカルなものまで、いろいろやったなあ、という感想です。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。Tumblrメドピアエンジニアの難波です。Journalというサービスのクローズを行いました。今回の記事ではその時に行った作業の紹介をしたいと思います。サービスの新規開発に関する記事というものは世の中にたくさんあれど、大規模なサービスにおける一部機能(サービス)の終了に関する経験や知見は中々オープンにされにくいものです。サービスを滞りなく素早く終了させることは新しいサービスを作るリソースの確保という観点でも大事なことであり、今回の記事が将来の参考になればと思い1つの事例としてここに認めます。Journal(以下Journal)とはPubMed(Wikipedia)という医学を中心とする生命科学の文献情報を収集したオンラインデータベースへの検索エンジンを利用して、MedPeer会員が世界中の論文に対して議論したりコメントしたりすることができるサービスです。2018年の夏にスタートしたのですが一年ほどの運用を経て検討した結果、様々な理由により2019年8月にサービスを終了することとなりました。事業的な観点による反省や改善点などは色々とあるものの今回はそれは置いておいて、本記事ではMedPeerのような内部で様々なサービスが運営されているWebサービスにおける、一部サービスの終了とそれに伴う開発的なフローについてまとめます。クローズの計画づくり一言でサービスを終了するといっても、サービスを終了するために必要な実装というものが存在します。提供しているのが単一のサービスでありそれを終了するなら極論サーバを落としたりドメインの向き先をS3に置いた「サービス終了のお知らせ」という一枚のHTMLにすることも可能ですが、JournalのようにMedPeerというサービスの1コンテンツとして運用しているものはそうもいきません。よってまずサービスの終了に必要な作業をまとめ、見積もりを行います。その見積もりを見て終了しない場合に毎月かかるコストやリスク、終了にかかる実装コスト、終了に伴うユーザへの影響などを総合的に鑑みて判断が行われます。この部分はサービスの性質によって内容が大きく異なる部分かと思いますが、Journalでは終了に必要な作業として大まかに以下のようなタスクを挙げました。必要なこと開発が必要なものとしてまずはユーザへの告知です。Journalのトップ画面にお知らせという形で表示しました。Journalでは一部の論文に関してお手伝いいただいている医師の先生に論文解説を書いていただいておりました。これらを含め他のサービスで活かせる情報も多く、具体的に必要な移行作業などを検討して方針を決めました。終了日に必要なこと終了日に最低限必要なことはユーザが正常にサービスにアクセスできなくなることです。そのためにまずRailsのroutes.rbから該当するルーティングを削除しました。一部URLについてはトップ画面へリダイレクトするという処理をNginxの設定ファイルに追記しました。ここで大変だったのは社内の別のページからJournalへリンクしているかどうかの調査です。MedPeer内の別ページからのリンク(ヘルプページなど)、更にはコーポレートサイトにJournalの紹介などのリンクがないかをチェックする必要もありました。基本的には社内の各サービス担当者にヒアリングを行い、各種リポジトリ内でgrepすることで抜け漏れを探す作業になります。終了後に必要なことユーザがサービスにアクセスできなくなってもソースコードはまだ残っており、それを消さなければRailsや各種gemfileのアップデート時の負債になり続けます。Journalにおいては上記で書いたように一部データについては移行を行う予定だったため、ViewとController、テストについてはほとんど全てを、Modelについては一部を残して削除しました。今回はRubyの世界で移行作業を行うことにしたためこういう方針にしましたが、別の方針としてデータベースの当該テーブルをダンプしてS3等に保存、その後新規に作成したテーブルにSQLでデータを流し込むといった方針もあったと思います。Cloudを使用しておりました。こちらについては他のサービスも使用しているため全面的にストップということにはなりませんでしたが、データの削除、インスタンスタイプの変更などを行いました。他サービスに移行して使用することになったデータを管理するための管理画面の作成などを行いました。実際に起きたヒヤリハットなのですが、大規模な機能改修が頻繁に行われているリポジトリでは様々なブランチで編集が行われるファイルがあります。該当するものでしょう。いう時に注意してレビューを行わないと、クローズ時に消したはずのコードが復活することがあります。Requestについても一通り確認しておきましょう。まとめ今回のJournalクローズでは終了後のトラブルといったこともあまりなく、滞りなく作業を行うことができました。サービスをクローズすることは残念なことですが、それによって生まれた開発的な正の変化としては以下のようなものがあります。終了しないに越したことはありませんが、終了するとなったら後顧の憂いを残さぬように予定を決めて速やかに削除しましょう。՞)☝︎是非読者になってくださいメドピアでは一緒に働く仲間を募集しています。月別アーカイブリンク医師専用サイト「MedPeer」エンジニア採用はこちら!スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。