カレンダー同期をユーザー・日付単位で直列化しorphanイベントを防止 - #67
Merged
Merged
Conversation
同一ユーザー・同一日付の特別シフト申請(または通常シフト)の カレンダー同期が並行実行されると、後発の同期が先発の同期の 作成イベント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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
問題
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 することを確認済み:修正後は全 366 テストがパス。
本番の残留イベントの掃除
このバグで既に残ってしまったイベントは、管理画面の「指定日のカレンダーリセット」(POST /api/calendar/clean-date) で Scratch Day の日付を指定して実行すれば、全削除 → DB から再同期されてきれいになります。
🤖 Generated with Claude Code