レビュー対象が1つのフラグしか持たない場合、Score_to_hide_postが0になる

要約

バックグラウンドジョブ Jobs::ReviewablePriorities が、パーセンタイルクエリが行を返さない場合に priority_#{priorities[:high]}0 として書き込むため、score_to_hide_post0 になる可能性があります。このジョブは reviewable_count >= 15 の場合にトリガーされますが、パーセンタイル計算には HAVING COUNT(*) >= :target_count (通常 target_count2) を満たすレビュー対象のみが含まれます。ほとんどのレビュー対象が1つのフラグしか持たない場合、パーセンタイルサブクエリは何も返さず、high0 になり、score_to_hide_post = ((high * ratio) * scale).truncate(2)0 に評価されます。これにより、非表示のしきい値がゼロに収縮し、不正確な非表示動作が発生します。


由来(関連コード/事実)

  • score_to_hide_post は以下を通じて計算されます。
score_to_hide_post = ((high.to_f * ratio) * scale).truncate(2)
  • high はプラグインストアから読み取られます。
PluginStore.get("reviewables", "priority_#{priorities[:high]}")
  • そのプラグインストアのエントリは Jobs::ReviewablePriorities (システムジョブ) によって書き込まれます。
    ジョブは以下の場合に実行されます。
reviewable_count = Reviewable.approved.where("score > ?", min_priority_threshold).count
return if reviewable_count < self.class.min_reviewables

ここで self.class.min_reviewables15 です。

  • ジョブは SQL を使用して high を計算します。
SELECT COALESCE(PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY score), 0.0) AS medium,
       COALESCE(PERCENTILE_DISC(0.85) WITHIN GROUP (ORDER BY score), 0.0) AS high
FROM (
  SELECT r.score
  FROM reviewables AS r
  INNER JOIN reviewable_scores AS rs ON rs.reviewable_id = r.id
  WHERE r.score > :min_priority AND r.status = 1
  GROUP BY r.id
  HAVING COUNT(*) >= :target_count
) AS x

:target_count は通常 2 です。


原因

2つの別々のしきい値が組み合わさってギャップが生じます。

  1. ジョブは、min_priority_threshold を超えるレビュー対象が少なくとも min_reviewables (15) 個ある場合にトリガーされます。これは :target_count 要件を無視した大まかなカウントです。
  2. しかし、high を生成するパーセンタイル計算には、COUNT(*) >= :target_count (つまり、少なくとも 2 つのレビュー対象スコア) を満たすレビュー対象のみが含まれます。多くのレビュー対象がそれぞれ1つのフラグしか持たない場合、サブクエリは行を返さず、パーセンタイルはフォールバックの 0.0 を返します。

そのため、ジョブは実行される可能性があります (大まかなカウントで ≥ 15 個のレビュー対象があるため) が、パーセンタイル集計には適切な行がなく (HAVING を満たすものがないため)、high0 になり、その結果 priority_high0 として書き込まれます。これが score_to_hide_post にフィードされ、収縮します。


影響

  • score_to_hide_post0 になり、誤って投稿が非表示とみなされたり、合理的な非表示しきい値に依存するロジックが壊れたりする可能性があります。
  • これは、多くのレビュー対象が存在するが、それぞれが単一のフラグ/レビュアーしか持たないサイトで発生します。これは、小規模または中規模のコミュニティでは珍しくありません。

提案される修正(オプション)

  1. 書き込む前にパーセンタイルクエリが十分な行を返すことを保証する
  • パーセンタイルクエリを実行した後、パーセンタイル値が 0 であり、サブクエリが行を返したかどうかを確認します。
  • 行が返されなかった場合は、既存の priority_high上書きせず、書き込みをスキップするか、以前の値を使用するか、設定済みのデフォルト値にフォールバックします。
  • これは最も安全で、侵襲性の低いアプローチです。
  1. target_count を考慮してジョブトリガーを調整する
  • HAVING COUNT(*) >= :target_count を満たすレビュー対象の数が min_reviewables 以上の場合にのみ実行されるように、ジョブの事前チェックを変更します。
    つまり、COUNT(*) >= target_count を満たすレビュー対象を id でグループ化してカウントし、そのカウントが ≥ min_reviewables の場合にのみ続行します。
  1. 管理者が score_to_hide_post または priority_high を手動で設定できるようにする
  • 管理インターフェースで score_to_hide_post または priority_high を直接入力または調整するオプションを提供します。
  • これにより、パーセンタイルクエリが予期しない結果を生成した場合でも (例: サンプルが少なすぎるため)、システムは管理者が指定した合理的なしきい値を使用でき、自動計算によるエラーを防ぐことができます。