2016.1.26
0からはじめるPHP#75(番外)【通知機能を考える】
このシリーズ、番外といいつつちゃっかり番号はカウントしてるんですが、番外編は
要するにただの「結論の出ていないぼやき」なので
つまりは普通の日記です。
ということで今回はこの
通知というシステムをぼやきます。
あ、その前に現状報告でもしておきましょうか。
メイン画面はこんな感じです。といってもデザイン周りはまじで何も触ってないのでとても殺風景です。
あと、当たり前ですが記事番号はデバッグ用に表示させてるだけで、日時ももっと下に表示させる予定ですよ。
サイト名はまだ暫定でしか決まってませんし、そもそも僕自身気に入ってないので非公開にしておきます。っていうかどんな方向性のプロダクトか完全にバレちゃいますからね←
「返信」の文字をクリックするとこのようなアコーディオンが開きます。
なんか下の方に変なボタンありますが、まぁ言いたいことは分かるかと思います。あ、ちなみにユーザビリティ向上を目指し、これらのボタンは返信メニューから取り外して投稿に直接表示させる予定です。じゃあなんでアコーディオンに埋め込んだんだって話ですね。
何も考えずに置いたことがバレてしまいました。
あと
作業に飽きてきたので紹介文書きました。
半分ぐらいモザイクかけましたが、興味を持ってもらえるでしょうか・・・・・・?(笑)
正直情報を出しすぎて、このプロダクトの方向性がたぶん伝わってしまったんじゃないかと思ってますけど、気づいた方は興味あるかどうか教えて下さい←
公式がミュート推奨してるってよくよく考えたらキチガイじみてますよね
さて、今回のテーマはこの説明文の中にもある
「お気に入り」機能です。
あ、僕は「いいね」嫌いなんでふぁぼを輸入します。ふぁぼの方がどう見ても汎用性高いですしね。
この機能は今日の朝思いついて、輸入しようと思ったんですが
具体的な実装法がまじで分からないんですよ。
分かりやすくtwitterで例えると、ツイートをお気に入りに登録するところまでは簡単に実装できます。
ただ、その後
相手にどうやって通知するんだろうってところで詰みます。
案1:ツイートとふぁぼをを紐付ける
最初に思いつくのはこれです。ふぁぼが送られたらツイートのテーブルに格納してある配列に新たに書き加え、投稿者と日時を保持します。
そうすると後はそれを通知される側が読みこめば実装できますね。
ただ、同時書き込みが多発するので悲観的ロックを施さなければいけなかったり、読み込んで検索するのが大変そうだったり、まぁ明らかに現実的じゃないんですよね。
案2:ユーザーテーブルに通知領域を作る
ということで、該当ユーザーに通知用の領域を作り、アクセスがあったら通知済領域に移動させ削除する、みたいな手も考えられます。
でも、これも同時書き込みの問題があるのであまり好ましくありません。
案3:ふぁぼテーブルを作る
ということで、結論は必然的にこうなります。ふぁぼ情報を管理するテーブルを別途作るわけです。
これなら同時書き込みは起こりえません。同時にふぁぼられても、2つのレコードが生まれるだけで競合は考えられません。
で、通知を読み込もうって時はwhere句で自分に関係するところだけ絞って取って来ればいいワケですね。
ふぁぼテーブルを作ることまではおそらく確定なんですが、通知するという段階だと、これだけだと工夫が足りません。
時系列順に並べるだけだと、もし50通ぐらいふぁぼが来たとするとパンクしますよね。っていうか、ふぁぼぐらいまとめろよって思いますよね。
単にまとめるだけなら簡単ですが、ふぁぼされるツイートが2つあり、それぞれバラバラなタイミングでふぁぼられていくとするとどうでしょう。
当然、通知領域には最新のアクティビティがあった順に表示されるべきです。しかしそれを取得する上手い方法を考えないと、思った通りに動作しません。
今からそれをちょっと考えてみましょう。
キーとなるのは
重複行の削除です。
手順1:ふぁぼテーブルから自分に関するカラムを抽出
これは簡単です。where句で指定するだけでできます。
手順2:時系列順にソート
まずここで考えなきゃいけないのは、一体どこまでを取得するかって問題ですよね。まぁ水平パーティションの知識がない現状、全件取得するしかないんですけど、将来的にはどうすべきか考えなきゃいけませんね。インデックスつけるんであんま関係ないかも知れないですけど。
手順3:ふぁぼ元の情報が入ったカラムのみをSELECTし、重複行を削除
ここがまず肝となりうるところですね。ここがふぁぼを「まとめる」場所に該当します。
時系列順に並べるには最新行だけあれば良いので、それ以外のものはばっさり切ってしまいます。あとは、最新のアクティビティに合わせ、関連するアクティビティを同時に表示すれば、最新が時系列順で並べられ、かつ同様のアクティビティをまとめることができます。
が、ここではまだ準備が整っただけです。ここから先どうしましょうね。
大きな問題を孕みますが、この後の手順を予測して考えてみましょう。
手順4:一旦getし、foreachでそれぞれについて取得する
まぁ実際問題こうする他ありません。時系列順に並べた親に、子の情報をくっつけることでお望みのものが手に入るわけですね。
ここで水平パーティションが行えるようなら行ってもいいんですが、将来どういうシステムになるかまだ予想がつかないですし、全件取得でもインデックスの力を借りればなんとか・・・・・・?
ということで方針が書いてる内に定まってしまったのですが、ここで一つの大きな問題が発生します。
基本的にtwitterと同様のシステムなんで、通知領域のページが存在することはイメージできるかと思います。
twitterでは、通知に表示するのはリプライとふぁぼでございます。
しかしこれらは全く別の構造を持つために、別々のテーブルに保存されています。この別々のテーブルに保存されたものを、更に時系列順にソートしなくてはなりません。
この問題に対する対策をいくつか考えてみましょう。
案1:ふぁぼテーブルでなくアクティビティテーブルにする
つまり、一つのテーブルで管理してしまいましょう、というわけですね。しかしちょっと情報が余分になってしまい、データベース的にあまり美しくないですし、管理も面倒になりそうです。
しかも、出来るテーブルがふぁぼとリプのIDが混在しただけのものなので、まず表示するデータがリプかどうかを判断し、リプの場合はwhere検索で逐一情報を取得していかなくてはいけません。ちょっとコストかかるんじゃないでしょうか・・・・・・。
この方法だと確かに動きはするんですけど、ちょーっと下手くそすぎるというか、未来がない気がします。データ構造が一緒ならJOINできるんですけどね・・・・・・。
案2:つぶやきに「最新のふぁぼられ時間」カラムを追加する
今思いついたんですけど、これはそこそこ現実的なんじゃないでしょうか。
誰かがふぁぼったら、その該当レコードに時間だけupdateし、それを用いてソートすることでちゃんと時系列順になりますね。
しかし、よくよく考えてみると、つぶやきのレコード自体を時系列順にソートしてしまうと、リプの時系列も狂ってしまう気がしますね。
あくまでもふぁぼとリプって別々じゃないですか。でも、一緒くたにしてしまうと、昔のリプがふぁぼられたらそのリプ自体が上に表示されてしまいますね。それはよくないです。
案3:ふぁぼ通知を別ページに分ける
おい逃げんなよ
妥協案と見せかけて実はいいんじゃ・・・・・・?と思っている案の一つです。
というのも、全ユーザーが同じタイムラインを見る図を想像してもらうと分かると思うんですが、
ふぁぼ魔が100人居ると完全に機能停止するんですよ。なんかいっぱいふぁぼってくる人いるけど、どれが重要なのか分かりっこねえよ、と。
別ページに分けるのであればリプと一緒にソートする必要もありません。だって一緒に表示することがないですしね。
ふぁぼ通知を別ページに分けるんだとすると
そもそも表示を統合する必要なくねってことにも気づきます。
だって、統合しちゃったら見た目ショボくなっちゃいますしね。
ということで
技術的理由によりふぁぼ通知は別のページにすることにしました。
twitterのシステムを真似る、という手もあったんですが、なかなかいい情報が見れなかったので諦めます。将来誰か詳しい人に教えてもらうことにします。僕の知識じゃ上手いシステムが思いつきません。
はー・・・・・・システム設計って難しい!!!
・・・・・・あ、そうそう。
私の身の回りには就活を控えた後輩たちとかいて、僕もどんな未来を想定するにしても、就活はたぶんします。
技術ってのは人からパクるのが一番ラクですからね。
で、具体的に何がしたいのかわかんないよーって人が割と殆どだと思うんですよね。まぁ普通の人はそうでしょう。
かくいう僕も、最終地点やそれに至る経路ぐらいは考えてたんですが、言語化して説明できる程度のものはなかったんですね。
でも、僕は自分が一番やりたいことが何なのか分かりました。
企画です。
僕はSEとかプログラマとか、そういう道を歩むことにはなると思っていたんですが、自分が本当にやりたいのは企画する側なんですよね。
だってコーディングだるいし。
ただまぁ、分かったところで就活ではやっぱりSEを目指すことになると思います。
だって、自分が一番欲してるのは技術ですから。