Skip to content

カレンダー同期をユーザー・日付単位で直列化しorphanイベントを防止 - #67

Merged
champierre merged 1 commit into
mainfrom
feature/serialize-calendar-sync
Jun 11, 2026
Merged

カレンダー同期をユーザー・日付単位で直列化しorphanイベントを防止#67
champierre merged 1 commit into
mainfrom
feature/serialize-calendar-sync

Conversation

@champierre

@champierre champierre commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

問題

Scratch Day 2026 の特別シフトで、さっしーのシフトが Google カレンダーに大量登録された(「9:30〜17:30」「10:00〜17:30」…と開始時刻が 30 分ずつずれたイベントの山)。一方、DB 上は 9:00-17:30 のひとまとまりの申請のみ。

原因

同一ユーザー・同一日付のカレンダー同期 syncSpecialShiftApplicationsForUserAndDate並行実行されると orphan イベントが残るため。

同期処理は「DB に記録された calendar_event_id のイベントを削除 → マージ済みイベントを新規作成 → 作成したイベント ID を DB に書き戻す」という流れだが、30 分スロットを連続申請すると同期が次々と発火し、後発の同期が読む時点では先発の同期がまだイベント ID を DB に書き戻していない。その結果、後発は先発の作成イベントを削除対象として認識できず、中間状態のマージ済みイベント(X:XX〜17:30)がすべてカレンダーに残留する。

#62 (b1920fd) でレスポンス前に sync を await する修正が入ったが、これはリクエスト単体の完了を待つだけで、複数リクエスト間の並行実行は防げない

修正

CalendarService(同期種別, userId, date) をキーとする Promise キュー (runSerialized) を追加し、同一キーの同期を直列実行するようにした。通常シフトの syncShiftsForUserAndDate も同じ問題を抱えるため同様に直列化。

テスト (TDD)

server/src/__tests__/CalendarServiceSyncSerialization.test.ts を新規追加。修正前は以下の通り fail することを確認済み:

  • 2件並行同期 → カレンダーに 2 イベント残留(期待: 1)
  • 9:00-17:30 の 30 分刻み17スロットを連続申請 → 17 イベント残留(本番の現象と一致)

修正後は全 366 テストがパス。

本番の残留イベントの掃除

このバグで既に残ってしまったイベントは、管理画面の「指定日のカレンダーリセット」(POST /api/calendar/clean-date) で Scratch Day の日付を指定して実行すれば、全削除 → DB から再同期されてきれいになります。

🤖 Generated with Claude Code

同一ユーザー・同一日付の特別シフト申請(または通常シフト)の
カレンダー同期が並行実行されると、後発の同期が先発の同期の
作成イベントID(DB書き込み前)を観測できず、削除されない
orphan イベントが Google Calendar に大量に残る問題を修正。

例: 9:00-17:30 を 30 分刻みで連続申請すると「X:XX〜17:30」の
イベントが最大17件残留(Scratch Day 2026 で実際に発生)。

CalendarService に (同期種別, userId, date) をキーとする
Promise キューを追加し、同一キーの同期を直列実行する。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@champierre
champierre merged commit 7f240d0 into main Jun 11, 2026
4 checks passed
@champierre
champierre deleted the feature/serialize-calendar-sync branch June 11, 2026 14:37
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.

1 participant