Discourse における投稿追跡の仕組み

Discourse は、画面に表示されるすべての投稿の読了時間を追跡します。このシステムは年々進化しており、その仕組みや存在理由を理解するために、コードを振り返る必要があることがよくあります。

この投稿では、現在の実装に関する痛ましい技術的な詳細について説明します。

Discourse クライアントがタイミングを追跡する方法

投稿のタイミング追跡は、screen-track.js.es6 で実装されています。このモジュールは、投稿が画面に表示されてからどれくらいの時間が経過したか、およびトピックが画面に表示されてからどれくらいの時間が経過したかを追跡する役割を担っています。

トピックページが「スクロール」すると、画面トラッカーに対して、現在表示されている投稿と、その中で既読の投稿を通知します。部分的に表示されている投稿も「表示中」とみなします。

その後、screen track は毎秒「tick」を発火させ、サーバーに送信する必要があるデータを決定します。

画面トラッカーは、複数のリストを保持します。

a. サーバーにまだ送信されていない(投稿/その投稿に費やされた時間)のリスト
b. 既読であるとわかっている投稿のリスト
c. 現在画面に表示されているとわかっている投稿のリスト

タイクの開始時(毎秒)、(a) に投稿がある場合、それらをサーバーに送信することを検討します:

  • flush_timing_secs(デフォルト 60 秒)の SiteSetting が、最後にサーバーにデータを送信してから経過している場合。

  • 投稿のいずれかがユーザーによって「未読」である場合、リスト全体をすぐに送信します。

タイクの終了時に Discourse がフォーカスされている場合:

「画面に表示されている投稿」がある場合、各投稿に対して「1 tick」の時間を記録します。

任意の時点でトピックから離れる場合(Discourse の別の場所へ移動する場合)

「進行中」の (a) のすべてをすぐにサーバーに送信します。

制限事項

  • トピックを表示するたびに、投稿あたりの読了時間は最大6 分まで記録されます(離れてから再度その投稿に戻るとリセットされます)。

  • 3 分経過してもスクロールが一切行われなかった場合、再びスクロールが行われるまでこのサブシステムは無効になります。

  • 匿名ユーザーに対しては最大5 トピックまでのタイミングを記録します(これはユーザーが登録した際に posts_timing テーブルのデータに変換されます)。

重要な観察事項

  1. post_timings テーブルはミリ秒単位で追跡していますが、tick が発火するタイミングに応じて、投稿あたり「0〜1000ms」の「未記録」時間が発生します。

  2. トピックを見る「セッション」ごとに、投稿あたりの読了時間は最大 6 分まで記録されます。投稿あたりの読了時間に上限はなく、ユーザーがトピックに戻れば、数日間にわたって読了時間を記録し続けることができます。

このデータをどのように活用するか

最も重要な情報は「ユーザー X が投稿 Y を読んだかどうか」です。これはトピックの未読数や、その他多くの重要なデータを決定します。

この二元的な利用を除き、post_timings に記録された時間を使用して、投稿の avg_time を計算します。

投稿の平均時間は、時間の自然対数の平均の指数(幾何平均)として計算されます。

例えば:

投稿 1: sam、10 秒
投稿 2: jane、1 時間

avg_time = exp((log(3600000) + log(10000)) / 2)
=~ exp((15.09 + 9.2) / 2)
=~ 189094
=~ 189 秒

この avg_time は、スコア計算機において「投稿スコア」の 構成要素 として使用されます。

スコア = 5 * 返信数 + 15 * 評価スコア + 5 * 流入リンク数 + 2 * ブックマーク数 + 0.05 * avg_time + 0.2 * 投稿読了数

したがって、上記の例では、投稿の平均読了時間 189 秒は 37 ポイントに相当します。つまり、約 2 件の「いいね」に相当するか、あるいは 72 件の読了に相当します。

「投稿スコア」は「ベストオブ」において、トピック内の最高の投稿を特定するために使用されます。

「いいね!」 24

Isn’t this read data the backing store for these numbers, total read time?

「いいね!」 3

Yes the “topic read time” I will update the OP to explain about that, I touched on it very lightly.

The topic_users table has a column called total_msecs_viewed. This number is updated independent of post_timings in the same controller action. We can not “rebuild” that number from post timings cause we have no idea about overlapping times.

The “topic timing” piece has no 6 minute limit like post timing does. The number is flushed with the same post timing batch according to the same rules.

I think I was so focused on talking about post tracking that I missed out on explaining the topic tracking part.

「いいね!」 4

Whoa… we do? Do we really need to do this? It feels unnecessary?

What do you mean by “overlapping times”?

「いいね!」 1

I have not tested this recently, but yes the code is all there to do it. I guess it means you can get to TL1 a bit faster.

Say you we know about my timings:

Post 1: 10 seconds
Post 2: 12 seconds
Post 3: 17 seconds

The time I spent reading the topic can be anywhere between 17 seconds and 39 seconds.

So we can not use the data in the posts timing table to figure out what the number in topic users should be. So we are forced to track that other number independently.

It is not a huge deal and makes no big diff, but there is no way to “run an inventory” and check that the number in topic user is 100% correct.

「いいね!」 3