From f4cded7fb5dedfe27f736cdddaabbac310bdd0a4 Mon Sep 17 00:00:00 2001 From: Donghoon Seo Date: Tue, 18 Aug 2026 18:46:13 +0900 Subject: [PATCH] =?UTF-8?q?bug:=20=EA=B5=AC=EA=B8=80=20=EC=BA=98=EB=A6=B0?= =?UTF-8?q?=EB=8D=94=20=EC=82=AD=EC=A0=9C=20=EC=9D=BC=EC=A0=95=20=EB=8F=99?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EB=88=84=EB=9D=BD=20=EB=B0=8F=20=EA=B3=B5?= =?UTF-8?q?=ED=9C=B4=EC=9D=BC=20=EC=A4=91=EB=B3=B5=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/service/GoogleCalendarClient.java | 12 ++++++------ .../migration/V20__add_unique_index_for_holiday.sql | 9 +++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/db/migration/V20__add_unique_index_for_holiday.sql diff --git a/src/main/java/com/tryna/domain/external/service/GoogleCalendarClient.java b/src/main/java/com/tryna/domain/external/service/GoogleCalendarClient.java index 0184f68..98530df 100644 --- a/src/main/java/com/tryna/domain/external/service/GoogleCalendarClient.java +++ b/src/main/java/com/tryna/domain/external/service/GoogleCalendarClient.java @@ -39,19 +39,19 @@ public Map fetchEvents(String accessToken, LocalDateTime lastSyn headers.setBearerAuth(accessToken); HttpEntity request = new HttpEntity<>(headers); - String uriTemplate = calendarApiUrl + "?timeMin={timeMin}&timeMax={timeMax}&singleEvents=true&showDeleted=true"; + String uriTemplate = calendarApiUrl + "?singleEvents=true&showDeleted=true"; List uriVariables = new java.util.ArrayList<>(); - uriVariables.add(timeMin.toInstant().toString()); - uriVariables.add(timeMax.toInstant().toString()); if (lastSyncedAt != null) { - // 마지막 동기화 시간 이후에 변경된 데이터만 요청 (updatedMin) + // 증분 동기화 시 timeMin/timeMax 필터를 제외해야 구글이 삭제된(cancelled) 일정을 보내줍니다. String updatedMin = lastSyncedAt.atZone(ZoneId.of("Asia/Seoul")).toInstant().toString(); uriTemplate += "&updatedMin={updatedMin}"; uriVariables.add(updatedMin); } else { - // 최초 동기화 - uriTemplate += "&orderBy=startTime"; + // 최초 Full 동기화 시에는 해당 연도 데이터만 가져오도록 범위 지정 + uriTemplate += "&timeMin={timeMin}&timeMax={timeMax}&orderBy=startTime"; + uriVariables.add(timeMin.toInstant().toString()); + uriVariables.add(timeMax.toInstant().toString()); } // 페이지네이션 토큰이 존재하면 쿼리 파라미터 추가 diff --git a/src/main/resources/db/migration/V20__add_unique_index_for_holiday.sql b/src/main/resources/db/migration/V20__add_unique_index_for_holiday.sql new file mode 100644 index 0000000..976b910 --- /dev/null +++ b/src/main/resources/db/migration/V20__add_unique_index_for_holiday.sql @@ -0,0 +1,9 @@ +-- V20: 공휴일 다중 서버 동시성(Race Condition) 중복 삽입 방어 로직 추가 + +-- 1. 중복된 데이터가 남아있으면 Unique Index 생성이 실패하므로, 기존 공휴일 데이터를 깔끔하게 초기화 +DELETE FROM events WHERE source_type = 'HOLIDAY'; + +-- 2. DB 레벨에서 '동일한 외부 공휴일 ID'가 2번 들어오는 것을 원천 차단하는 방어막 생성 +CREATE UNIQUE INDEX uq_events_holiday_external_id + ON events (external_event_id) + WHERE source_type = 'HOLIDAY'; \ No newline at end of file