Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] データベース内データのJSONダンプおよびダンプされたデータのインポート #156

Draft
wants to merge 43 commits into
base: master
Choose a base branch
from

Conversation

ochaochaocha3
Copy link
Member

現在のデータベース内データをJSONにダンプする機能と、その機能によってダンプされたデータをデータベースにインポートする機能を作っています。

ダンプ機能では、指定したディレクトリの中に、モデルごとに以下のJSONファイルにデータを出力します。ファイル名にNがついているモデルは、件数が多いため10,000件ごとにファイルを分割するものです。

  • Channel -> channels.json
  • IrcUser -> irc_users_N.json
  • MessageDate -> message_dates_N.json
  • Setting -> settings.json
  • User -> users.json
  • ChannelLastSpeech -> channel_last_speeches.json
  • Message -> messages_N.json
  • ConversationMessage -> conversation_messages_N.json

まず最低限のダンプ機能を作ったため、正常に動くかローカル環境で試していただけないでしょうか?

もし正常に動いた場合、続いて以下のことをこのPR上で確認、決定したいと考えています。

  1. ダンプしたデータをインポートするタスクの名前をどうするか。irclog2jsonで出力したJSONファイルのインポートのタスクとは、ファイル名などが異なるため、分ける必要があります。
    • 例えば data:import:dumped_json[dir] など。
  2. irclog2jsonで出力したJSONファイルと形式(キー名と、値の型や書式)を合わせるか。
    • 最初にダンプ機能を作った段階では、Railsが標準で提供していた to_json を使ってJSONにするデータを作っていたため、そのデータと形式を合わせておくと、インポート処理を書くのが楽です。
    • ダンプしたデータをインポートするタスクとirclog2jsonで出力したJSONファイルのインポートのタスクは別々に作られるため、異なる形式のJSONにするのでも大丈夫だとは思います。
  3. ダンプ用のタスクをモデルごとに分割するか。
    • 件数が多い、MessageやConversationMessageのダンプに長い時間を要するため、一部のモデルのデータだけを短時間でダンプする需要があるかもしれないと感じました。
    • もし分割する場合、各モデルのデータをダンプする複数のタスクと、それらをすべて実行するタスクを作ることになるでしょう。

件数が多いため、each_group_of_hash_for_json_in_batchesのバッチ処理数を
10000件に上げた(併せて、引数を渡していなかったバグを直した)
updated_at を調べるべき箇所で created_at を調べていた
@koi-chan
Copy link
Member

  1. タスクは、ダンプしたもののと過去ログ用のは分けましょう。

  2. タスクを分けるのであれば、JSON の中身も違っていてよいと現段階では考えます。IrcUser のデータが PRIVMSG/NOTICE にあるなど、そもそもデータの中身の質が違うので、分かれていた方が自然だと思います。もし統一するのなら、過去ログの形式・タスクをダンプしたものと合わせたいです。

  3. モデルごとに分割した方がよいと思います。ただ、いくつかのモデルはダンプ処理の必要性を感じられません。例えば ChannelLastSpeech や MessageDate は、インポート処理が速いからといって先にインポートした結果、Message や ConversationMessage がインポートされていない状態では壊れたデータでしかないはずです。分割しないのならばインポート処理を簡略化できますが、モデルごとのダンプ・インポートをするのなら正常な状態を壊してしまう可能性が高いと思います。

ただ、これらとは矛盾するようですが、今回の最終的な目的である、テーブル構造の一からの見直しするためのダンプ機能であるなら、処理は複雑化しますが IRC ネットワークを流れるデータを模したような形式での(過去ログをインポートするような)インポートを目指すべきなのかもしれないとも思います。
つまり、チャンネル(Channel)とメッセージ(Message/ConversationMessage)だけを抽出し、それらから紐づけられたデータを取り込める機能の方が、テーブル構造の改良後に使いやすいし、これでダンプされたデータ構造をそれほど意識せずに改良作業を行えるのではないかと考えています(むしろ当初はそれを想定していました)。

@ochaochaocha3
Copy link
Member Author

1と2について、承知しました。その方針で作ろうと思います。

3について、MessageDateとChannelLastSpeechはキャッシュ用途なので、ダンプしないことに同意します。

最後の段落について、「IRCネットワークを流れるデータを模したように」する具体例と、「テーブル構造の改良後に使いやすい」のがどのような場合かの具体例はございますでしょうか? やや抽象的で、今のところどうなるのかの想像が難しいです。

@koi-chan
Copy link
Member

koi-chan commented Aug 31, 2019

IRCネットワークを流れるデータを模した具体例は以下の通りです。

[
  {
    "type": "Notice",
    "channel_id": 1,
    "channel": "もの書き",
    "timestamp": "2019-08-31T19:31:12.000+09:00",
    "nick": "Role",
    "message": "koi-chan -> 2D6 = [5,5] = 10",
    "created_at": "2019-08-31T19:31:12.000+09:00",
    "updated_at": "2019-08-31T19:31:12.000+09:00",
    "user": "dicebot",
    "host": "services.cre.jp"
  }
]

