19,800원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
네임드락에서 부모 트랜잭션과 별도로 실행해야하는 이유
네임드락에서 Propagation.REQUIRES_NEW을 설정하는 이유로 부모 트랜잭션과 별도로 실행되어야 한다고 하셨는데, 왜 별도로 실행해야하는지 궁금합니다.다른분들 질문에서 아래처럼 답변해주셨는데 제가 이해한바가 맞을까요?부모의 트랜잭션과 동일한 범위로 묶인다면 Synchronized 와 같은 문제가 발생합니다. Database 에 commit 되기전에 락이 풀리는 현상이 발생합니다. 그렇기때문에 별도의 트랜잭션으로 분리를 해주어 Database 에 정상적으로 commit 이 된 이후에 락을 해제하는것을 의도하였습니다. 핵심은 lock 을 해제하기전에 Database 에 commit 이 되도록 하는것입니다. Synchronized와 같은 문제 : 트랜잭션이 시작 -> 락 획득 -> 로직 수행 -> 락 반납 -> 트랜잭션 커밋과 같이 커밋되기 전에 락 반납한 상황에서 다른 요청이 들어올 수 있기 때문에 동시성 이슈는 여전히 발생위의 내용대로라면 부모 트랜잭션의 로직은 보류해두고, 동시성이 발생하는 로직만 따로 분리해서 새로운 트랜잭션으로 로직 실행후 commit하고, 부모 트랜잭션의 나머지 로직 수행으로 이해했는데, 제가 응용한 프로젝트는 왜 네임드락의 정합성이 안맞는지 궁금합니다..동시성 테스트할때 시간도 엄청 오래걸립니다.2번 질문에 이어서 제가 위에서 이해한대로 트랜잭션 로그도 확인해봤는데, 부모 트랜잭션 이후로 새로운 트랜잭션 완료한 뒤 기존 트랜잭션을 처리하는걸로 로직은 잘 동작하는데 부정합이 이유가 무엇일까요..?20240302 22:10:41.691 [http-nio-8080-exec-1] INFO o.a.c.c.C.[.[.[/] - Initializing Spring DispatcherServlet 'dispatcherServlet' 20240302 22:10:41.691 [http-nio-8080-exec-1] INFO o.s.w.s.DispatcherServlet - Initializing Servlet 'dispatcherServlet' 20240302 22:10:41.695 [http-nio-8080-exec-1] INFO o.s.w.s.DispatcherServlet - Completed initialization in 4 ms 20240302 22:10:41.957 [http-nio-8080-exec-1] TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [com.flab.offcoupon.service.CouponIssueService.issueCoupon] 20240302 22:10:41.957 [http-nio-8080-exec-1] INFO c.f.o.s.CouponIssueService - 트랜잭션 1 : 쿠폰 발급 요청. eventId : 1, couponId : 1, memberId : 1 20240302 22:10:41.988 [http-nio-8080-exec-1] DEBUG j.sqltiming - com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) 1. SELECT id, category, description, start_date, end_date, daily_issue_start_time, daily_issue_end_time, created_at, updated_at FROM event WHERE id = 1; {executed in 14 msec} 20240302 22:10:41.998 [http-nio-8080-exec-1] INFO j.resultsettable - |---|---------|-------------|-----------|-----------|-----------------------|---------------------|--------------------|--------------------| |id |category |description |start_date |end_date |daily_issue_start_time |daily_issue_end_time |created_at |updated_at | |---|---------|-------------|-----------|-----------|-----------------------|---------------------|--------------------|--------------------| |1 |바디케어 |바디케어 전품목 할인 |2024-02-27 |2024-02-29 |13:00:00 |15:00:00 |2024-02-27T22:34:01 |2024-02-27T22:33:57 | |---|---------|-------------|-----------|-----------|-----------------------|---------------------|--------------------|--------------------| 20240302 22:10:42.012 [http-nio-8080-exec-1] DEBUG j.sqltiming - com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) 1. SELECT GET_LOCK('namedLock', 3000); {executed in 9 msec} 20240302 22:10:42.013 [http-nio-8080-exec-1] INFO j.resultsettable - |----------------------------| |get_lock('namedlock', 3000) | |----------------------------| |1 | |----------------------------| 20240302 22:10:42.014 [http-nio-8080-exec-1] INFO c.f.o.s.CouponIssueService - getLock = 1 20240302 22:10:42.014 [http-nio-8080-exec-1] TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [com.flab.offcoupon.repository.IncreaseIssuedCoupon.increaseIssuedCouponQuantity] 20240302 22:10:42.014 [http-nio-8080-exec-1] INFO c.f.o.r.IncreaseIssuedCoupon - 트랜잭션 2 쿠폰 발급 수 증가. couponId : 1 20240302 22:10:42.015 [http-nio-8080-exec-1] INFO c.f.o.r.IncreaseIssuedCoupon - 트랜잭션 2 쿠폰 조회. couponId : 1 20240302 22:10:42.029 [http-nio-8080-exec-1] DEBUG j.sqltiming - com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) 1. SELECT id, event_id, discount_type, discount_rate, discount_price, coupon_type, max_quantity, issued_quantity, validate_start_date, validate_end_date, created_at, updated_at, version FROM coupon WHERE id = 1; {executed in 13 msec} 20240302 22:10:42.041 [http-nio-8080-exec-1] INFO j.resultsettable - |---|---------|--------------|--------------|---------------|------------------------|-------------|----------------|--------------------|------------------|-----------------|-----------------|--------| |id |event_id |discount_type |discount_rate |discount_price |coupon_type |max_quantity |issued_quantity |validate_start_date |validate_end_date |created_at |updated_at |version | |---|---------|--------------|--------------|---------------|------------------------|-------------|----------------|--------------------|------------------|-----------------|-----------------|--------| |1 |1 |PERCENT |50 |[null] |FIRST_COME_FIRST_SERVED |500 |10 |2024-02-01T00:00 |2024-02-05T00:00 |2024-02-01T00:00 |2024-02-01T00:00 |0 | |---|---------|--------------|--------------|---------------|------------------------|-------------|----------------|--------------------|------------------|-----------------|-----------------|--------| 20240302 22:10:42.058 [http-nio-8080-exec-1] DEBUG j.sqltiming - com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) 1. UPDATE coupon SET issued_quantity = 11 WHERE id = 1 {executed in 15 msec} 20240302 22:10:42.059 [http-nio-8080-exec-1] TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [com.flab.offcoupon.repository.IncreaseIssuedCoupon.increaseIssuedCouponQuantity] 20240302 22:10:42.090 [http-nio-8080-exec-1] DEBUG j.sqltiming - com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) 1. SELECT RELEASE_LOCK('namedLock'); {executed in 29 msec} 20240302 22:10:42.092 [http-nio-8080-exec-1] INFO j.resultsettable - |--------------------------| |release_lock('namedlock') | |--------------------------| |1 | |--------------------------| 20240302 22:10:42.093 [http-nio-8080-exec-1] INFO c.f.o.s.CouponIssueService - releaseLock = 1 20240302 22:10:42.138 [http-nio-8080-exec-1] DEBUG j.sqltiming - com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) 1. SELECT EXISTS (SELECT 1 FROM coupon_issue WHERE member_id = 1 AND coupon_id = 1 AND DATE (created_at) = 2024-02-29 LIMIT 1); {executed in 25 msec} 20240302 22:10:42.139 [http-nio-8080-exec-1] INFO j.resultsettable - |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |exists (select 1 from coupon_issue where member_id = 1 and coupon_id = 1 and date (created_at) = '2024-02-29' limit 1) | |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |false | |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 20240302 22:10:42.176 [http-nio-8080-exec-1] DEBUG j.sqltiming - com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) 1. INSERT INTO coupon_issue (member_id, coupon_id, coupon_status, created_at, updated_at) VALUES (1, 1, 'NOT_ACTIVE', 2024-03-02T22:10:42.141176, 2024-03-02T22:10:42.141176) {executed in 32 msec} 20240302 22:10:42.178 [http-nio-8080-exec-1] TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [com.flab.offcoupon.service.CouponIssueService.issueCoupon]
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
NamedLock 에서 @Transactional 사용에 관한 질문
NamedLock을 사용한 예제실습 중 질문입니다NamedLockStockFacade의 decrease 메소드에 @Transactional 을 걸고 StockService의 decrease 메소드에는 @Transactional 을 걸지 않아도 잘 작동해야하는거 같은데, 데드락에 걸리는거 같습니다. 그 이유가 궁금합니다제 생각에는 NamedLockStockFacade의decrease 메소드에 @Transactional 을 걸면,lockRepository.getLock(id.toString()), stockService.decrease(id, quantity), lockRepository.releaseLock(id.toString()) 이 3 메소드가 모두 한 트랜잭션 안에서 처리되므로 lock을 걸고 lock을 해제하는 그 사이에 재고를 감소하므로 아무 문제가 없어보여서 질문드립니다.
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
충돌의 기준이 무엇인가여?
Optimistic Lock은 충돌이 빈번할 경우 적절하지 않다고 하는데, 충돌이 기준이 어떤건지 궁금합니다. 별도로 프로젝트에서 Mybatis랑 같이 쿠폰발급 프로젝트에서 응용중입니다.전 쿠폰 500개를 기준으로 발급된 수량을 합산하는 로직입니다.스레드 100개기준으로 동시성테스트를 진행했을때 69개만 적용되더라구요. 재시도 로직에서 Thread.sleep(1000)으로 하면 98개까지 적용됩니다. 그런데 스레드 10개를 기준으로 동시성 테스트를 진행했을때는 정합성이 맞고, 스레드1000개를 돌려버리면 500개가 전부다 발급됩니다. 1000개 중에 몇개는 실패하고, 몇개는 성공하고 그래서 500개 전부 다 발급된 상황이라고 예상됩니다.Q1. 충돌이 많은 상황이라는게 동시에 여러 스레드가 접근하는 걸 의미하는게 맞나요? 충돌의 기준은 무엇인지, 어플리케이션 내부 로직마다 다른걸까요?Q2. 충돌이 빈번하지 않을때 낙관적락을 사용하는 경우가 어떤 경우인지 궁금합니다.. 정합성이 많이 떨어진다고 생각하는데 실제로 현업에서 많이 사용하나요?Q3. 데이터베이스에 락을 걸지 않아서 성능상 이점이 있다고 하셨는데, 제가 테스트해봤을때는 pessimisticLock보다 Mysql에 더 부하가 많이가더라구요. 재시도로직으로 I/O작업이 더 많이 일어나서 그런것 같은데, 말씀하시는 '성능상 이점'이라는건 어떤건지 궁금합니다.
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
안녕하세요! 강사님 예외처리에 대한 질문이 있습니다.
현재 강의에서는 정상적인 코드면 무한루프가 종료되는걸로 생각하고 있습니다.하지만1. 재고가 감소시켜야 하는 수량 보다 적을 경우는 무한루프를 도는것을 확인했습니다.어떻게 예외처리를 해야할 지 잘 모르겠어서 질문드립니다 ㅠ public class Stock { // ... public void decrease(Long quantity) { if (this.quantity - quantity < 0) { throw new RuntimeException("재고 수량이 부족합니다."); } this.quantity -= quantity; } }public class OptimisticLockService { private final StockRepository stockRepository; @Transactional public void decrease(Long id,Long quantity){ Stock stock = stockRepository.findByIdWithOptimisticLock(id); if (stock.getQuantity() < quantity) { throw new RuntimeException("재고 수량이 부족합니다."); } stock.decrease(quantity); } }stock 클래스에서 예외처리를 하는것이 좋을지 서비스 클래스에서 예외처리를 좋을지?!예외를 주고 OptimisticLockStockFacade에 대한 무한루프를 어떻게 처리할지..강의 잘 들었습니다 답변 부탁드려용 .. @Component @RequiredArgsConstructor public class OptimisticLockStockFacade { private final OptimisticLockService optimisticLockService; public void decrease(Long id, Long quantity) throws InterruptedException { while (true) { try { optimisticLockService.decrease(id, quantity); break; }catch(Exception e) { Thread.sleep(50); } } } } 현재 아래코드를 실행하면 무한루프가 나갑니다.! @Test @DisplayName("재고가 없을시 재고감소 로직 요청시 예외가 일어난다.") void decreaseStockZero() { //given //when //then assertThatThrownBy(() -> optimisticLockStockFacade.decrease(1L, 101L)) .isInstanceOf(RuntimeException.class) .hasMessage("이미 품절된 상품입니다."); }
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
테스트를 전체 실행할 시 한개만 성공 한개는 실패
위의 질문과 똑같은 질문입니다!@SpringBootTestclass stockServiceTest {@Autowired private StockService stockService; @Autowired private StockRepository stockRepository; @BeforeEach public void setUp(){stockRepository.saveAndFlush(new Stock(1L,100L)); }@AfterEach public void after(){stockRepository.deleteAll(); }@Test @DisplayName("동시에 100개 요청")void 동시에_100개의_요청() throws InterruptedException {//given int threadCount = 100; // ExecutorService는 비동기로 실행하는 작업을 단순화하여 사용할 수 있게하는 자바 api ExecutorService executorService = Executors.newFixedThreadPool(32); // CountDownLatch는 다른 쓰레드에서 수행중인 작업이 완료될때까지 대기할 수 있도록 도와주는 클래스 CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) {executorService.submit(()->{try {stockService.decrease(1L,1L); }finally {latch.countDown(); }}); }latch.await(); //when //then Stock stock = stockRepository.findById(1L).orElseThrow(); assertThat(stock.getQuantity()).isEqualTo(0); }@Test @DisplayName("재고감소")void decreaseStock(){//given //when stockService.decrease(1L,1L); Stock stock = stockRepository.findById(1L).orElseThrow(); //then assertThat(stock.getQuantity()).isEqualTo(99); }}1번테스트와 2번테스트를 각각 테스트를 실행할 경우 테스트가 성공하지만 전체 테스트를 실행하게 하여 1번테스트와 2번테스트를 돌리면 No value presentjava.util.NoSuchElementException: No value present라는 예외가 뜹니다.@BeforeEach와 @AfterEach가 있어서 문제가 없을거라 생각하는데 왜 전체 테스트중 동시에_100개의_요청이라는 테스트가 실패하는건가요?
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
@Transactional(isolation = Isolation.SERIALIZABLE)
@Transactional(isolation = Isolation.SERIALIZABLE) 이렇게 해도 동시성 제어 테스트에 실패해서 검색해보니까 SERIALIZABLE 격리 단계는 락이 걸려 있는 동안 update를 못하는거지 select는 가능하다 라는 식의 글을 봤습니다. 그러면 각 스레드들이 접근해서 select만 한 상태에서 대기하고 있다가 락이 풀리면 update를 시도하는 건가요? 그래서 테스트에 실패하는 건가요?
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
Named Lock의 경우 데드락 발생?
Named Lock을 이용할 경우 Pessimistic Lock처럼 데드락이 혹시 발생할 수 있는지 궁금합니다.
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
NamedLock을 사용하는 경우 DataSource 분리
NamedLock을 사용하는 경우에 DataSource를 분리하는 것이 좋다고 하여 시도해봤으나 DataSource 설정의 문제인지 정상적으로 동작하지 않아 질문드리게 되었습니다. Main이 JPA에서 사용하게 될 데이터소스이고, Lock이 JdbcTemplate를 사용하기 위해서 사용하는 데이터소스입니다.spring: datasource: main: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3308/stock_example?serverTimezone=Asia/Seoul username: root password: 1234 lock: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3308/stock_example?serverTimezone=Asia/Seoul username: root password: 1234 hikari: maximum-pool-size: 20 jpa: database: mysql database-platform: org.hibernate.dialect.MySQLDialect hibernate: ddl-auto: create logging: level: root: INFO org: hibernate: SQL: DEBUG type.descriptor.sql.BasicBinder: TRACE애플리케이션 자체에서 사용해야 할 커넥션 풀만 관리를 하면 될 것이라고 생각하여 동일한 데이터베이스를 사용하였습니다. 먼저, 메인으로 사용하는 NamedLock을 사용하지 않는 데이터소스설정클래스입니다.@Configuration @EnableJpaRepositories( basePackages = "com.example.stock.stock", entityManagerFactoryRef = "mainEntityManager", transactionManagerRef = "mainTransactionManager") public class MainDataSourceConfiguration { @Bean @Primary @ConfigurationProperties("spring.datasource.main") public DataSourceProperties mainDataSourceProperties() { return new DataSourceProperties(); } @Bean @Primary public DataSource mainDataSource() { return mainDataSourceProperties() .initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); } @Bean(name = "mainEntityManager") @Primary public LocalContainerEntityManagerFactoryBean mainEntityManager( EntityManagerFactoryBuilder builder) { return builder.dataSource(mainDataSource()) .packages("com.example.stock.stock") .build(); } @Bean(name = "mainTransactionManager") @Primary public PlatformTransactionManager mainTransactionManager( @Qualifier("mainEntityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) { return new JpaTransactionManager(Objects.requireNonNull(entityManagerFactoryBean.getObject())); } }다음으로 Lock을 사용하기 위한 데이터소스 설정 클래스입니다.@Configuration @EnableTransactionManagement public class LockDataSourceConfiguration { @Bean @ConfigurationProperties("spring.datasource.lock") public DataSourceProperties lockDataSourceProperties() { return new DataSourceProperties(); } @Bean public DataSource lockDataSource() { return lockDataSourceProperties() .initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); } @Bean(name = "lockTransactionManager") public JdbcTransactionManager lockTransactionManager(@Qualifier("lockDataSource") DataSource dataSource) { return new JdbcTransactionManager(dataSource); } }@Configuration public class JdbcTemplateConfiguration { @Bean public NamedParameterJdbcTemplate namedParameterJdbcTemplate(@Qualifier("lockDataSource") DataSource dataSource) { return new NamedParameterJdbcTemplate(dataSource); } }그리고 테스트를 돌려봤는데, 재고가 정상적으로 감소가 되지 않습니다. 이상한 점은 업데이트 쿼리가 발생을 하지 않는다는 점입니다.또 매번, 생성되는 스톡의 id가 1씩 증가한다는 점입니다. 혹시 제가 놓친 부분이 있을까요..?
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
synchronized 사용 시 쓰레드 질문
synchronized를 사용하면 1개의 쓰레드만 이용한다고 말씀해주셨는데startTransaction(); stockService.decrease(id, quantity); endTransation();에서 왜 다른 쓰레드가 먼저 호출되는 상황이 발생하는건가요?이 부분이 이해가 잘 안됩니다
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
Redisson 값 설정 질문드립니다.
Redisson을 사용할때 waitTime과 leaseTime 값 설정이 잘못되면 서비스의 큰 문제가 발생할 수 있지 않나요?예를들어, 서비스가 서버 상황에 따라 특정 로직을 처리하는데 걸리는 시간이 다를 거라고 생각합니다. 이때 leaseTime이 처리 시간보다 적다면 동시성 문제가 추가로 발생하지 않나요?또한 강의에서처럼 waitTime 값에 따라 동시성 문제 발생 여부가 달라진다면 이 또한 실제 현업에서는 상황에 따라 다르기 때문에 문제가 되지 않을까요? 위와 같은 이유로, Redisson이 너무 위험하다고 생각이 드는데 제가 잘못이해한건지 궁금합니다.
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
CountDownLatch
다른 스레드에서 작업이 끝날 때 까지 기다리는 CountDownLatch를 이용해야한다고 하셨는데 왜 Race Condition이 발생하는지 궁금합니다. 결국은 CountDownLatch를 안써도 되는 것이 아닌가요?
- 해결됨재고시스템으로 알아보는 동시성이슈 해결방법
분산락이라고 할 때 분산이
어플리케이션 서버가 여러개 라는 뜻일까요?DB 서버가 여러개라는 뜻일까요? 멀티 스레드 테스트를 통해여러 애플리케이션 서버가 동시에 요청이 들어오는 경우는비관,낙관적 락으로도 해결이 가능한걸 확인했고DB가 분산서버일때 분산락을 이용해야한다고 이해했는데 맞을까요? namelock도 결국엔 문자열에 건다해도 그 문자열 == 특정 데이터 인데특정 데이터에 락== 비관적 락이랑 다를게 없는데 정확한 차이를 모르겠습니다.문자열로 하면 뭐가 다른건지...
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
synchronized 활용 시 StockServiceTest의 일부 테스트가실패하는 이유
synchronized를 이용해서 동시성 이슈를 해결한 후StockServiceTest를 실행 했을 때 왜 둘 중 하나의 테스트가 실패하는지 궁금합니다.테스트 메소드 각각 따로 실행하면 각자 성공하는데, 함께 실행하면 둘 중 하나의 테스트가 실패합니다.실패 시 다음과 같은 에러가 발생합니다.java.lang.NullPointerException: Cannot invoke "com.example.stock.domain.Stock.decrease(java.lang.Long)" because "stock" is null@BeforeEach, @AfterEach로 테스트 실행 전 Stock을 저장/삭제를 하고 있기 때문에 함께 실행해도 문제 없을 것으로 생각되어 질문 남깁니다.
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
동시성 문제점
동시성 문제가 생기는 부분이 재고를 감소하고 쿼리가update stock set quantity = :quantity where product_id = :id 이렇게 날라가서 문제가 되는것 같은데decrease를 없애고 쿼리를 update stock set quantity = quantity - :quantity where product_id = :id 이런식으로는 안되는건가요?JPA로는 불가능한가요?
- 해결됨재고시스템으로 알아보는 동시성이슈 해결방법
안녕하세요 분산환경 질문이있습니다
스프링서버 3대가 scale-out 되어있는 상황이고 DB는 하나를 쓰고있습니다. 3대의 스프링서버에서 5분에 한번 실행되는 스케줄링 서비스로, DB에서 좋아요 많은순으로 5개씩 가져옵니다이때 3대의 서버가 동일한 게시글을 가져와야해서, 락의 필요성을 느꼈는데 저는 redis 분산락 말고는 해결방법이 잘 떠오르질 않더라구요. 서버1 select -> 그 사이에 좋아요 update -> 서버2 select -> 서버3 select위같은 상황이 발생해 서버 1과 2,3이 다른 결과를 select 할수도 있을것 같은데 비관,낙관적 락과 네임드락으로 해결할수 없다고 판단했는데 생각이 맞을까요?
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
'Synchronized 이용해보기' 강의에서 질문이 있습니다.
제가 이해한 바는 다음과 같습니다. Transactional 어노테이션을 사용시해당 메서드나 클래스 내의 모든 메서드가 하나의 트랜잭션으로 묶여 처리 synchronized를 사용시동시에 같은 객체의 특정 메서드를 여러 스레드가 호출하지 못하게 하는 데 사용 그런데 1분 29초부터 설명을 들어보면decrease 함수 안에서, endTransaction을 수행하기 전에 다른 스레드가 decrease 메소드를 호출할 수 있다고 하셨습니다. 그런데 이 부분이 이해가 안됩니다. synchronized 키워드를 붙였다면, 동시에 같은 객체의 메서드를 다른 스레드가 호출하지 못하는것 아닌가요? Transactional 어노테이션을 주석처리하면 동작하던데, 이 여부와 관계 없이 동작해야하는것 아닌가요?
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
Named Lock에서 락 획득 실패 후 락 해제할 때가 궁금합니다.
NamedLockFacade에서@Transactional public void decrease(Long id, Long quantity) { try { lockRepository.getLock(id.toString()); stockService.decrease(id, quantity); } finally { lockRepository.releaseLock(id.toString()); } }락 획득을 하지 못해도 releaseLock이 실행되서 락을 해제하는 코드입니다.락을 획득하지 못해도 락을 해제하려고 하는데 따로 문제가 발생하진 않나요?
- 해결됨재고시스템으로 알아보는 동시성이슈 해결방법
redisson lock을 AOP로 변경하는 경우 실무 사용 방법 문의드립니다!
강사님 안녕하세요!해당 강의 수강 후 실무에 재고 동시성 처리 코드를 개발하고 있는데 궁금한 부분이 있어서 문의드립니다! redisson lock 으로 구현하였는데, 중복 코드가 많아서 AOP로 작업 작업을 하려합니다.aop를 작업 하는 경우 facade가 아니라 stockService에 직접 어노테이션을 추가하는 형태로 하는게 맞을지 혹시 해당 부분은 실무에서 사용하실때 어떤 형태로 하시는지 문의드립니다.구글 검색을 하다보니 AOP로 트랜잭션 new로 한다는 이야기가 있는데 실무에서 어떻게 쓰셨는지 궁금해서 문의드립니다!감사합니다! @Component public class RedissonLockFacade { private RedissonClient redissonClient; private StockService stockService; public RedissonLockFacade(RedissonClient redissonClient, StockService stockService) { this.redissonClient = redissonClient; this.stockService = stockService; } public void decrease(Long id, Long quantity) { RLock lock = redissonClient.getLock(id.toString()); try { boolean available = lock.tryLock(10, 1, TimeUnit.SECONDS); if (!available) { System.out.println("lock 획득 실패"); return; } stockService.decrease(id, quantity); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } }
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
lettuce에서 lock할 때 동시성 문제
안녕하십니까 lettuce 라이브러리를 사용해서 lock을 할 때 redisLockRepository.lock(key)로 redis에 key값을 확인 하잖아요 동시에 여러서버에서 lock을 걸 때 저 코드에서는 동시성 문제가 안생기나요?
- 미해결재고시스템으로 알아보는 동시성이슈 해결방법
stringRedisTemplate 사용시 이슈 파악
StockRepository 가 아닌 stringRedisTemplate 을 사용하면 어떤 차이점이 있을까요?