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: redis 새로운 방식으로 작성 #163

Merged
merged 7 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions .github/workflows/style.yml

This file was deleted.

15 changes: 0 additions & 15 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,6 @@ dependencies {
testImplementation 'org.testcontainers:junit-jupiter:1.20.1'
}

spotless {
java {
target 'src/**/*.java' // 대상 파일 지정
googleJavaFormat().aosp()
importOrder('java', 'javax', 'jakarta', 'org', 'com')
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
}

asciidoctor {
inputs.dir snippetsDir
configurations 'asciidoctorExt'
Expand All @@ -94,10 +83,6 @@ tasks.named('test') {
useJUnitPlatform()
}

tasks.named('spotlessJava') {
dependsOn 'copyYml'
}

tasks.register('copyDocument', Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.thirdparty.ticketing.domain.ticket.service;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;

import com.thirdparty.ticketing.domain.common.ErrorCode;
import com.thirdparty.ticketing.domain.common.TicketingException;
import com.thirdparty.ticketing.domain.member.Member;
import com.thirdparty.ticketing.domain.member.repository.MemberRepository;
import com.thirdparty.ticketing.domain.seat.Seat;
import com.thirdparty.ticketing.domain.seat.repository.SeatRepository;
import com.thirdparty.ticketing.domain.ticket.dto.request.SeatSelectionRequest;
import com.thirdparty.ticketing.domain.ticket.dto.request.TicketPaymentRequest;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;


@Slf4j
@RequiredArgsConstructor
public class NewRedisReservationService implements ReservationService{

private final MemberRepository memberRepository;
private final SeatRepository seatRepository;
private final StringRedisTemplate redisTemplate;
private final int reservationReleaseDelay;

private final static String SEAT_CONST = "seat-selected-number:";

@Override
public void selectSeat(String memberEmail, SeatSelectionRequest seatSelectionRequest) {
Seat seat =
seatRepository.findById(seatSelectionRequest.getSeatId())
.orElseThrow(() -> new TicketingException(ErrorCode.NOT_FOUND_SEAT));

Member member =
memberRepository.findByEmail(memberEmail)
.orElseThrow(() -> new TicketingException(ErrorCode.NOT_FOUND_MEMBER));

Boolean result = redisTemplate.opsForValue()
.setIfAbsent(SEAT_CONST + seat.getSeatId(), member.getEmail(), reservationReleaseDelay, TimeUnit.SECONDS);

if (Boolean.FALSE.equals(result)) {
throw new TicketingException(ErrorCode.NOT_SELECTABLE_SEAT);
}
}

@Override
public void reservationTicket(String memberEmail, TicketPaymentRequest ticketPaymentRequest) {
// do nothing
}

@Override
public void releaseSeat(String memberEmail, SeatSelectionRequest seatSelectionRequest) {
redisTemplate.delete(SEAT_CONST + seatSelectionRequest.getSeatId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,10 @@ public class ReservationTransactionService implements ReservationService {
private final PaymentProcessor paymentProcessor;
private final LockSeatStrategy lockSeatStrategy;
private final EventPublisher eventPublisher;

private final ReservationManager reservationManager;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
private final int reservationReleaseDelay;

@Value("${ticketing.reservation.release-delay-seconds}")
private int reservationReleaseDelay;
private final ScheduledExecutorService scheduler;

@Override
@Transactional
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.thirdparty.ticketing.global.config;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.StringRedisTemplate;

import com.thirdparty.ticketing.domain.common.EventPublisher;
import com.thirdparty.ticketing.domain.common.LettuceRepository;
Expand All @@ -21,6 +26,22 @@

@Configuration
public class ReservationServiceContainer {

@Value("${ticketing.reservation.release-delay-seconds}")
private int reservationReleaseDelay;

private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);

@Bean
@Primary
public ReservationService newRedisReservationService(
MemberRepository memberRepository,
SeatRepository seatRepository,
StringRedisTemplate redisTemplate) {
return new NewRedisReservationService(memberRepository, seatRepository, redisTemplate, reservationReleaseDelay);
}


@Bean
public ReservationService redissonReservationServiceProxy(
RedissonClient redissonClient,
Expand All @@ -46,7 +67,6 @@ ReservationService optimisticReservationServiceProxy(
return new OptimisticReservationServiceProxy(persistenceOptimisticReservationService);
}

@Primary
@Bean
ReservationService pessimisticReservationServiceProxy(
@Qualifier("persistencePessimisticReservationService")
Expand All @@ -69,7 +89,10 @@ public ReservationTransactionService cacheReservationTransactionService(
paymentProcessor,
lockSeatStrategy,
eventPublisher,
reservationManager);
reservationManager,
reservationReleaseDelay,
scheduler
);
}

@Bean
Expand All @@ -87,7 +110,10 @@ public ReservationTransactionService persistenceOptimisticReservationService(
paymentProcessor,
lockSeatStrategy,
eventPublisher,
reservationManager);
reservationManager,
reservationReleaseDelay,
scheduler
);
}

@Bean
Expand All @@ -105,6 +131,9 @@ public ReservationTransactionService persistencePessimisticReservationService(
paymentProcessor,
lockSeatStrategy,
eventPublisher,
reservationManager);
reservationManager,
reservationReleaseDelay,
scheduler
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;

import com.thirdparty.ticketing.domain.common.LettuceRepository;
import com.thirdparty.ticketing.domain.common.TicketingException;
Expand All @@ -36,6 +39,7 @@

public class CacheReservationTest extends BaseIntegrationTest {

private static final Logger log = LoggerFactory.getLogger(CacheReservationTest.class);
@Autowired private SeatRepository seatRepository;

@Autowired private MemberRepository memberRepository;
Expand All @@ -46,9 +50,7 @@ public class CacheReservationTest extends BaseIntegrationTest {

@Autowired private PerformanceRepository performanceRepository;

@Autowired private LettuceRepository lettuceRepository;

@Autowired private RedissonClient redissonClient;
@Autowired private StringRedisTemplate redisTemplate;

@Autowired
@Qualifier("lettuceReservationServiceProxy")
Expand All @@ -58,6 +60,10 @@ public class CacheReservationTest extends BaseIntegrationTest {
@Qualifier("redissonReservationServiceProxy")
private ReservationService redissonReservationServiceProxy;

@Autowired
@Qualifier("newRedisReservationService")
private ReservationService newRedisReservationService;

private List<Member> members;
private Seat seat;
private Zone zone;
Expand Down Expand Up @@ -97,6 +103,7 @@ void setUp() {
.seatCode("R")
.seatStatus(SeatStatus.SELECTABLE)
.build());
redisTemplate.getConnectionFactory().getConnection().serverCommands().flushAll();
}

@AfterEach
Expand All @@ -108,6 +115,11 @@ void breakUp() {
memberRepository.deleteAll();
}

@Test
public void testConcurrentSeatSelectionWithNewRedis() throws InterruptedException {
runConcurrentSeatSelectionTest(newRedisReservationService);
}

@Test
public void testConcurrentSeatSelectionWithLettuce() throws InterruptedException {
runConcurrentSeatSelectionTest(lettuceCacheTicketService);
Expand Down Expand Up @@ -142,6 +154,7 @@ private void runConcurrentSeatSelectionTest(ReservationService reservationServic
} catch (TicketingException e) {
failureSelections.incrementAndGet();
} catch (Exception e) {
log.error("Error occurred", e);
} finally {
// latch 카운트 감소, 스레드 완료 시 호출
latch.countDown();
Expand All @@ -151,8 +164,6 @@ private void runConcurrentSeatSelectionTest(ReservationService reservationServic

latch.await();

Seat reservedSeat = seatRepository.findById(seat.getSeatId()).orElseThrow();
assertThat(reservedSeat.getMember()).isNotNull();
assertThat(successfulSelections.get()).isEqualTo(1);
assertThat(failureSelections.get()).isEqualTo(4);
}
Expand Down
Loading