シンクロ・フード エンジニアブログ

飲食店ドットコムを運営する、株式会社シンクロ・フードの技術ブログです

Kotlin Fest 2025 に参加しました

開発部モバイルアプリ開発チームの小関です。
昨年行われたKotlin Fest 2025に弊社のAndroidエンジニア2名で参加してきました。
本記事では、参加した小関と横山の2名による対談形式で、印象に残ったセッションや会場の様子をレポートします。

会場の様子

自己紹介

小関:シンクロ・フードに新卒で入社して8年目の小関です。普段はモバイルアプリチームのリーダーとして、飲食店と、飲食店で働きたい求職者をマッチングする「求人飲食店ドットコム」のモバイルアプリ開発を主に担当しています。AndroidやKotlin系のイベントに関しては今回が初参加になりました。よろしくお願いします。

横山:中途入社2年目の横山です。私も普段は主に「求人飲食店ドットコム」のAndroidアプリ開発を担当しています。Kotlinに触れ始めたのは入社後からなので歴はまだ浅いのですが、少しでも知見を得られればと思い、今回初めてKotlin Festに参加しました。よろしくお願いします。

セッション1:文字列操作の達人になる 〜 Kotlinの文字列の便利な世界 〜

speakerdeck.com

小関:Kotlinの文字列の内部構造の説明から始まり、10個ほどの知見が共有されました。初心者向けのメソッドからテクニック的なものまで幅広かったですが、横山さんはどこが印象的でしたか?
横山:紹介されていたものの多くは知っている内容でしたが、いくつか知らないものもあり勉強になりました。特に 正規表現の名前付きキャプチャーグループ の箇所ですね。現在開発しているアプリ内でも正規表現を使っている箇所が多々あるので、これを使ってリファクタリングすることで、より分かりやすいコードにしていきたいと感じました。
小関:今の我々のコードでは名前付きにしていない箇所が大半だと思いますので、つけたほうが可読性が上がりそうですね。

横山:もう一点、複数行の文字列に対してシンタックスハイライトを効かせる機能も参考になりました。IDE側の補完機能ですが、覚えておきたいですね。

