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

Feature: 대기열의 사용자를 메모리 작업 가능 공간으로 이동 기능은 동시에 한 번만 실행된다. #99

Merged
merged 9 commits into from
Aug 20, 2024

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

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

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class WaitingAspect {

private final WaitingManager waitingManager;
private final WaitingSystem waitingSystem;

private Object waitingRequest(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request =
Expand All @@ -28,13 +25,11 @@ private Object waitingRequest(ProceedingJoinPoint joinPoint) throws Throwable {

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String email = (String) authentication.getPrincipal();

WaitingMember waitingMember = new WaitingMember(email, performanceId);
if (waitingManager.isReadyToHandle(waitingMember)) {
if (waitingSystem.isReadyToHandle(email, performanceId)) {
return joinPoint.proceed();
} else {
long waitingNumber = waitingManager.enterWaitingRoom(waitingMember);
return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT).body(waitingNumber);
waitingSystem.enterWaitingRoom(email, performanceId);
return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.thirdparty.ticketing.global.waitingsystem.memory;

import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
public class MemoryDebounceAspect {

private final ConcurrentHashMap<Long, DebounceInfo> debounceMap = new ConcurrentHashMap<>();
private static final long DEBOUNCE_MILLIS = 10_000; // 10초

private static class DebounceInfo {
volatile Instant lastExecution;
final ReentrantLock lock;

DebounceInfo() {
this.lastExecution = Instant.EPOCH; // 1970-01-01T00:00:00Z
this.lock = new ReentrantLock();
}
}

@Pointcut("@annotation(com.thirdparty.ticketing.global.waitingsystem.Debounce)")
private void debounceAnnotation() {}
lass9436 marked this conversation as resolved.
Show resolved Hide resolved

@Pointcut(
"execution(public void com.thirdparty.ticketing.domain.waitingsystem.WaitingSystem.moveUserToRunning(long))")
private void moveWaitingMemberToRunning() {}

@Around("debounceAnnotation() || moveWaitingMemberToRunning()")
public Object debounce(ProceedingJoinPoint joinPoint) throws Throwable {
long performanceId = extractPerformanceId(joinPoint);
DebounceInfo info = debounceMap.computeIfAbsent(performanceId, k -> new DebounceInfo());

if (info.lock.tryLock()) {
try {
Instant now = Instant.now();
if (now.isAfter(info.lastExecution.plusMillis(DEBOUNCE_MILLIS))) {
lass9436 marked this conversation as resolved.
Show resolved Hide resolved
info.lastExecution = now;
log.info("[waiting] 디바운스 요청 실행. 공연 ID={}", performanceId);
return joinPoint.proceed();
}
} finally {
info.lock.unlock();
}
}

log.info("[waiting] 디바운스로 인한 요청 무시. 공연 ID={}", performanceId);
return null;
}

private long extractPerformanceId(ProceedingJoinPoint joinPoint) {
for (Object arg : joinPoint.getArgs()) {
if (arg instanceof Long) {
return (Long) arg;
}
}
throw new IllegalArgumentException("메서드 인자에서 공연 ID를 찾을 수 없습니다.");
}
}
Loading
Loading