Fix/#54 mark processed on lazy trip plan creation#55
Conversation
📝 WalkthroughWalkthrough
Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 분 Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@linktrip-application/src/main/kotlin/com/linktrip/application/domain/trip/TripPlanService.kt`:
- Around line 71-74: The current registerRequest() leaves some requests
unprocessed when an existing TripPlan exists because the unprocessed-request
cleanup (requestPort.findUnprocessedByVideoAnalysisTaskId(...).filter {
it.memberId == memberId }.onEach { it.markProcessed() } and
requestPort.saveAll(...)) runs only after creating a new TripPlan; move that
retrieval/mark-and-save block to execute before the early-return that checks for
an existing TripPlan so cleanup always runs regardless of whether a new plan is
created, keeping the same calls to markProcessed() and saveAll(...) and using
the same memberId/videoAnalysisTaskId filtering.
- Around line 71-74: Ktlint is failing because the chained call after the
assignment in TripPlanService (the expression starting with
requestPort.findUnprocessedByVideoAnalysisTaskId(...)) begins on the same line
as the `=`; split the expression into a multiline assignment so the chain starts
on the next line, e.g. place the call to
requestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId) on a new
indented line and keep subsequent .filter { ... } and .onEach { ... } each on
their own lines, then call requestPort.saveAll(unprocessedRequests); this fixes
the line-break rule without changing logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ffcb5390-f46e-4c52-830e-43264711243e
📒 Files selected for processing (6)
linktrip-application/src/main/kotlin/com/linktrip/application/domain/trip/TripPlanRequest.ktlinktrip-application/src/main/kotlin/com/linktrip/application/domain/trip/TripPlanService.ktlinktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.ktlinktrip-application/src/main/kotlin/com/linktrip/application/port/output/persistence/TripPlanRequestPersistencePort.ktlinktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/TripPlanRequestPersistenceAdapter.ktlinktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TripPlanRequestQuerydslRepository.kt
📜 Review details
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2026-03-18T01:08:05.661Z
Learnt from: toychip
Repo: Link-Trip/BackEnd PR: 24
File: linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeVideoPersistenceAdapter.kt:16-28
Timestamp: 2026-03-18T01:08:05.661Z
Learning: Similarly, `linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeChannelPersistenceAdapter.kt` likely has the same deferred concurrent-write policy (single server, ShedLock planned for replication phase).
Applied to files:
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TripPlanRequestQuerydslRepository.kt
📚 Learning: 2026-03-18T01:07:53.575Z
Learnt from: toychip
Repo: Link-Trip/BackEnd PR: 24
File: linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeVideoPersistenceAdapter.kt:16-28
Timestamp: 2026-03-18T01:07:53.575Z
Learning: Apply the same deferred concurrent-write policy to all Kotlin persistence adapters in the MySQL adapter package: ensure writes are coordinated on a single server with ShedLock considered for the replication phase. For YouTubeVideoPersistenceAdapter.kt (and other adapters in this directory), verify that concurrent writes are serialized or properly guarded, document the policy in code comments, and ensure CI checks or deployment gating will catch any regression where multiple instances might attempt a conflicting write during replication.
Applied to files:
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/TripPlanRequestPersistenceAdapter.kt
📚 Learning: 2026-03-18T01:08:05.661Z
Learnt from: toychip
Repo: Link-Trip/BackEnd PR: 24
File: linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeVideoPersistenceAdapter.kt:16-28
Timestamp: 2026-03-18T01:08:05.661Z
Learning: In `linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/YouTubeVideoPersistenceAdapter.kt`, the concurrent-write safety (duplicate videoId in batch, race condition on uk_youtube_video_video_id) is intentionally deferred. The maintainer (toychip) confirmed the system is currently single-server, so this is not a concern yet. When replication is introduced in the future, ShedLock will be used for distributed locking to address this. At that point, in-batch videoId deduplication should also be applied.
Applied to files:
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/TripPlanRequestPersistenceAdapter.kt
🪛 GitHub Actions: CI - Pull request
linktrip-application/src/main/kotlin/com/linktrip/application/domain/trip/TripPlanService.kt
[error] 71-71: ktlintMainSourceSetCheck failed (ktlint). Violation at :71:35: "A multiline expression should start on a new line".
🔇 Additional comments (4)
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/repository/TripPlanRequestQuerydslRepository.kt (1)
33-33: 변경 사항 이상 없습니다.Line 33의 개행 추가만 있으며, 동작/쿼리 로직 영향은 없습니다.
linktrip-application/src/main/kotlin/com/linktrip/application/domain/trip/TripPlanRequest.kt (1)
14-16: 상태 전이를 도메인 메서드로 모은 방향 좋습니다.호출부가
processed플래그를 직접 건드리지 않고 의도를 드러내게 돼서 읽기 쉬워졌습니다.linktrip-application/src/main/kotlin/com/linktrip/application/domain/video/VideoAnalyzeEventListener.kt (1)
92-92:markProcessed()를 바로 쓰도록 정리한 점 좋습니다.보조 컬렉션 없이 요청 객체 상태로 흐름을 맞춰서 처리/로그 계산이 더 단순해졌습니다.
linktrip-output-persistence/mysql/src/main/kotlin/com/linktrip/output/persistence/mysql/adapter/TripPlanRequestPersistenceAdapter.kt (1)
30-31: 부분 엔티티 스냅샷으로 인한 감사 상태 덮어쓰기 우려는 현재 구현에서 발생하지 않습니다.
TripPlanRequestEntity의 설계가 이미 이 문제를 방지하고 있습니다:
createdAt:@Column(updatable = false)지정으로 JPA가 MERGE 중 업데이트하지 않음updatedAt:@LastModifiedDate데코레이터가 자동 관리deleted:protected set지정으로 외부에서 직접 수정 불가
jpaRepository.saveAll(requests.map { TripPlanRequestEntity.from(it) })을 호출해도 감사 필드와 soft-delete 상태는 보호됩니다. 부분 엔티티로 MERGE될 때 JPA는 명시적으로 채워진 필드(id,memberId,videoAnalysisTaskId,processed)만 적용하고 감사 필드는 무시합니다.
| val unprocessedRequests = requestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId) | ||
| .filter { it.memberId == memberId } | ||
| .onEach { it.markProcessed() } | ||
| requestPort.saveAll(unprocessedRequests) |
There was a problem hiding this comment.
요청 정리를 새 플랜 생성 뒤에만 두면 미처리 요청이 남습니다.
Lines 71-74는 새 TripPlan을 만든 경우에만 실행됩니다. 그런데 registerRequest()는 기존 플랜 존재 여부를 보지 않고 요청을 저장하므로(Lines 29-35), 이미 플랜이 있는 상태에서는 Lines 45-47에서 바로 반환되어 processed=false가 그대로 남습니다. 멤버+task 기준 요청 조회/정리를 early-return 앞쪽으로 옮겨서 기존 플랜 경로도 같이 정리해주세요.
🧰 Tools
🪛 GitHub Actions: CI - Pull request
[error] 71-71: ktlintMainSourceSetCheck failed (ktlint). Violation at :71:35: "A multiline expression should start on a new line".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@linktrip-application/src/main/kotlin/com/linktrip/application/domain/trip/TripPlanService.kt`
around lines 71 - 74, The current registerRequest() leaves some requests
unprocessed when an existing TripPlan exists because the unprocessed-request
cleanup (requestPort.findUnprocessedByVideoAnalysisTaskId(...).filter {
it.memberId == memberId }.onEach { it.markProcessed() } and
requestPort.saveAll(...)) runs only after creating a new TripPlan; move that
retrieval/mark-and-save block to execute before the early-return that checks for
an existing TripPlan so cleanup always runs regardless of whether a new plan is
created, keeping the same calls to markProcessed() and saveAll(...) and using
the same memberId/videoAnalysisTaskId filtering.
Ktlint가 Line 71에서 깨집니다.
= 뒤 체이닝이 같은 줄에서 시작해서 CI가 실패합니다. multiline expression 형태로 줄바꿈만 맞추면 됩니다.
예시 수정
- val unprocessedRequests = requestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId)
- .filter { it.memberId == memberId }
- .onEach { it.markProcessed() }
+ val unprocessedRequests =
+ requestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId)
+ .filter { it.memberId == memberId }
+ .onEach { it.markProcessed() }
requestPort.saveAll(unprocessedRequests)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| val unprocessedRequests = requestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId) | |
| .filter { it.memberId == memberId } | |
| .onEach { it.markProcessed() } | |
| requestPort.saveAll(unprocessedRequests) | |
| val unprocessedRequests = | |
| requestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId) | |
| .filter { it.memberId == memberId } | |
| .onEach { it.markProcessed() } | |
| requestPort.saveAll(unprocessedRequests) |
🧰 Tools
🪛 GitHub Actions: CI - Pull request
[error] 71-71: ktlintMainSourceSetCheck failed (ktlint). Violation at :71:35: "A multiline expression should start on a new line".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@linktrip-application/src/main/kotlin/com/linktrip/application/domain/trip/TripPlanService.kt`
around lines 71 - 74, Ktlint is failing because the chained call after the
assignment in TripPlanService (the expression starting with
requestPort.findUnprocessedByVideoAnalysisTaskId(...)) begins on the same line
as the `=`; split the expression into a multiline assignment so the chain starts
on the next line, e.g. place the call to
requestPort.findUnprocessedByVideoAnalysisTaskId(videoAnalysisTaskId) on a new
indented line and keep subsequent .filter { ... } and .onEach { ... } each on
their own lines, then call requestPort.saveAll(unprocessedRequests); this fixes
the line-break rule without changing logic.
관련 이슈
변경 내용
1. Lazy 생성 경로 processed 미반영 문제 수정
TripPlanService에서 Lazy 생성 이후TripPlanRequest.processed상태를 명시적으로 업데이트하도록 수정videoAnalysisTaskId+memberId조건의 미처리 요청 조회 후 일괄 처리2. 상태 변경 책임을 도메인으로 이동
TripPlanRequest.markProcessed()메서드 추가3. Persistence 구조 개선
markAsProcessed(ids)제거saveAll(requests)기반으로 변경하여도메인 상태 변경 → 저장 흐름으로 일관성 유지
4. EventListener 로직 단순화
processedIds제거markProcessed()수행 후saveAll호출processed상태 기반으로 변경5. 네이밍 개선
markAsProcessed→markBatchProcessed(이전 커밋)체크리스트
Summary by CodeRabbit
릴리스 노트
Refactor
Chores