Skip to content

Commit

Permalink
Refactor: 대기열 시스템 로직을 개선한다. (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
hseong3243 authored Aug 29, 2024
2 parents 64c10ca + 8d02c23 commit 787679e
Show file tree
Hide file tree
Showing 28 changed files with 218 additions and 424 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -13,6 +14,13 @@
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse<Void>> handleAllExceptions(Exception e) {
log.error("예측하지 못한 예외 발생. 메시지={}", e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR));
}

@ExceptionHandler(TicketingException.class)
public ResponseEntity<ErrorResponse<Void>> handleTicketingException(TicketingException e) {
ErrorCode errorCode = e.getErrorCode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
public class SeatController {
private final SeatService seatService;

@Waiting
@GetMapping("/performances/{performanceId}/zones/{zoneId}/seats")
public ResponseEntity<ItemResult<SeatElement>> getSeats(@PathVariable("zoneId") long zoneId) {
ItemResult<SeatElement> seats = seatService.getSeats(zoneId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.thirdparty.ticketing.domain.ticket.dto.response.TicketElement;
import com.thirdparty.ticketing.domain.ticket.service.ReservationService;
import com.thirdparty.ticketing.domain.ticket.service.TicketService;
import com.thirdparty.ticketing.domain.waitingsystem.Waiting;

import lombok.RequiredArgsConstructor;

Expand All @@ -41,6 +42,7 @@ public ResponseEntity<Void> releaseSeat(
return ResponseEntity.ok().build();
}

@Waiting
@PostMapping("/seats/select")
public ResponseEntity<Void> selectSeat(
@LoginMember String memberEmail,
Expand All @@ -49,6 +51,7 @@ public ResponseEntity<Void> selectSeat(
return ResponseEntity.ok().build();
}

@Waiting
@PostMapping("/tickets")
public ResponseEntity<Void> reservationTicket(
@LoginMember String memberEmail,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
public class DebounceAspect {

private static final String DEBOUNCE_KEY = "debounce_performance:";
public static final int DEBOUNCE_TIME = 5;

private final ValueOperations<String, String> debounce;

Expand All @@ -40,7 +41,8 @@ public Object debounce(ProceedingJoinPoint joinPoint) throws Throwable {
performanceId = (long) arg;
}
}
if (debounce.setIfAbsent(getDebounceKey(performanceId), "debounce", 10, TimeUnit.SECONDS)) {
if (debounce.setIfAbsent(
getDebounceKey(performanceId), "debounce", DEBOUNCE_TIME, TimeUnit.SECONDS)) {
log.info("[waiting] 디바운스 요청 실행. 공연 ID={}", performanceId);
return joinPoint.proceed();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package com.thirdparty.ticketing.domain.waitingsystem;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.thirdparty.ticketing.domain.common.EventPublisher;
import com.thirdparty.ticketing.domain.waitingsystem.running.RunningManager;
import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingManager;
import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingMember;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class WaitingSystem {

private final Map<Long, PollingEvent> pollingEventCache = new ConcurrentHashMap<>();
private final WaitingManager waitingManager;
private final RunningManager runningManager;
private final EventPublisher eventPublisher;
Expand All @@ -25,10 +27,12 @@ public void enterWaitingRoom(String email, long performanceId) {
}

public long getRemainingCount(String email, long performanceId) {
WaitingMember waitingMember = waitingManager.findWaitingMember(email, performanceId);
long memberWaitingCount = waitingManager.getMemberWaitingCount(email, performanceId);
long runningCount = runningManager.getRunningCount(performanceId);
long remainingCount = waitingMember.getWaitingCount() - runningCount;
eventPublisher.publish(new PollingEvent(performanceId));
long remainingCount = memberWaitingCount - runningCount;
PollingEvent pollingEvent =
pollingEventCache.computeIfAbsent(performanceId, PollingEvent::new);
eventPublisher.publish(pollingEvent);
if (remainingCount <= 0) {
eventPublisher.publish(new LastPollingEvent(email, performanceId));
}
Expand All @@ -39,9 +43,8 @@ public void moveUserToRunning(long performanceId) {
Set<String> removeMemberEmails = runningManager.removeExpiredMemberInfo(performanceId);
waitingManager.removeMemberInfo(removeMemberEmails, performanceId);
long availableToRunning = runningManager.getAvailableToRunning(performanceId);
Set<WaitingMember> waitingMembers =
waitingManager.pullOutMembers(performanceId, availableToRunning);
runningManager.enterRunningRoom(performanceId, waitingMembers);
Set<String> emails = waitingManager.pullOutMemberEmails(performanceId, availableToRunning);
runningManager.enterRunningRoom(performanceId, emails);
}

public void pullOutRunningMember(String email, long performanceId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

import java.util.Set;

import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingMember;

public interface RunningManager {
boolean isReadyToHandle(String email, long performanceId);

long getRunningCount(long performanceId);

long getAvailableToRunning(long performanceId);

void enterRunningRoom(long performanceId, Set<WaitingMember> waitingMembers);
void enterRunningRoom(long performanceId, Set<String> emails);

void pullOutRunningMember(String email, long performanceId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
public interface WaitingManager {
void enterWaitingRoom(String email, long performanceId);

WaitingMember findWaitingMember(String email, long performanceId);

Set<WaitingMember> pullOutMembers(long performanceId, long availableToRunning);

void removeMemberInfo(String email, long performanceId);

void removeMemberInfo(Set<String> emails, long performanceId);

long getMemberWaitingCount(String email, long performanceId);

Set<String> pullOutMemberEmails(long performanceId, long availableToRunning);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thirdparty.ticketing.domain.common.EventPublisher;
import com.thirdparty.ticketing.domain.waitingsystem.WaitingAspect;
import com.thirdparty.ticketing.domain.waitingsystem.WaitingSystem;
Expand Down Expand Up @@ -43,15 +42,13 @@ public WaitingManager waitingManager(
}

@Bean
public RedisWaitingRoom waitingRoom(
StringRedisTemplate redisTemplate, ObjectMapper objectMapper) {
return new RedisWaitingRoom(redisTemplate, objectMapper);
public RedisWaitingRoom waitingRoom(StringRedisTemplate redisTemplate) {
return new RedisWaitingRoom(redisTemplate);
}

@Bean
public RedisWaitingLine waitingLine(
StringRedisTemplate redisTemplate, ObjectMapper objectMapper) {
return new RedisWaitingLine(redisTemplate, objectMapper);
public RedisWaitingLine waitingLine(StringRedisTemplate redisTemplate) {
return new RedisWaitingLine(redisTemplate);
}

@Bean
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.util.Set;

import com.thirdparty.ticketing.domain.waitingsystem.running.RunningManager;
import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingMember;

import lombok.RequiredArgsConstructor;

Expand All @@ -29,10 +28,9 @@ public long getAvailableToRunning(long performanceId) {
}

@Override
public void enterRunningRoom(long performanceId, Set<WaitingMember> waitingMembers) {
waitingMembers.forEach(WaitingMember::enter);
runningCounter.increment(performanceId, waitingMembers.size());
runningRoom.enter(performanceId, waitingMembers);
public void enterRunningRoom(long performanceId, Set<String> emails) {
runningCounter.increment(performanceId, emails.size());
runningRoom.enter(performanceId, emails);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.stream.Collectors;

import com.thirdparty.ticketing.domain.waitingsystem.running.RunningRoom;
import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingMember;

import lombok.RequiredArgsConstructor;

Expand All @@ -18,7 +17,7 @@ public class MemoryRunningRoom implements RunningRoom {
private static final int MAX_MEMORY_RUNNING_ROOM_SIZE = 100;
private static final int EXPIRED_MINUTE = 5;

private final ConcurrentMap<Long, ConcurrentMap<String, WaitingMember>> room;
private final ConcurrentMap<Long, ConcurrentMap<String, ZonedDateTime>> room;

public boolean contains(String email, long performanceId) {
if (!room.containsKey(performanceId)) {
Expand All @@ -34,13 +33,14 @@ public long getAvailableToRunning(long performanceId) {
- (room.containsKey(performanceId) ? room.get(performanceId).size() : 0));
}

public void enter(long performanceId, Set<WaitingMember> waitingMembers) {
public void enter(long performanceId, Set<String> emails) {
room.compute(
performanceId,
(key, room) -> {
ConcurrentMap<String, WaitingMember> runningRoom =
ConcurrentMap<String, ZonedDateTime> runningRoom =
(room != null) ? room : new ConcurrentHashMap<>();
waitingMembers.forEach(member -> runningRoom.put(member.getEmail(), member));
emails.forEach(
email -> runningRoom.put(email, ZonedDateTime.now().plusSeconds(30)));
return runningRoom;
});
}
Expand All @@ -55,15 +55,15 @@ public void pullOutRunningMember(String email, long performanceId) {
}

public Set<String> removeExpiredMemberInfo(long performanceId) {
ConcurrentMap<String, WaitingMember> performanceRoom = room.get(performanceId);
ConcurrentMap<String, ZonedDateTime> performanceRoom = room.get(performanceId);
if (performanceRoom == null) {
return Set.of();
}

ZonedDateTime fiveMinutesAgo = ZonedDateTime.now().minusMinutes(EXPIRED_MINUTE);
ZonedDateTime now = ZonedDateTime.now();
Set<String> removeMemberEmails =
performanceRoom.entrySet().stream()
.filter(entry -> entry.getValue().getEnteredAt().isBefore(fiveMinutesAgo))
.filter(entry -> entry.getValue().isBefore(now))
.map(Entry::getKey)
.collect(Collectors.toSet());
removeMemberEmails.forEach(performanceRoom::remove);
Expand All @@ -74,14 +74,7 @@ public void updateRunningMemberExpiredTime(String email, long performanceId) {
room.computeIfPresent(
performanceId,
(key, room) -> {
room.computeIfPresent(
email,
(k, waitingMember) ->
new WaitingMember(
email,
performanceId,
waitingMember.getWaitingCount(),
ZonedDateTime.now().plusMinutes(5)));
room.put(email, ZonedDateTime.now().plusMinutes(EXPIRED_MINUTE));
return room;
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.time.ZonedDateTime;
import java.util.Set;
import java.util.stream.Collectors;

import com.thirdparty.ticketing.domain.common.ErrorCode;
import com.thirdparty.ticketing.domain.common.TicketingException;
Expand All @@ -28,18 +29,12 @@ public void enterWaitingRoom(String email, long performanceId) {
}
}

@Override
public WaitingMember findWaitingMember(String email, long performanceId) {
return waitingRoom
.findWaitingMember(email, performanceId)
.orElseThrow(() -> new TicketingException(ErrorCode.NOT_FOUND_WAITING_MEMBER));
}

@Override
public Set<WaitingMember> pullOutMembers(long performanceId, long availableToRunning) {
return waitingLine.pullOutMembers(performanceId, availableToRunning);
}

@Override
public void removeMemberInfo(String email, long performanceId) {
waitingRoom.removeMemberInfo(email, performanceId);
Expand All @@ -49,4 +44,16 @@ public void removeMemberInfo(String email, long performanceId) {
public void removeMemberInfo(Set<String> emails, long performanceId) {
waitingRoom.removeMemberInfo(emails, performanceId);
}

@Override
public long getMemberWaitingCount(String email, long performanceId) {
return waitingRoom.getMemberWaitingCount(email, performanceId);
}

@Override
public Set<String> pullOutMemberEmails(long performanceId, long availableToRunning) {
return waitingLine.pullOutMembers(performanceId, availableToRunning).stream()
.map(WaitingMember::getEmail)
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.thirdparty.ticketing.domain.common.ErrorCode;
import com.thirdparty.ticketing.domain.common.TicketingException;
import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingMember;
import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingRoom;

Expand Down Expand Up @@ -47,4 +49,10 @@ public void removeMemberInfo(Set<String> emails, long performanceId) {
return room;
});
}

public long getMemberWaitingCount(String email, long performanceId) {
return findWaitingMember(email, performanceId)
.map(WaitingMember::getWaitingCount)
.orElseThrow(() -> new TicketingException(ErrorCode.NOT_FOUND_WAITING_MEMBER));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.util.Set;

import com.thirdparty.ticketing.domain.waitingsystem.running.RunningManager;
import com.thirdparty.ticketing.domain.waitingsystem.waiting.WaitingMember;

import lombok.RequiredArgsConstructor;

Expand All @@ -30,9 +29,9 @@ public long getAvailableToRunning(long performanceId) {
}

@Override
public void enterRunningRoom(long performanceId, Set<WaitingMember> waitingMembers) {
runningRoom.enter(performanceId, waitingMembers);
runningCounter.increment(performanceId, waitingMembers.size());
public void enterRunningRoom(long performanceId, Set<String> emails) {
runningCounter.increment(performanceId, emails.size());
runningRoom.enter(performanceId, emails);
}

@Override
Expand Down
Loading

0 comments on commit 787679e

Please sign in to comment.