Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 회원 탈퇴, 약속방 나가기 TaskScheduler 예약된 작업 취소 처리 #654

Open
2 tasks done
eun-byeol opened this issue Oct 3, 2024 · 0 comments · May be fixed by #716 or #733
Open
2 tasks done

feat: 회원 탈퇴, 약속방 나가기 TaskScheduler 예약된 작업 취소 처리 #654

eun-byeol opened this issue Oct 3, 2024 · 0 comments · May be fixed by #716 or #733
Assignees

Comments

@eun-byeol
Copy link
Contributor

eun-byeol commented Oct 3, 2024

📝 작업 내용

  • 회원 탈퇴 시, fcmTopic 구독 취소 -> 탈퇴한 회원이 알림을 받는 문제 해결
  • 회원 탈퇴 시, 회원의 출발 알림 스케줄링 취소 -> 회원 탈퇴시 출발 알림이 다른 사람에게 전송되는 문제 해결
  • 약속방 나가기 기능이 없어서, 탈퇴만 고려했어요.

탈퇴한 회원의 출발 알림 스케줄링만 취소하는 이유

  • 현재 스케줄링은(eta notice, departure reminder) 모두에게 발송되는 메시지(GroupMessage) 예약만 걸고 있어요.
  • 따라서, 예약 메시지 스케줄링을 취소하는 경우는 2가지 입니다.
    (1) eta notice 취소 - 조건 : eta 오픈 시간 전 + 참여자 0명 <- 알림 DB에 저장X
    (2) departure reminder 취소 - 조건 : 출발알림 시간 전 + 해당 회원의 탈퇴 <- 알림이 DB에 저장O

(1) 스케줄링 취소 구현하지 않는 이유(일단보류..)

  • 회원 탈퇴 시, fcmTopic 구독 해지하기 때문에 불필요한 알림이 가는 문제는 없어요.
  • 스프링이 꺼지면 해당 다시 스케줄링 되지 않고 있어요.(이게 맞나?)
  • 지금 구조에선 eta 오픈 전 모든 참여자가 탈퇴하는 경우가 발생할 순 있지만, 특수한 경우라고 판단했어요.
    Q. 다들 어떻게 생각하시나요?

(2) 구현 방법

1. 예약된 스케줄링 취소

  • taskScheduler.schedule() 후 반환되는 ScheduledFuture 객체 저장
  • scheduledFuture.cancel()로 취소 가능
  • 저장을 위해 캐싱 사용

장점: 스케줄링 낭비 리소스 제거 가능, DB를 거치지 않고 작업 가능
단점: 취소를 위해 객체를 들고 있어야 함. 스케줄링 취소가 누락되면 알림이 갈 수 있음

MessageScheduler
package com.ody.meeting.service;

import com.ody.notification.domain.message.GroupMessage;
import com.ody.notification.service.FcmPushSender;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MessageScheduler {

    private static final Map<GroupMessage, ScheduledFuture<?>> SCHEDULED_GROUP_MESSAGES = new ConcurrentHashMap<>();

    private final TaskScheduler taskScheduler;
    private final FcmPushSender fcmPushSender;

    public void scheduleGroupMessage(GroupMessage groupMessage, Instant startTime) {
        ScheduledFuture<?> scheduled = taskScheduler.schedule(
                () -> fcmPushSender.sendNoticeMessage(groupMessage), startTime
        );
        SCHEDULED_GROUP_MESSAGES.put(groupMessage, scheduled);
    }

    public int cancelAllGroupMessages(long meetingId) {
        List<GroupMessage> groupMessages = SCHEDULED_GROUP_MESSAGES.keySet().stream()
                .filter(groupMessage -> groupMessage.meetingId() == meetingId)
                .toList();

        groupMessages.forEach(groupMessage -> SCHEDULED_GROUP_MESSAGES.remove(groupMessage).cancel(true));

        log.info("스케줄링 취소 - meetingId={}의 {}개 그룹 메시지 스케줄링 취소", meetingId, groupMessages.size());
        return groupMessages.size();
    }
}
  1. fcm 알림 보낼 때, Notification Status=DISMISSED 이면 발송 하지 않기
    장점: 스케줄링 객체를 저장하지 않고 있어도 됨. 간단함
    단점: 푸시 알림을 보낼 때마다 DB를 조회해야 함

참고) noti 로직이 헷갈려서 정리했어요

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment