こんにちは、開発部デザイン開発チームの神尾です。
今回は、弊社サービスの「内装建築.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%(移行後検証済み)
紹介したような問題など、対応に苦労した点もありましたが、最終的には無事予定通り希望いただいた会社さんのデータ移行を完了することができました。 今回の経験も踏まえて今後も開発を頑張っていきたいと思います。