こんにちは。Feature2 Unitのうなすけです。我々のチームの担当範囲のひとつには「データの入出力」というものがあり、お客様からAPI呼び出しやファイルアップロードなどで受け取ったデータを適切に処理するコンポーネントの運用・開発をしています。
我々の担当している機能のひとつに、お客様からアップロードしていただいたCSVファイルの内容をデータベースにインポートするというものがあります。これは裏側ではEmbulkを使ってMySQL(Aurora)に投入するということを行っています。
このとき、アップロードされるCSVの内容に日本語(マルチバイト文字列)を含められるように機能追加しようとしたところ、日本語のデータがインポートできていないという問題が発生しました。冒頭画像で ???
となっている部分には日本語の文字列が表示されていてほしかったのですが、データの取り込みに失敗しています。
どこかでASCIIの範囲外のデータが失われてしまっているようです。
データの流れ
この機能において、お客様からアップロードしていただいたデータはおおまかに次のようなフローで処理されていきます。
- RailsがCSVを受け取り、S3に置く
- Lambda functionがS3に置かれたファイルを読み、中身のバリデーションを行う
- Embulkによって、まずMySQLの一時テーブルにCSVの中身を取り込む
- Embulkによって、既存のデータとの重複を取り除きつつMySQLの永続テーブルに挿入する
- 処理が完了したら完了メールを、失敗したらアラートを鳴らし、失敗したメールを送信する
このフローによって取り込まれたデータがReproのサービス上で参照できるようになるのですが、そのデータに含まれている日本語文字(マルチバイト文字)が画面上で、冒頭でも貼った以下のような状態(???
)になっていました。
実は前回記事にした、Lambdaで日本語CSVを扱えるようにした対応のあと、こんどは後段のEmbulkで日本語がエラー(というよりは欠落するよう)になったというわけです。
このようなとき、インターネットを検索するとMySQL output plugin for Embulkのoptionに characterEncoding: UTF-8
を設定するとよい、という情報が見つかります。具体的には以下のような感じです。
https://github.com/embulk/embulk-output-jdbc/blob/master/embulk-output-mysql/README.md
out: type: mysql host: foo port: 3306 user: bar password: "sample" database: "sampledb" table: "sampletable" options: serverTimezone: UTC characterEncoding: UTF-8 # これ mode: insert
しかし、これを一時テーブルの設定、永続テーブルの設定の両方に書いてみてもなんだかうまくいきません。
タネあかし : 一時テーブルの create_table_option
不足
なぜうまくいかないのか、Embulkの処理を追いつつ作成されたテーブルの中身も見ていくと理由がわかりました。
Embulkでの処理において、一時テーブルはその都度作成するようにしているのですが、そのテーブルを作成するときにCHARACTER SET
の指定が欠けていました。具体的には以下のような指定をすることで日本語が欠落することなく永続テーブルに保存されるようになりました。
out: type: mysql host: foo port: 3306 user: bar password: "sample" database: "sampledb" table: "intermediate_table_foo" options: serverTimezone: UTC characterEncoding: UTF-8 create_table_option: 'DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin' # これ mode: insert
わかってしまえば簡単なことではありますが、なかなか気付くことができずに時間を溶かしてしまいました。見落としがちですが、エンコード情報は指定できる場所では執拗に指定していくことが大事ですね。
という小ネタでした。
We are hiring
ReproではRailsだけでなくEmbulkのような大規模なトラフィックやデータを扱うためのソフトウェアを多く利用しています。「Railsアプリケーションを開発してきたけど、そろそろ新しい技術的な挑戦をしてみたい!」という方がいらっしゃいましたら、ぜひご連絡ください!