Skip to content

fix: Discord 分割送信の途中失敗時の重複投稿を防ぐ (#94)#97

Merged
limit7412 merged 4 commits into
masterfrom
fix/discord-chunked-read-atomicity
Jul 14, 2026
Merged

fix: Discord 分割送信の途中失敗時の重複投稿を防ぐ (#94)#97
limit7412 merged 4 commits into
masterfrom
fix/discord-chunked-read-atomicity

Conversation

@limit7412

Copy link
Copy Markdown
Owner

概要

issue #94 を解決する。Discord モードで通知が複数の webhook 投稿に分割される際、分割送信の途中で失敗すると前半チャンクが送信済みなのに既読化されず、次回実行で重複投稿されうる問題を修正する。

issue で検討された チャンク単位の last_read_at 付き一括既読化 を採用した(スレッド単位既読化の N+1 懸念を避けつつ、既読化コールを送信コールと同数に抑えられる案)。

変更内容

  1. 通知を updated_at 昇順にソートしてから送信 (Notify::Usecase)
  2. 各チャンクの送信成功ごとに送信済み分だけを既読化
    • Notify::PostRepository#send_messages を、チャンク送信成功ごとに累計送信件数を yield するブロック付き I/F に変更
      • Slack: 全 attachments を 1 投稿で送る atomic 実装のため、送信後に一度だけ全件を yield
      • Discord: webhook 投稿(チャンク)ごとに累計件数を yield
    • Notify::Usecase が yield された件数から last_read_at を算出し PUT /notifications を都度呼ぶ
  3. GitHub::NotificationRepository#notification_to_readlast_read_at パラメータを追加
    • 指定時刻以前に更新された通知だけを既読化する
  4. Github::Notificationupdated_at を追加

途中失敗時の挙動

チャンク送信が途中で失敗しても、送信済みチャンクまでは last_read_at で既読化済みのため、未送信分だけが次回実行で再取得される。前半チャンクの重複投稿が原理的に発生しない。

同一タイムスタンプの境界考慮

issue の注意点どおり、未送信の先頭通知と同一 updated_at の通知まで巻き込んで既読化しないよう、last_read_at を「未送信先頭より前の最大 updated_at」に丸めている。安全に前進できる位置が無い(送信済みが全て未送信先頭と同時刻)稀なケースはスキップし、次回実行に委ねる(通知ロストより稀な重複を許容)。

副次効果

現行のパラメータなし PUT /notifications(現在時刻までの全既読化)にあった「fetch 後〜既読化までに届いた新規通知を通知せず既読化する窓」も、last_read_at 方式により狭まる。

テスト

  • spec/notify/usecase_spec.cr を新規追加し、全チャンク成功 / 途中失敗 / 同一タイムスタンプ境界 / 通知ゼロ件の既読化挙動を検証
  • crystal spec 31 examples 0 failures / ameba 0 failures / crystal build src/main.cr 成功

Closes #94

🤖 Generated with Claude Code

チャンク単位の last_read_at 付き一括既読化を導入。通知を updated_at 昇順に
ソートし、各チャンクの送信成功ごとに送信済み分だけを PUT /notifications
(last_read_at) で既読化する。途中失敗しても送信済み分は既読化済みのため、
未送信分だけが次回再取得され前半チャンクが重複投稿されない。

- Github::Notification に updated_at を追加
- notification_to_read に last_read_at パラメータを追加
- Notify::PostRepository#send_messages をチャンク送信成功ごとに累計件数を
  yield するブロック付き I/F に変更(Slack は atomic なので一度だけ yield)
- Notify::Usecase が未送信先頭と同時刻の通知を巻き込まないよう last_read_at を
  境界手前に丸める
- Notify::Usecase の既読化ロジックを検証する spec を追加

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements chunk-by-chunk notification marking as read to prevent duplicate posts in case of partial failures during delivery. It introduces the updated_at field to GitHub notifications, sorts notifications chronologically, and updates the post repositories to yield cumulative sent counts so that the use case can safely advance the last_read_at timestamp. Feedback suggests adding a Content-Type: application/json header to the GitHub API request to ensure the JSON body is parsed correctly, and defensively clamping the sent count to prevent potential out-of-bounds index errors.

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.

Comment thread src/github/repository.cr Outdated
Comment thread src/notify/usecase.cr
- notification_to_read の PUT /notifications に Content-Type: application/json
  を明示。無いと GitHub 側でボディが解析されず last_read_at が無視される恐れ
- mark_read_through で sent_count を通知件数でクランプし、poster 実装(外部
  境界)から想定外の値が渡っても安全にスライスできるようにする

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e68a9d3203

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/notify/usecase.cr
Comment thread src/github/repository.cr Outdated
/notifications は既定で 1 ページ分しか返さないため、一部ページのみ取得した
状態で last_read_at 既読化を行うと、未取得の古い通知(更新時刻が小さく
チャンク送信対象外)まで last_read_at 以前として既読化され、未送信のまま
消える恐れがあった。全ページ取得してから境界を決めるよう修正する。

- find_notifications_unread を per_page=100 で全ページ取得に変更
- 途中ページの一時失敗(5xx/401)時は不完全な取得での既読化を避けるため
  その実行を丸ごとスキップし次回に委ねる
- 暴走防止に MAX_PAGES=20 の上限を設け、到達時はログを残す

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b061d9510

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/github/repository.cr Outdated
Comment thread src/github/repository.cr Outdated
Comment thread src/github/repository.cr Outdated
- ページ数上限(MAX_PAGES)到達で取得しきれない場合は不完全取得としてその実行を
  スキップ。未取得の古い通知を last_read_at で巻き込み既読化するのを防ぐ
- ページング中の新着によるオフセットずれで通知が抜けるのを防ぐため、取得開始
  時刻を before に固定してページ集合を安定させる
- notification_to_read が PUT のレスポンスを検証し、既読化失敗時は例外にして
  後続チャンクの送信を止める(重複防止は既読化成功に依存するため)
- 既読化失敗時に送信を止めることを検証する spec を追加

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@limit7412 limit7412 merged commit 9f97e02 into master Jul 14, 2026
2 checks passed
@limit7412 limit7412 deleted the fix/discord-chunked-read-atomicity branch July 14, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discord 分割送信の途中失敗時に通知が重複投稿されうる(既読化の atomicity)

1 participant