小関:同じく9番目の正規表現における話でいえば、Multiline strings (""")のコラムとして紹介されていた、バックスラッシュによるエスケープを重ねなくて済むという話が気になりました。弊社の現状のコードを見ると一部では使われていますが、徹底はされていない状況なので、今後は積極的に使っていきたいですね。
横山:確かにそうですね。

小関:他に私が気になったのは、8番目に紹介されていた lines() メソッドですね。現在、アプリ内で改行コードをBRタグに変換して繋ぎ直す処理がありますが、現状のコードは正規表現で複数の改行コード(\n, \r, \r\n)を判定して変換するような書き方になっていました。lines() を使えばより簡潔に書けることが分かったので、リファクタリングの参考にしたいです。存在自体を知らなかったので、便利だなと思いました。

セッション2:内部実装から理解するCoroutines ー Continuation・Structured Concurrency・Dispatcher

speakerdeck.com

小関:続いてはCoroutineのセッションですね。内容について、横山さんから簡単に説明いただけますか。
横山:このセッションでは、Coroutineの中心的なコンセプトを仕組みから理解し、ソースコードを読めるようになることを目指して、Continuation、Dispatcher、Structured Concurrencyの3点について解説がありました。

横山:正直なところ、私はどれも聞き慣れない言葉が多く難しく感じたのですが、小関さんはどういった印象を持ちましたか?
小関:私もContinuationなどの詳細については改めて学ぶ部分が多かったです。このセッションではContinuation周りをデコンパイルしたコードを基に説明されていましたが、実際にJVM上で動作する際に中断ポイントの前後にラベルをつけて制御しているといった内部実装の話は、普段意識しないブラックボックスな部分だったので非常に興味深かったです。

横山:なるほど。Dispatcherに関してはどうでしょうか。
小関:Dispatcherに関しては、現状はwithContextを使って Dispatchers.IOに切り替えるコードなどを書いていますが、Dispatchers.Defaultとの処理の差までは意識できていませんでした。今回の図解による解説を通して、各Dispatcherがどのようにスレッドを扱っているのかイメージが持てたという点でためになりました。

セッション3:AIとの協業で実現!レガシーコードをKotlinらしく生まれ変わらせる実践ガイド

speakerdeck.com

小関:Javaが多く残っているプロジェクトをKotlinにリプレイスする際、AIをどのように活用するかという知見が共有されたセッションでしたね。「Kotlinらしさ」をどう定義し、AIに表現してもらうかという言語化の部分が印象的でした。
横山:「求人飲食店ドットコム」のアプリも、Kotlinで書かれてはいるものの古い書き方が残っている箇所があるので、スライドにあったプロンプトの工夫などを参考に改善していけるのではないかと思って聞いていました。

小関:横山さんは普段、どのように生成AIを使っていますか?
横山:変数名や命名規則の候補出しという単純なものから、リファクタリングの例を挙げてもらうといった使い方もしています。Android StudioのGitHub Copilotプラグインもそうですが、弊社の全社員が利用しているブラウザ版のGeminiを会話形式で使うことも多いですね。
小関:なるほど。セッションの中では、何もコンテキストを渡さないとAIがJavaの文法に近いKotlinや古い記述を出力してしまうという課題が挙げられていました。あらかじめ「モダンなKotlinとは何か」をインストラクションとして渡しておく手法は参考にできそうだと感じました。例えば、セッション1で話題に上がった lines() のようなメソッドも、「改行コードの分割には正規表現ではなく lines() を優先して使うこと」といった具体的なルールをカスタムインストラクションに含めておけば、より精度の高い「Kotlinらしい」コードを提案してくれそうですよね。
横山:そうですね。チームで推奨する書き方をあらかじめプロンプトに入れておくだけでも、リプレイスの質がかなり変わりそうです。

小関:あとはAIの出力を鵜呑みにせず、テストを書いて検証するという点も強調されていましたね。これからAIにコードを書かせる場面が増えていく中で、すべてを人間が目検で確認するのは大変ですよね。テストコード自体もAIに書かせつつ、テストを増やしていくことでデグレを回避し、品質管理を徹底していきたいと感じました。
横山:品質管理といえば、最近はコードレビューの際にもAIの使い方が変わってきましたよね。以前なら、レビュー中に知らない書き方が出てきたら、まず公式ドキュメントや技術記事を漁って、この書き方に特有のハマりどころがないかを一から調べていました。
小関:確かにそうですね。今はまずAIにコードの意図や、考えられるリスクを聞いて、ある程度の「当たり」をつけてから、最終的な裏付けと自分自身の理解のために一次ソースを読みに行く、というフローに変わりました。
横山:はい。AIに事前確認を挟むことで、ゼロから検索するよりも格段にスピードが上がりました。その分、浮いた時間でより本質的なロジックの検討に集中できるようになったと感じます。

全体を通して

小関:セッション以外にもイベント全体の印象はどうでしたか?
横山:弊社ではKotlinはAndroidアプリ開発でのみ使用しているので見えていなかったところですが、他社ではサーバーサイドでもKotlinが積極的に活用されているんだなというのが全体的な印象として強かったです。また、企業ブースごとにスタンプラリーがあったり、スタッフの方たちが気さくに話しかけてくれたりして、各社のブースを回って楽しむことができました。実際にスタッフの方たちから、現場での開発手法や裏話なども聞けて非常に良かったです。
小関:オフラインイベントならではですよね。スポンサーブースで各社がどのようなアーキテクチャで開発しているかを図で示してくれているブースが多く、非常に参考になりました。バックエンドもKotlinで書くことで、1人のエンジニアがAPIとアプリの両方を見ているような事例もあり、同じ言語で書かれている利便性を感じましたね。

横山:ノベルティもたくさんいただけました。
小関:入場時にもらったロゴ入りのミニトートはデザインも良く、普段使いできそうで嬉しかったですね(笑)。

小関:最後に、今回初めて技術カンファレンスに参加してみていかがでしたか。
横山:他社のエンジニアの方々とお話しする機会はあまりなかったので、スポンサーブース以外にも懇親会で他社のAndroidエンジニアの方々と交流できたことは貴重な体験になりました。自分の知識不足を再認識できたことで、学習に対するモチベーションが非常に高まりました。
小関:私もAndroidやKotlinのイベントに参加するのは初めてでしたが、サーバーサイドでの広まりなど、実際に会場に足を運んで肌で感じられることが多かったです。他社さんも我々と近い状況で、これからJetpack Composeを導入していくというフェーズの会社もあり、少し安心した反面、よりモダンな開発環境へのアップデートを加速させていきたいと強く感じました。
横山:はい、頑張りたいです。


会場の様子

以上、Kotlin Fest 2025のレポートでした。

今回のカンファレンスを通じて、最新の技術動向だけでなく、Kotlinエンジニアの方々の熱量に触れ、良い刺激をいただくことができました。 登壇者の方をはじめ、この素晴らしいイベントの運営・開催に携わったすべての皆様に感謝申し上げます。

今回得た知見や刺激を日々のプロダクト開発に還元し、価値あるサービスを提供していけるよう、チーム一丸となって取り組んでいきたいと思います。 最後までお読みいただき、ありがとうございました。

iOSDC Japan 2025 参加レポ Day2

こんにちは、開発部モバイルアプリチームの小関です。

Day1の佐々木の記事に続き、Day2の参加レポートをお届けします。
※Day1の記事はこちら: tech.synchro-food.co.jp

私からは、Day2に参加したトークセッションの中から、特に印象に残った2つについてレポートします。

トークセッション紹介

1. 5000万ダウンロードを超える漫画サービスを支えるログ基盤の設計開発の全て

speakerdeck.com

アクティブユーザーも多いであろうこの規模感のサービスだからこそ、厳しいパフォーマンス要件を満たす必要があったログ基盤を、ゼロから自作したというセッション内容でした。

特に興味深かったのは、Remote Configによる段階的移行の戦略に関してでした。
これほど大規模な変更を強制アップデートで対応するのはリスクが高いため、Firebase Remote Configを活用して段階的に移行する手法が紹介されていました。

具体的には、移行後の新仕様においては「コード上のデフォルト値はON(最終的な状態)」にしておき、「Remote ConfigでOFF」を配信してスタートする運用です。
弊社のアプリでもRemote Configを使ったA/Bテストなどは行っていますが、これまでは0か100かの切り替えにしか使っていませんでした。
また、設定の同期に失敗した場合などを懸念して、つい「変更前」をコードのデフォルト値にするパターンが多く、強制アップデートなどに伴ってフラグの削除対応などを行っていました。
しかし、最終的にフラグを削除した後のコードの状態を考えると、この手法のほうが理に適っており適切だと感じました。

2. 止められない医療アプリ、そっと Swift 6 へ

speakerdeck.com

医療という信頼性の求められるアプリ開発において、どのように安全にSwift 6へ移行していくかというセッションでした。

このセッションでは、移行の具体的な話に入る前段として、「何がなんでも移行するというスタンスではなく、本質的に品質を高めるために対応をしたい」というチームでの話し合いについて触れられていました。
このスタンスは、一見当たり前にも思えるものの、一エンジニアとして深く共感したものでした。

技術的な話としても、Swift 6系への移行がまだ進められていない弊社にとっては、実践的な移行方針やモジュールの移行順序といった知見は大変参考になりました。
特に、Swift 6.2で導入される「Approachable Concurrency」についても言及されていましたが、ここは我々も注目しているトピックであるため、今後さらに多くの他社事例が出てくることを期待しているところではあります。

また、移行の過程で活用したAIツールの事例が紹介されていました。
今回のiOSDC全体を通して、他のセッションやブースでの会話においてAIツールの話題が非常に盛んだったのも印象的でした。
メドレーさんでは「Claude Code」をメインにしつつ、タスクに応じて他のツールや他サービスのMCPを用いた連携にも取り組まれているとのことでした。
弊社ではiOSアプリのコーディングにおいて「GitHub Copilot for Xcode」をメインに使っていますが、まだ活用しきれていないとも感じています。
社内の他チームにて検証しているAIツールがあるため、それらを積極的に取り入れていくことも検討していきたいです。

まとめ

トークセッションの締めくくりに行われるLT大会も、写真のように恒例のペンライト演出と相まって大いに盛り上がりました。
これまでは毎年オンラインで眺めているだけだったため、初のオフライン参加で実際にペンライトを振ることができたのは感慨深かったです。

私自身、技術カンファレンスのオフライン参加は久々でしたが、会場の雰囲気と一体となりながら聞くトークや、ブースの交流におけるあの独特な温度感は改めて良いものだなと感じました。
そこには「受け手」的な立ち位置の人がおらず、知見を共有し合うというエンジニア同士の対等な関係性でイベントが成り立っていて、そんな特殊な空間だからこそ得られる刺激があり、定期的にこの空気を味わうべきだと改めて感じました。

そして、今回得られた知見や刺激を、さっそく普段の業務に活かしていきたいです。

Kaigi on Rails 2025に参加しました

はじめに

アプリケーション基盤チームの深野です。普段はRailsアプリケーションのRuby/Railsバージョンアップ対応や開発環境整備を担当しています。今年も社内制度(業務時間内の勉強会・技術カンファレンス参加可)を利用し、昨年に続いてKaigi on Railsに参加しました。

Kaigi on Railsとは

Kaigi on Railsは「初学者から上級者までが楽しめるWeb系技術カンファレンス」をコンセプトとするイベントです。コロナ禍でオンライン限定として始まり、その後はオフラインでも開催されるハイブリッド形式へ発展しました。現在もチケットがあればオンライン参加できる点が継続されています。扱われるトピックが幅広く、日常的にRailsを主軸としていないWebエンジニアでも多くの学びを得られるのが特徴です。発表後に公開スライドがはてなブックマークなどで話題になることも多く、自分が会場でリアルタイムに聞いた発表が話題になっていると少し嬉しい気持ちになります。(今年は「技術負債にならない・間違えない」権限管理の設計と実装Sidekiq その前に:Webアプリケーションにおける非同期ジョブ設計原則が特に話題だった印象です)

今年の会場は東京駅から徒歩数分のJPタワー内「JPホール&カンファレンス」でした。

JPタワー
(会場となっていたJPタワー。外観・内装ともに洗練されていました)

印象に残ったセッション

ここからは特に印象に残ったセッションを公開スライドと併せて紹介します。(発表直後にスライドを公開してくださる登壇者が多く大変助かりました)

Railsアプリケーション開発者のためのブックガイド

speakerdeck.com

密度の高い発表でした。まずは冒頭でWeb検索や生成AIの発達した時代にあえて技術書籍を読む効能として「メンバーが入れ替わっても揺らがない<文化>を作るのに貢献できること」が語られていました。ポイントは次の2点です。

  • 本はその場で文脈に応じて内容が生成されるAIとは異なり、全員が同一内容を共有できる
  • 途中参加メンバーも社内限定ドキュメントより既読である可能性が高く、キャッチアップしやすい

弊社の一部チームでも最近はチーム全員で参加する読書会を行っており、同じ書籍を読むことで議論が一段深くなる実感があるためこの内容には強く共感しました。

その後は登壇者である高橋さん(日本Rubyの会代表)による推薦技術書の紹介が続き、数えてみると約87冊に及びました。各書籍には魅力的なコメントが添えられていて、終了後にはスライドを見直して気になる本を探していました。
弊社シンクロ・フードでは年間30000円まで技術書を購入するための料金を負担してくれる制度があるため、そういった意味でもピッタリの発表でした。

あなたのWebサービスはAIに自動テストしてもらえる?アクセシビリティツリーで読み解く、AIの「視点」

speakerdeck.com

Browser UseやPlaywright MCPなどのブラウザ操作系AIツールがWebページを操作するにあたって、Accessibility Tree(AX Tree)を用いていることを検証した上で、WebページのそのAI可読性を高めるには何を整備すべきかが整理されていました。さらに登壇者が開発する自然言語ベースのRuby製テストツールにAX Tree対応を導入したところ処理速度や精度が向上したという内容も紹介されており、説得力がありました。

AIにとって“読みやすい”Webページを作ることはE2Eテスト自動化だけでなく、将来的に“SEOならぬAIO(仮称)”といった評価軸にも関わってくる可能性があると思います。現状どのような仕組みでAIがページ構造を解釈しようとしているかを把握できた点や、AX Treeの可読性を下げるアンチパターンを知れたことは大きな学びでした。

Introducing ReActionView: A new ActionView-Compatible ERB Engine

speakerdeck.com

英語での発表でしたが、非英語話者にも配慮されたスライド構成や発表内容となっていて十分理解することができました。中でもデモは非常に印象的で、Webページ上の要素をクリックするとその場で対応するERBコードを表示する機能などが紹介されてとても盛り上がっていました。Reactなどのフロントエンド系のツールではこのような機能は存在しますが、RailsのActionViewでこれを実現している点は意外性があり驚きました。

最後に掲げられた“The community has built incredible tooling for Ruby itself. Now it's time to bring that same care to the view layer.”というメッセージも、発表全体を象徴しており印象的でした。

セッション以外で印象に残った体験

ここではスポンサーブースについて触れます。Kaigi on Railsでは複数のスポンサー企業がブースを出展しており、実際にその企業で働いているエンジニア・人事の方と直接会話できます。各ブースはノベルティや独自企画を用意していて、文化祭のような活気がありました。
中でも、個人的には参加者がクイズやアンケートに回答する形式のブースが特に好みです。イベント参加の目的は魅力的なセッションを生で聞くことに加えて、普段接点のない他社の取り組みを知ることにもあると思います。アンケートやクイズはその自然なきっかけになるためです。

中でも印象的だったのがMedPeerさんの「技術選定クイズ」です。
Medpeerさんのテックブログで、実際に会場で出題されていたクイズの画像が載っています。
ActiveJobのアダプタやN+1検知などの、Railsで開発する上では避けて通れない要素について、実際にどのgemを採用したかを当てる形式でした。回答後に、どの観点を比較してそのgemを選定したのかを具体例とともにエンジニアの方に伺うことができ、非常に参考になりました。

おわりに

昨年に続き今年も参加した感想でした。多くの刺激と学びを得られたため、来年も機会があれば参加したいと考えています。

iOSDC Japan 2025 参加レポ Day1

こんにちは、開発部モバイルアプリチームの佐々木です。

2025年9月19日(金)から21日(日)の3日間にかけて開催された、iOS開発者向けカンファレンス「iOSDC Japan 2025」に参加しました!

Day0はオンライン参加で、Day1とDay2はiOSエンジニアの小関と二人でオフライン参加しました。
私自身は過去二回はオンラインのみで参加しており、オフラインは今回が初めてです。

今回は各日で担当を分けて記事を書くことになったので、本記事では私からDay1の現地レポートをお届けします。
初めてのオフライン参加で体感した会場の熱気や雰囲気、そして参加したセッションからいくつかピックアップして感想をご紹介します。
Day2については別途記事が公開されるので、そちらも合わせてご覧いただけると嬉しいです。

イベント全体の雰囲気

iOSDCでは各セッションが行われる部屋以外に、各スポンサー企業のブースが並ぶエリアや、iOSDCから提供された飲食物が並ぶエリアなどがあり、会場全体が明るく賑やかでした。

技術カンファレンスというと少し厳かなイメージがあったので、現地に着いていきなり目にしたドーナツを受け取るための列には驚きました。(運営の方が朝一で遠くのミスドまで買いに行ってくださったらしいです)

iOSDCは今年で10回目の開催ということで、過去のロゴにちなんだお菓子や、10周年記念パーカーが当たるくじ引きなど、まさにお祭りのような雰囲気でした。

会場で配布される名札はビンゴシートになっていて、各ブースでスタンプをもらいつつ話ができるという、参加者同士の会話を促すような仕組みとなっていました。
運営の方がオープニングでもおっしゃっていた「コミュニケーションを大切にするイベント」という印象を強く受けました。

セッション紹介

そろそろ FormatStyle

speakerdeck.com

Swift 5.5で導入された「FormatStyle」について、従来のFormatterとの比較しつつ、どのように移行をすればよいのか、どのような種類があるのかが網羅的に紹介されていました。

セッション内で触れられていたようなFormatterのデメリットは私自身も感じたことがあり、特にDate→Stringのフォーマットは非直感的で、求める結果を得るために必要以上のコードを書かないといけない印象でした。また、Unicode仕様に沿って "yyyy年MM月dd日" のように文字列で指定しなければいけない点も、やや煩雑に感じる部分でした。

FormatStyleではそれらが解決されており、なおかつFormatterよりも柔軟性があるということで、アプリチーム内でセッションのスライドを見つつ、この知見を共有しました。
新機能についてのキャッチアップ不足を実感すると共に、日常業務にも役立つ内容で参加した意義を感じたセッションでした。

ユーザー数10万人規模のアプリで挑んだトップ画面のUI刷新

speakerdeck.com

WebView中心のGaroonのアプリをUX改善のためにネイティブ化した話でした。課題整理からデザイン案の採用理由、旧UIから新UIへの移行方法まで紹介されました。

このセッションで一番印象的だったのは新UIへ移行する際の丁寧さです。アプリのUI変更はABテストか急に切り替わるかのどちらかというイメージでしたが、Garoonアプリではまず社内ユーザーからのフィードバックを集め、ユーザー自身で新旧UIを切り替えられる期間を設け、アンケートで意見を集めて問題の修正や要望の対応を行い、満を持して新UIのみのバージョンをリリースしていました。

法人向けサービスというのもあるのかと思いますが、非常にユーザーへ配慮した移行方法だなととても驚きました。
また、普段ストア以外からユーザーの感想を知ることがあまりない私にとって、社内外のユーザーからたくさんフィードバックを集められたという話は特に印象的でした。

カスタムUIを作る覚悟

speakerdeck.com

iOS標準に寄らない独自デザインを実現する際、標準UIの再現や使いやすさを損なわずにカスタムUIを導入することの難しさが紹介されました。標準のUIは幅広いユーザーが使いやすいように作られており、それを見た目のために軽率に損なうことの是非を問うようなセッションでした。

普段開発しているアプリでもカスタムUIを使用していますが、開発時にそういった判断ができていなかったことを自覚して反省しました。
標準UIについてそこまで細かく動きを見ることもあまりなかったので、標準搭載されているアプリにもっと目を向けて、様々な使い方をしてUXについて理解することの重要性を感じました。

iOSエンジニアキャリア設計入門 〜”先進性”をキャリアの武器へ〜

speakerdeck.com

転職ドラフトさんによるスポンサーセッションで、エンジニアの転職に関する話が中心でしたが、ここ数年でFlutterの需要が高まっているというデータが興味深かったです。

つい最近、弊社初のFlutterアプリである「グルメバイトちゃん」をリリースしました。私もプロジェクトに参加していたのですが、それまでiOSだけしか経験のない私としては違いを感じる場面が多く、苦労したのは記憶に新しいです。

Flutterを採用する企業が増えているという印象はありましたが、それが転職市場においてもデータに表れていて、技術動向としても参考になるセッションでした。

イベントに参加してみて

今回初めてオフラインで参加してみて、改めて現地ならではの刺激をたくさん受けました。次回はもっと積極的にブースやスピーカーの方々と話してみたいなと思いました。

業務に直接関わるテーマは多くなかったものの、その分視野を広げるきっかけになりました。 理解が追いつかない内容もありましたが、各セッションでは自作アプリや実装例を交えて説明してくださったため、難しいテーマでもイメージを持って聞くことができました。

「ルーキーズLT大会」というiOSDCで初めてトークする方々によるセッションは、一人五分なのでどの発表も聞きやすく、内容のバリエーションも豊かで楽しめました。

イベントを通して幅広い話を聞いたことで、「こんなアプリを作ってみたい」「この技術を使ってみよう」と、開発へのモチベーションが高まりました。

S3とLambdaでEXIF 情報除去機能を作成する

SREチームの下野です。

以前こちらの記事で紹介したリアルタイム画像リサイズAPI に対し、 その前段で EXIF情報除去(および画像の回転補正)を行う仕組みを追加する対応を行いました。
今回はその対応や検討の経緯を紹介させていただきたいと思います。 tech.synchro-food.co.jp

前提

リアルタイム画像リサイズAPIとは

以下のようにCloudFront 、API Gateway、Lambda、S3(Public Bucket)を組み合わせて動的に画像をリサイズするAPIです。
弊社の各サービス共通で画像のリサイズに使用しています。

リアルタイム画像リサイズAPIの処理内容は以下の通りです。

  1. ユーザーがCloudFrontにリクエストを送ります。初回リクエストはCloudFront上にキャッシュが無いため、CloudFrontはオリジンとして設定しているS3のResizedBucketにリクエストを流します
  2. CloudFrontから流れてきたリクエストを受け、S3はBucket内を探しますが、Resizeされた画像が存在しません
  3. 画像が存在しない場合、307レスポンスが投げられます
  4. CloudFrontはS3からの307のレスポンスをそのままユーザーへ戻します
  5. ユーザーは307レスポンスを受け、次は画像変換APIのURLを待ち受ける、APIGatewayにリクエストを投げます
  6. API GatewayよりLambdaの画像変換関数がキックされます
  7. LambdaがS3のOriginalBucketより、画像リサイズ元となる画像を取得します
  8. 7で取得した画像をパラメータに従ってリサイズし、ResizedBucketに保存します
  9. 8で保存した画像へのURLへの301レスポンスをLambdaにて返します
  10. 301レスポンスをそのままユーザーに返します
  11. ユーザーは301レスポンスに従い、もう一度リサイズ画像のリクエストを送ります
  12. CloudFrontからResizedBucketにリクエストが流れ、変換後画像をレスポンスとしてキャッシュしつつ、結果をユーザに返す

詳細はこちらの記事をご参照ください。

tech.synchro-food.co.jp

EXIFとは

EXIF(EXchangeable Image File Format)とは、デジタルカメラやスマートフォン等が JPEG や TIFF(一部 HEIF 等を含む)に埋め込むメタデータ規格です。
撮影日時や、端末・カメラの機種、 位置情報、画像の向き情報などを保持しています。

EXIF情報を保持した状態で画像を公開すると、悪意のある利用者によって写真を撮影した際の位置情報等から個人情報の特定につながる可能性があり、削除しておいたほうが安全です。
また、EXIFは画像の向き情報を保持しており、EXIF情報を除去すると画像の正しい向きがわからなくなる場合があるため、EXIF情報を除去する際は除去前に画像の向きの補正をする必要があります。

背景

これまでリアルタイム画像リサイズAPIの構成では、使用しているS3がPublic Bucketのため アップロードされた画像がEXIF情報を保持している場合、EXIF情報ごと画像を公開してしまう可能性がありました。
そのため、リアルタイム画像リサイズAPIを使用する前に、アプリケーション側でEXIF情報を除去して画像をアップロードしていました。

しかしアプリケーション 経由のアップロードでは、 画像が大きい場合に時間がかかることや、EXIF除去の処理がアプリケーション側の性能劣化に繋がる等の課題がありました。

これらの課題に対応するため、リアルタイム画像リサイズAPIを利用するアプリでは、ユーザーから直接 S3 へアップロードするような対応を検討していましたが、その場合これまでのようにEXIF情報をアプリケーション で削除できなくなる問題がありました。

リアルタイム画像リサイズAPIは各サービス共通で利用されているため、EXIF情報除去についても同じように利用できた方が良いため、機能を追加することになりました。

対応内容

構成の検討

方針

チーム内で構成を検討したところ、リアルタイム画像リサイズAPI へ以下のような仕組みを追加する方針になりました。

追加する仕組みの処理内容

  1. クライアントからプライベートS3へ画像をアップロードする
  2. S3からLambdaへアップロードイベントを通知する
  3. Lambdaが Private Bucketから画像をダウンロードする
  4. 画像の向き補正・EXIF情報除去を実行する
  5. 処理済みの画像をリアルタイム画像リサイズAPIの Original image bucketへ保存する

構成検討の経緯

以下の理由で既存のバケットを Public から Private に変えられないため、EXIF除去前の画像を公開していない場所で受け取る必要があり、ユーザーからの画像アップロードを受け取るPrivate Bucketを追加する方針になりました。
なお、Private Bucket にアップロードされたEXIF情報がある画像はS3のライフサイクルポリシーによって削除しています。

  • Original image bucket
    • EXIF情報が残っている画像ファイルが公開されないようにする対応であるため、単にリアルタイム画像リサイズAPI のOriginal image bucket をPrivate Bucketにできないかを考えましたが、Original image bucket内に公開している画像が存在したためできませんでした。
  • Resized image bucket
    • リアルタイム画像リサイズAPIは、リサイズ後の画像を保存するバケットでリサイズされた画像がない場合にリダイレクト処理を実行 しています。リダイレクト処理は、S3の 静的ウェブサイトホスティング機能 とリダイレクト機能を使用しており、Privateに変更するとリダイレクトが動作しなくなり、大規模な改修が必要になると想定されたため、実現したいことに対して影響が大きくこちらの設定変更での対応は難しいと判断しました。

EXIF除去処理の実装

EXIF除去を行う処理は、Pythonで実装しました。
画像の向き補正、EXIF削除にはWand(ImageMagickのPythonバインディング) を使っています。
Wandにした理由は、社内でImageMagickの利用実績が多かったためです。

以下の通り処理自体は比較的にシンプルなため、それほど苦労せずにコードを作成できました。

  1. Private Bucketから画像をダウンロード
  2. 画像の向き補正、EXIF情報除去
  3. 処理済みの画像をOriginal image bucketへ保存

しかし、性能面の調整に苦労をしました。
前提として、リアルタイム画像リサイズAPI の前段に処理を追加する構成であるため、高速に処理する必要がありました。

初めはコスト最適化を意識して Lambdaに搭載するメモリは 1~2GB(1792MB/2vCPU)で開始しましたが、以下の課題があることがわかりました。

  1. 画像の最大サイズと想定している20MB の画像でメモリが足りないことにより Runtime Exitエラーが起きる
  2. 画像のサイズを大きくしていくとImageMagick の処理に時間を要し、処理時間が 3 秒以上かかる

性能面の課題への対応 1

まずは、課題の1点目の対応をしました。
処理が正常終了しないと本番運用できないため、まずはRuntime Exitエラーが起きないようにLambdaのメモリを4096MB(3vCPU) に増やしました。
メモリを4096MB(3vCPU) に増やしたところエラーは起きなくなり処理性能も一定の向上があったものの、画像のサイズによっては処理時間が 3 秒を越えやすい状況でした。

性能面の課題への対応 2

2点目の課題への対応です。
最初は、1点目の課題への対応と同じく Lambdaの vCPU/メモリ の量を増やすことで改善が図れないかと考えました。
Lambdaの割当メモリを増やすことでの懸念はコストですが、Lambdaは実行時間に対してコストが発生するため、処理性能が良くなるのであればコストは大きくは増えないと考え、かける労力が少なく対応出来ると良いかと思ったためです。
効果があるかを確認するため、一旦 Lambdaの割当メモリを 4096MB -> 8192MB(5vCPU)にして試しましたが、処理時間にはごくわずかな差 しかありませんでした。

次に、ディスクIOが原因ではと考えました。
S3から取得した画像やEXIF情報を除去した画像は、Lambdaの一時ディスク上に保存していたためです。
Lambdaのメモリ上で、EXIF除去・画像の向きを補正するよう修正して試しましたが、こちらもわずかな差しかありませんでした。
性能が上がらなかったことの調査のため実行ログを見ると、ディスク IO が原因ではないことがわかりました。画像の向き補正に時間がかかっていたようです。画像サイズが約 10MB を超えると向き補正に要する時間が増加しており、この処理は Lambda の割当リソースを増やしても改善されないものでした。

その後もImageMagickの依存ライブラリの変更などを試したのですが、あまり効果はなく画像のサイズによっては処理時間がかかるケースがありました。
こちらについては、アプリ側でユーザー向けにアップロード中であることを画面に表示すること、過去の傾向的にサイズの大きい画像はアップロードされるケースが少なかったことから、今回は受容する方針になりました。

振り返り

今回の対応では、無事EXIF情報除去機能を追加することができました。既存の画像リサイズAPIの機能・運用を維持したまま、当初の課題のアプリケーション側での処理を回避するという目的を達成できた点は良かったと思います。

実装面では、Lambdaのメモリ増強や処理フローの見直しなど、性能面の課題にも対応しました。通常サイズの画像では十分な性能を確保できた一方で、大容量の画像ファイルに対しては処理時間が長くなるという課題が残りました。発生頻度や運用上の影響を考慮し、現状では許容することとし、将来的に改善していければと思っています。
性能面の課題への対応では、先にもっと深く調査をしておくべきだったというような反省がありますので次回の対応時に活かせるようにしたいと思います。

まとめ

本記事では、リアルタイム画像リサイズAPI に対し、前段で EXIF情報除去(および画像の回転補正)を自動的に行う仕組みを追加する対応をした経緯をご紹介しました。

この記事がどなたかのお役に立てれば幸いです。

複雑化していた Java の環境構築を Dev Container 化して Eclipse から移行しました

こんにちは、アプリケーション基盤チームの坂本です。
今回は Java の開発環境を Dev Container 化した対応について紹介します。

弊社の開発環境について

弊社では「飲食店ドットコム」をはじめとした飲食店向けのサービスを多数運用しています。
それらのサービスは元々全て Java で書かれていましたが、数年前から段階的に Rails への移植を行っています。
主要なサービスのいくつかはすでに移植が完了していますが、一部のサービスはまだ移植の途中であり、Java で実装された機能と Rails で実装された機能が混在しています。
また、社内システムの中には Rails 移植の工数をかけない判断が下されたものもあります。

このような状況のため、開発者は Java と Rails の両方の環境構築をして業務を行っています。
Rails の環境構築は docker compose を使って比較的簡潔な手順で構築できます。
一方で、Java の環境構築は以下のような問題が発生していました。

  • ローカルに Java、Eclipse、Tomcat などを入れて、手順書を見ながら手動で様々な設定をする必要がある
  • 環境構築手順がいくつも条件分岐していたり、複数の記事に分かれていたりと複雑化している
  • 依存関係解決のため、CodeArtifact の token を取得して Eclipse に渡す手順があり、手順書では毎日手動でやることになっていた
  • バッチを実行するために専用の手順書通りに設定を行い、切り替える必要がある
    • この切り替えが面倒なため、同じリポジトリを2つクローンして片方を Web アプリ用、もう一方をバッチ用にして凌いでいる人が複数人いた

そのため、新しくインターンや新卒の方が入った際には環境構築に詰まって社員に質問することが頻繁に発生したり、環境構築に2日以上かかってしまう場合があるなど、何かしら改善したい状況でした。

今回の対応の概要

前述のような問題があったものの、以下のような理由から対応されないままになっていました。

  • 複数のチームにまたがって利用する環境のため、対応するチームが明確に決まっていない
  • 一度構築すれば当分は再度構築する必要がないこと
  • 弊社の人員増加は緩やかなためオンボーディングを改善する優先度がそれほど高くないこと
  • 私を含め最近入ってきたメンバーは Java の環境や歴史的経緯に詳しくなく、触りにくさを感じていたこと

そこで、今回の対応では弊社の価値創造と呼ばれる制度を利用して Java の開発環境の Dev Container 化を行っています。
この制度では、毎月業務時間の10%以下の時間であれば、保守性が気になっている部分をリファクタリングしたり、業務改善用のアプリケーションを作成したりすることができます。

今回の対応では以下の点を重視しつつ Java の開発環境を Dev Container 化しました

  • Dev Container で簡単に構築できるようにする
  • Eclipse で構築した環境とゆるく共存でき、段階的に移行できるようにする

今回の対応のポイント

ここからは今回の対応のポイントをいくつかピックアップして紹介します。

Tomcat

弊社では、Java で書かれた画面と Rails で書かれた画面が混在しているアプリケーションがあるため、開発環境では全てのアクセスが一旦 Apache を通り、そこで URL のパスなどから Rails と Tomcat に振り分けられます。
Tomcat では複数の Java のアプリケーションが動いていて、設定に応じてさらに振り分けられます。
そのため、各アプリケーションの Dev Container 環境に Tomcat を入れて違う port で動かす場合、Apache の設定も変更しなければいけません。
また、その際に既存の方法で構築した環境も壊れないようにする必要もあります。
さらに、構築手順の完全移行後に余計な設定を消す対応も必要になります。
そこで、今回の対応では1つの Tomcat で複数の Java アプリケーションが動く現状の構造をそのままにすることで、Apache は何も変更せずに対応することにしました。

これを実現するため、Tomcat のリポジトリを用意しました。
利用者はこれを clone して docker compose up -d することで Tomcat が起動します。設定などは不要です。
起動しておけば各アプリケーションの Dev Container 環境でビルドされた成果物が volume 経由で自動的に配置される仕組みです。
これによって、一度起動したら特に意識することもなく、今まで通り開発環境の URL にアクセスすれば Apache 経由で Dev Container 環境でビルドしたアプリケーションにつながります。

Tomcat のリポジトリは compose.yml や tomcat の設定ファイルなどを含んでいます。
compose.yml はおおまかに以下のようになっています

services:
  shared-tomcat:
    build: .
    container_name: shared-tomcat
    command: ["/opt/tomcat/bin/catalina.sh", "run"]
    volumes:
      ...
    ports:
      - "8080:8080" # HTTP
      ...
    networks:
      - hoge
    environment:
      - CATALINA_OPTS=...

volumes:
  # Java のビルド結果を格納するボリューム
  # Dev Container と共有するためnameをつける(つけないとvolume名にprefixが付く)
  java_build_target_hoge_app:
    name: java_build_target_hoge_app
  java_build_target_huga_app:
    name: java_build_target_huga_app
  ...

# DB などのコンテナがあるネットワークに接続。DB などはRails環境と共通
networks:
  hoge:
    external: true

Apache 側を変えなくて良いように port を被せているので、Eclipse 側で使っている Tomcat は停止する必要があります。
ただし、新しい環境構築方法で何か問題が発生した場合には単に Dev Container 用の Tomcat を停止して、Eclipse 側で使っている Tomcat を起動すれば元の環境に戻せます。
これにより、問題が発生しても旧環境に戻して開発を続行できるため低リスクで移行できます。
同時起動ではないので完全な共存と書くと誤解を招きそうですが、Tomcat を起動したり止めたりするだけで切り替えできるので"ゆるく共存"と書いています。

アプリケーション側の構築

アプリケーション側では、基本的にReopen in Containerをするだけで Dev Container 環境がビルド・起動され、環境構築が完了します。

アプリケーション側の Eclipse 側環境との干渉防止

アプリケーション側でも、Eclipse で構築した環境などと干渉しないための対策をいくつか行っています。

まず、Maven の pom のプロファイルを分けています。
いくつかのプラグインの設定を変更したりしたいのですが、そのままだと Eclipse で構築した環境と干渉してしまいます。
そこで、Dev Container 環境では環境変数を設定し、その環境変数がある場合はプロファイルを切り替えるようにしています。
これによって Dev Container 環境以外に影響を及ぼさずに依存解決やプラグイン・ビルドに関する設定を変更することができます。

<profile>
  <id>devcontainer</id>
  <activation>
    <property>
      <!-- DEVCONTAINER という環境変数が true の場合に有効になる -->
      <name>env.DEVCONTAINER</name>
      <value>true</value>
    </property>
  </activation>
  <build>
    <plugins>
      <plugin>
      ...

バッチのビルドに使うプロファイルも同様に他の環境と分けています。
バッチのビルドでは、Eclipse が自動的に解決してくれていた依存関係がいくつかあるため、このプロファイルでのみそれらを CodeArtifact から取得するように変更しています。

また、Dev Container 専用の JNDI 設定を記述したファイルを用意し、Dev Container 環境ではそちらを読み込むようにすることで、環境による差異を吸収しています。

アプリケーション側の便利機能

構築される環境には以下のような特徴があります。

CodeArtifact の token 自動取得

Dev Container 環境起動時に自動的にホスト側で CodeArtifact のトークンが取得され、コンテナの環境変数に渡されます。
キーチェーンのパスワード入力と MFA コードの入力は必要ですが、これは手動対応でも必要だったのでそのままにしています。

バッチ実行の簡易化

Dev Container 環境では、VS Code のタスクを実行するだけで簡単にバッチをビルドしたり実行したりできるようにしています。
VS Code のタスクでは以下のように args を指定することで引数を指定できるので、それをバッチ実行用のシェルスクリプトに渡しています。
これによって専用手順での切り替えは必要なくなり、同じリポジトリを2つクローンするなどの回避策も不要になりました。

// バッチ実行タスク(引数指定可能)
{
  "label": "Execute Batch",
  "type": "shell",
  "args": [
    "${input:taskClassName}",
    "${input:taskArgs}"
  ],
  ...
}

// 入力変数の定義
"inputs": [
  {
    "id": "taskClassName",
    "type": "promptString",
    "description": "実行するタスククラスの完全修飾名を入力してください (例: synchrofood.hoge.HogeTask)"
  },
  {
    "id": "taskArgs",
    "type": "promptString",
    "description": "タスクの引数を入力してください。なければ空欄のままEnterしてください",
    "default": ""
  }
]

デバッガーの利用

デバッガーについても VS Code 標準の Run And Debug を押すだけで対応可能です。
Tomcat に接続されてブレークポイントなど自由に使用できます。

ログの閲覧

ログについても Dev Container 環境起動時に自動的に Tomcat にあるログが自動で tail コマンド によって表示されるようになっています。
間違ってそのターミナルを閉じてしまった場合などは VS Code のタスクでいつでも開くことができます。
旧環境では全てのアプリケーションのログが混ざっていましたが、新環境ではそのアプリケーションに関連するログだけ閲覧できます。

導入後の経過

導入後1ヶ月ほど経っていますが、今のところ特に問題の報告はないようです。
逆に「このアプリケーションにも導入してほしい」といった要望はあったため対象を拡大中です。
このまま問題がなさそうであれば、環境構築手順書を更新し、正式な手順にしたいと思っています。

まとめ

今回の対応によって、複雑化していた Java の環境構築が Dev Container の起動と Tomcat コンテナの起動のみで完結するようになりました。
これにより、Javaの環境構築にかかる時間が劇的に短縮され、手順も簡素になりました。
また、ローカル環境に Java や Eclipse、Tomcat などを入れる必要がなくなり、手動での設定もほぼ不要になりました。
毎回手動で行っていたトークン取得が自動化されたり、バッチ実行が簡単になり切り替えも不要になるなどの嬉しい変化もありました。

「内装建築.com」から「店舗デザイン.COM」へのデータ移行対応の紹介

こんにちは、開発部デザイン開発チームの神尾です。

今回は、弊社サービスの「内装建築.com」から「店舗デザイン.COM」へのデータ移行の対応をしたため、その内容と苦労した点について紹介したいと思います。

「内装建築.com」、「店舗デザイン.COM」とは

「内装建築.com」は約2年前に他社から事業譲受したサービスになります。
店舗を構えたい施主さんとその内装デザインを請け負うデザイン会社さんをマッチングするサービスです。

「店舗デザイン.COM」は弊社が2005年から運営しているサービスになります。
「内装建築.com」と同様に施主さんとデザイン会社さんをマッチングするサービスなのですが、施主さんやデザイン会社さんの層、工事内容などが異なるというものになります。

データ移行対応の概要

今回、「内装建築.com」と「店舗デザイン.COM」のマッチングサービスを統合することになりました。
それにあたって、「内装建築.com」の会社さんのうち、希望いただいた会社さんのデータを「店舗デザイン.COM」に移行する対応をしました。

移行対象のデータはそれぞれの会社さんの会社情報や事例情報のデータになります。
また、それぞれに関連する画像も移行しています。

移行対象データの詳細:

  • 会社情報: 約20社
  • 事例情報: 約200件
  • 関連画像: 約1,000枚(複数サイズ含め約4,000ファイル)

移行する際に苦労した点と解決方法

データを移行するにあたって、主に2点が課題になりました。
ひとつはデータ構造の違い、もう一つは大量の画像データです。

データの構造の違いについて

「内装建築.com」は元々他社サイトということもあり、「内装建築.com」と「店舗デザイン.COM」は別々のアプリケーションです。
フレームワークも違えば、作られた時期、設計思想も違います。
データ構造も当然違います。
ここで意識しなければいけないのは以下の2点でした。

  • バリデーションの違い
  • テーブル構造の違い

バリデーションの違いの解決

例えば、事例情報のうち店名のフリガナのデータについて、「店舗デザイン.COM」では全角カナのみしか登録できませんが、「内装建築.com」は中点(・)など一部の文字も許容されます。
つまり、例えばフリガナが「シンクロ・フード」の店舗はそのままでは「店舗デザイン.COM」に移行できません。

その対策として、データ移行をする前に「内装建築.com」側のデータにバリデーション違反がないかのチェックをしました。
方法としては、SQLで「店舗デザイン.COM」側のバリデーションを再現し、こちらに違反するデータを取得し、事前に修正しました。
なお、バリデーション違反のデータをどのように修正するかは機械的に決められなかったため、修正対応としては事業部側に依頼して手作業で修正いただいています。

例:店名カナのバリデーション違反を抽出するSQL:

SELECT
  *
FROM
  companies c
WHERE
  NOT(
    CHAR_LENGTH(c.name_kana) <= 50
    AND c.name_kana ~ '^[ァ-ヴー]+$'
  );

この対応によって、「店舗デザイン.COM」側のバリデーションに合った形でデータを移行することができるようになりました。

テーブルの構造の違いの解決

例えば、資格情報について、「内装建築.com」は業種や取得年など細かくカラムが分かれています。
一方で「店舗デザイン.COM」は資格情報は自由入力で、細かい書式などはユーザーに任せられています。
そのため、移行時は「内装建築.com」側の資格情報のカラムのデータを1つにまとめる必要があります。

「内装建築.com」の資格情報のカラム:

カラム名 内容
category int4 業種
licenser int4 許可の種類
division int4 許可区分
acquisition_year int4 取得年
number varchar(255) 号数

「店舗デザイン.COM」の資格情報のカラム:

カラム名 内容
license longtext 資格情報

このようにテーブルの構造が違うと、データをそのまま移行できません。
そのため、「内装建築.com」からデータを取得する際、「店舗デザイン.COM」側に合わせた構造に変換してデータを取得することで、移行を可能にしました。

資格情報のデータをまとめるSQL(一部省略):

SELECT
  ARRAY_TO_STRING(
    ARRAY_AGG(
      CONCAT(
        CASE pl.category
          WHEN 0 THEN '(土)土木一式工事 '
          -- (中略)
          WHEN 29 THEN '二級建築士事務所 '
          ELSE ''
        END,
        -- (中略)
        CASE pl.number IS NULL
          OR CHAR_LENGTH(pl.number) = 0
          WHEN TRUE THEN ''
          ELSE pl.number || ''
        END
      )
    ),
    CHR(13)
  )
FROM
  professional_licenses pl
WHERE
  pl.category IS NOT NULL;

大量の画像データについて

次に課題になったのは大量の画像データでした。
今回対象となる画像は各会社さんのロゴ画像と事例の画像になります。
その合計は1000枚ほどあり、さらに1枚の画像から複数サイズの画像を用意しているため、実質的にはその数倍の画像を処理する必要がありました。
また、他にも以下のようなシステム的な制約があり、これらを考慮する必要がありました。

  • 移行先システムのダウンタイムはゼロで実施する必要があったこと
  • バッチを実行しているプロセスが他のバッチと共有されている為、長時間連続でのバッチ処理は実行できないこと

移行先システムの負荷軽減

移行先システムでは、画像アップロード時に一旦NFSに配置し、サイズごとに展開し、その後S3に同期というような仕組みを取っています。
通常利用でも画像を1枚アップロードするごとに処理時間がかかるため、そこへバッチで一気に大量の画像をアップロードしてしまうと、移行先システムを利用中のユーザがアップロードした画像の反映にラグが生じる危険性がありました。
その対策として、画像を移行する際、画像を1枚アップロードするごとに5秒間の待機時間を設けました。

バッチ処理の長時間化への対策

ただ、この対応により画像を1枚アップロードするのに8秒ほどかかるようになりました。
バッチ完了までに単純計算で2時間以上を要する計算になります。
先述の通り、今回は長時間連続でバッチを実行できないというシステム的な制約があるため、このままではその制約に違反してしまいます。
その対策として、画像移行バッチが一定間隔で処理の中断と再開を繰り返すようにすることで、長時間実行を回避することにしました。

冪等性の担保

バッチを中断しても、再度そこから実行できるようにするためには、冪等性を担保しなければいけません。
今回は各画像がアップロード済みかどうかの情報をテーブルで管理し、バッチ実行時にアップロード済みの画像はスキップして実行するようにしました。
これにより、バッチを実行する際には前回完了したところから再開できるようになりました。

なお、アップロード済みかどうかは次のように判定しています。

  • 画像のアップロードが完了次第、アップロード後の画像のカラムにファイル名が保存される
  • そのカラムにデータが入っていればアップロード済みと判定する

進捗管理テーブルのアップロード後の画像カラム(例:会社ロゴ):

カラム名 内容
new_company_logo_image varchar(255) アップロード後会社ロゴ画像

アップロード済みでない画像を取得する処理:

画像データのリスト = 進捗管理テーブル.where(
  new_company_logo_image: nil,
)

また、画像のアップロード処理において、エラーが発生しても自動的にやり直すように実装しました。
アップロード処理ではアップロードが完了したタイミングで先述のテーブルのアップロード後の画像カラムにデータを入れているため、アップロード中にエラーが発生した際はこのテーブルに情報が保存されません。
そのため、その画像はまだアップロードされていないという判定になり、次回実行時に再実行(やり直し)されます。

なお、厳密には中断処理は1枚の画像アップロードの処理が終わったタイミングで指定の時間を超えていないか確認する形で実装しているため、中断処理によってアップロード処理が途中で止まることはない実装になっています。

中断処理の実装:

start_time = Time.zone.now
max_duration = 30.minutes
画像データのリスト.each do |画像データ|
  break if Time.zone.now - start_time > max_duration

  # 画像アップロード処理
  sleep(5)
end

まとめ

今回は「内装建築.com」から「店舗デザイン.COM」へのデータ移行の内容と、苦労した点について紹介しました。

移行結果:

  • 移行データ数: 会社約20社、事例約200件
  • 画像移行数: 約1,000枚(複数サイズ含め約4,000ファイル)
  • データ整合性: 100%(移行後検証済み)

紹介したような問題など、対応に苦労した点もありましたが、最終的には無事予定通り希望いただいた会社さんのデータ移行を完了することができました。 今回の経験も踏まえて今後も開発を頑張っていきたいと思います。