fix: last_read_at の排他的境界により最新通知が既読化されない問題を修正 (#100)#101
Conversation
PUT /notifications の last_read_at は updated_at < last_read_at のスレッド だけを既読化する排他的境界(等値は未読のまま残る。実 API 検証済み)。 最終チャンクで送信済み最新の updated_at 自身を境界にしていたため、バッチ内の 最新通知が絶対に既読化されず毎分再送され続けていた。 - 最終チャンクの境界を取得スナップショット(fetched_at)にする。取得フィルタ (before)と同じ値・同じ排他的比較のため、取得・送信した集合と既読化される 集合が一致する - 中間チャンクの境界を未送信先頭の updated_at に変更。排他的なので未送信先頭は 巻き込まれず、従来の「境界未満の最大値」より広くカバーできる(等値の 取り残しも解消)。同一秒がチャンク境界を跨ぐ場合は稀な重複を許容しロストを 避ける - find_notifications_unread がスナップショットを引数で受け取るよう変更し、 Notify::Usecase が取得と既読化で同じ値を使う - spec を新しい境界選択に合わせて更新、スナップショット共有のテストを追加 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates the notification fetching and marking-as-read logic to use a consistent snapshot time (fetched_at) for both operations, ensuring that the fetched and marked-read sets match and resolving issues with exclusive boundaries. The review feedback highlights a potential risk of notification loss due to sub-second precision mismatches between the GitHub GET and PUT APIs, and suggests truncating fetched_at to second precision to mitigate this risk.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
before / last_read_at の一致をシリアライズ精度(現状はどちらも秒単位の RFC 3339)に依存させないよう、スナップショット生成時に秒へ切り詰める。 サブセカンドの解釈差による取りこぼしを構造的に防ぐ。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
dev 環境での動作確認結果:
(確認中に判明した別事象=ウォームコンテナの接続固定による取得不能は #102 として切り出し) |
概要
issue #100 を解決する。CI 通知などバッチ内の最新の通知が既読化されず、毎分再送され続ける問題を修正する。
原因(実 API 検証済み)
PUT /notificationsのlast_read_atは排他的境界で、updated_at < last_read_atのスレッドだけが既読化される。等値(updated_at == last_read_at)は未読のまま残る。last_read_at09:24:54Z(等値 = 従来実装)09:24:54Z09:24:55Z(+1s)09:24:54Z従来の
mark_read_throughは最終チャンクでlast_read_at = sent.last.updated_at(送信した最新通知自身の時刻)を渡していたため、バッチ内の最新の1件は原理的に既読化されず、次回実行で再取得→再送を毎分繰り返していた(新しい通知が届くと境界が追い越して解消するが、今度はその通知がスタックする)。修正内容
排他的境界を前提に境界値を選ぶ:
fetched_atにするbefore= updated < fetched_at)と同じ値・同じ排他的比較のため、「取得・送信した集合」と「既読化される集合」が正確に一致するfind_notifications_unreadがスナップショットを引数で受け取るよう変更し、Notify::Usecaseが取得と既読化で同じ値を使うupdated_atにする途中失敗時の保証(従来どおり)
テスト
spec/notify/usecase_spec.crを新しい境界選択に合わせて更新crystal spec59 examples 0 failures /ameba0 failures /crystal build src/main.cr成功動作確認方法
マージ後 dev にデプロイし、次の CI 通知(CheckSuite)が 1 回だけ送信されて既読化されること(再送が続かないこと)を確認する。
Closes #100
🤖 Generated with Claude Code