上記の例のもととした conversation_messages_0.json との違いとしては以下の通りです。
messages_0.json も上と同様のデータ構造を意図しています。

  • チャンネル名自体のデータが存在する (channel_id と競合したときはチャンネル名を優先する)
  • irc_user 項目がなく、代わりにその中身である "user", "host" が存在する

こうすることで、例えば Messages-IrcUser の中間テーブルを作成するなどの「テーブル構造の改良後」のテーブルにダンプしたデータを再挿入したとき、元の IrcUser のデータと突き合わせてどのレコードとの紐付けが為されていたのかを探す処理を新しく作らずとも、IRC ボットから入力される(元からメッセージの他の構成要素とセットになっている)データを保存する処理を使い回しやすくなります。

さらに、このようにダンプデータも元のメッセージが予めセットになったデータにしておくことで、既に稼働している(チャンネルIDやIrcUserがダンプと重複するデータが保存された)データベースに追加するときにも、データの変更や復元処理が不要になります。

テーブル構造を新規に見直し作り直したプログラムの更新の際に、サービスを止める時間を短くすることも出来ます。
おそらく実際には、停止→データのダンプ→プログラムのアップデート→データのインポート→サービス再開という手順ではなく、予め違うディレクトリ・データベースで更新済みのプログラムのセットアップ(新規インストール)→サービス停止→サービス再開(IRCからデータベースへの書き込み開始)→データのインポート、という手順でプログラムの更新を行なうことも想定する可能性があります(というか可能ならば後者の手順を踏むはずです)。

@ochaochaocha3
Copy link
Member Author

具体例ありがとうございます。irclog2jsonが出力する形式に近いですね。多少インポートが遅くなりますが、データの整合性を考慮すると、確かにそちらの方がIrcUserなどの他テーブルに依存しない分安全で良いと思いました。徹底すると irc_user_id と同様に channel_id もなくしても良さそうに思いましたが、いかがでしょうか?

@koi-chan
Copy link
Member

koi-chan commented Sep 1, 2019

channel_id もなくして良いと思います。
IrcUser よりはデータの多様性が少なそうなので例では残しましたが、必須だとは思いません。

@ochaochaocha3
Copy link
Member Author

タスクを分割しました。また、ダンプ対象からキャッシュ用のテーブルを除きました。以下のものが対象として残っています。

  • channels
  • irc_users
  • settings
  • users
  • messages
  • conversation_messages

MessageのダンプにおいてN+1問題が発生し、非常に遅かったため
channelとirc_userのデータを含むようにする。
id、created_at、updated_at、channel_id、irc_user_idを除外する。
@ochaochaocha3
Copy link
Member Author

タスクの分割、キャッシュ用のテーブルの除外、MessageとConversationMessageのダンプで出力する内容の変更までできました。問題がなければ、インポートの実装に進めそうです。

@koi-chan
Copy link
Member

koi-chan commented Sep 8, 2019

テストファイルに ChannelLastSpeech, MessageDate が残っていますが、わざとでしょうか。

状態確認用のため、コンソール出力内容に、どのタスクを実行しているかを挿入していただけませんか。

@koi-chan
Copy link
Member

koi-chan commented Sep 8, 2019

data:dump:json:conversation_messages にて、以下のエラーが出力されます。

NoMethodError: undefined method `user' for nil:NilClass
/home/server-admin/git/hub/log-archiver/app/models/concerns/hash_for_json.rb:52:in `to_hash_for_json_with_channel_and_irc_user'

2019-09-08 17:14 追記
データが壊れていなければ(conversation_messages.irc_user_id = 0でなければ)、きちんと実行できるようです。
手元のデータが壊れていたのが原因ですので、処理は実行完了できました。

@ochaochaocha3
Copy link
Member Author

確認ありがとうございます。

* data:json:concat 複数の JSON ファイルを結合する
* data:json:sort_by_time タイムスタンプを元にメッセージを並べ替える
* data:json:suppliment_user_host JOIN メッセージを元に推測した IrcUser を補完する
@ochaochaocha3
Copy link
Member Author

@koi-chan さんとの相談の結果、タスクの名前空間を json:* に変え(dumpでは json:dump)、.rakeファイルも lib/tasks/json/ に入れることになりました。

テストファイルに ChannelLastSpeech, MessageDate が残っていますが、わざとでしょうか。

状態確認用に、コンソール出力内容に、どのタスクを実行しているかを挿入していただけませんか。

おっと、これらやってませんでした。また修正します。

@ochaochaocha3
Copy link
Member Author

テストファイルに ChannelLastSpeech, MessageDate が残っていますが、わざとでしょうか。

これについては残しておくことにします(to_hash_for_json の動作確認ができることから、残っていても問題がないと考えたため)

ochaochaocha3 and others added 16 commits September 10, 2019 00:02
「TODO」に反応してしまったため
ToDo: タスクの名前空間を json 以下に移動する
PR #159
JSON ファイルのログをデータベースに保存するタスクを追加
fix: #163

チャンネル名・ニックネームをキーとするキャッシュを用いることで、複数のチャンネルのメッセージが保存された JSON ファイルを処理できるようにした。

正しいデータが与えられている場合は動くが、一部が欠損したデータを与えられた場合はチャンネルによって補完が利かない可能性がある。
…host

JSON ダンプの IrcUser データ補完タスクの修正
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants