어떤 버그인가요
UserRemovalScheduler.scheduledUserRemoval이 매일 자정에 탈퇴 후 30일이 지난 사용자를 삭제합니다. 그런데 삭제 목록에서 빠진 테이블들이 외래키로 부모 행을 붙잡고 있어서 DELETE가 거부됩니다. MySQL InnoDB는 해당 제약들이 전부 NO ACTION이라 자식 행이 남아 있으면 부모 행 삭제를 막습니다.
scheduledUserRemoval은 삭제 대상 전원을 트랜잭션 하나로 처리합니다. 예외가 한 번 나면 그날 처리분이 전부 롤백됩니다. 조건에 걸리는 사용자가 한 명이라도 있으면 그날 삭제는 0건입니다.
문제가 되는 외래키
| 남는 행 |
제약 |
거부되는 DELETE |
해당 사용자 |
chat_message |
FK_CHAT_MESSAGE_SENDER_ID (sender_id → chat_participant.id) |
DELETE FROM chat_participant |
채팅 메시지를 보낸 적이 있는 사람 |
chat_room |
fk_chat_room_mentoring_id (mentoring_id → mentoring.id) |
DELETE FROM mentoring |
멘토링 채팅방이 있는 멘티 |
mentoring |
fk_mentoring_mentor_id (mentor_id → mentor.id) |
DELETE FROM mentor |
멘티가 있는 멘토 |
liked_news |
fk_liked_news_news_id (news_id → news.id) |
DELETE FROM news |
남이 좋아요한 뉴스를 쓴 사람 |
user_ban |
fk_user_ban_banned_user_id 외 2건 (→ site_user.id) |
DELETE FROM site_user |
정지 이력이 있거나 정지를 집행한 관리자 |
프로필 이미지가 없는 경우
s3Service.deleteExProfile이 profileImageUrl을 그대로 S3 key로 넘깁니다(UserRemovalScheduler.java:98). 이 값은 nullable이고 SignUpRequest.profileImageUrl에 검증이 없어 null이 될 수 있습니다. null key로 호출하면 이렇게 됩니다.
software.amazon.awssdk.core.exception.SdkClientException
message: Unable to marshall request to JSON: Parameter 'Key' must not be null
SdkException이라 deleteFile의 catch에 걸려 CustomException(S3_CLIENT_EXCEPTION)이 되고, RuntimeException이라 트랜잭션이 롤백됩니다. 외래키를 전부 채워도 남는 별도 경로입니다.
작업 범위
작업 전 정할 것
채팅과 멘토링은 상대방 데이터에도 영향이 갑니다. 아래 두 가지는 구현 전에 합의가 필요합니다.
- 탈퇴자가 보낸
chat_message를 지우면 상대방 화면에서도 대화가 사라집니다. 지울지, is_deleted 처리만 할지
chat_room.mentoring_id를 null로 끊을지, 멘토링 채팅방 자체를 정리할지
어떤 버그인가요
UserRemovalScheduler.scheduledUserRemoval이 매일 자정에 탈퇴 후 30일이 지난 사용자를 삭제합니다. 그런데 삭제 목록에서 빠진 테이블들이 외래키로 부모 행을 붙잡고 있어서DELETE가 거부됩니다. MySQL InnoDB는 해당 제약들이 전부NO ACTION이라 자식 행이 남아 있으면 부모 행 삭제를 막습니다.scheduledUserRemoval은 삭제 대상 전원을 트랜잭션 하나로 처리합니다. 예외가 한 번 나면 그날 처리분이 전부 롤백됩니다. 조건에 걸리는 사용자가 한 명이라도 있으면 그날 삭제는 0건입니다.문제가 되는 외래키
chat_messageFK_CHAT_MESSAGE_SENDER_ID(sender_id→chat_participant.id)DELETE FROM chat_participantchat_roomfk_chat_room_mentoring_id(mentoring_id→mentoring.id)DELETE FROM mentoringmentoringfk_mentoring_mentor_id(mentor_id→mentor.id)DELETE FROM mentorliked_newsfk_liked_news_news_id(news_id→news.id)DELETE FROM newsuser_banfk_user_ban_banned_user_id외 2건 (→site_user.id)DELETE FROM site_user프로필 이미지가 없는 경우
s3Service.deleteExProfile이profileImageUrl을 그대로 S3 key로 넘깁니다(UserRemovalScheduler.java:98). 이 값은 nullable이고SignUpRequest.profileImageUrl에 검증이 없어 null이 될 수 있습니다. null key로 호출하면 이렇게 됩니다.SdkException이라deleteFile의 catch에 걸려CustomException(S3_CLIENT_EXCEPTION)이 되고,RuntimeException이라 트랜잭션이 롤백됩니다. 외래키를 전부 채워도 남는 별도 경로입니다.작업 범위
chat_message삭제 추가mentoring삭제 추가 (현재는deleteAllByMenteeId만 있음)chat_room.mentoring_id처리liked_news삭제 추가 (현재는deleteAllBySiteUserId만 있음)user_ban삭제 추가S3Service.deleteExProfilenull 가드작업 전 정할 것
채팅과 멘토링은 상대방 데이터에도 영향이 갑니다. 아래 두 가지는 구현 전에 합의가 필요합니다.
chat_message를 지우면 상대방 화면에서도 대화가 사라집니다. 지울지,is_deleted처리만 할지chat_room.mentoring_id를 null로 끊을지, 멘토링 채팅방 자체를 정리할지