Skip to content

Commit

Permalink
feat: 좋아요, 리뷰 조회 성능 테스트 세팅
Browse files Browse the repository at this point in the history
  • Loading branch information
EunChanNam committed Nov 29, 2023
1 parent 038d3db commit 0de2aad
Show file tree
Hide file tree
Showing 21 changed files with 480 additions and 36 deletions.
23 changes: 13 additions & 10 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,20 @@ jobs:
source: "./.env"
target: "./"

- name: ubuntu Docker image build and push
run: |
docker-compose build -f ./docker/docker-compose.yml
docker-compose push -f ./docker/docker-compose.yml
# - name: ubuntu Docker image build
# run: docker build -t ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:latest -f Dockerfile-server .
#
# - name: Redis Docker image build
# run: docker build -t ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:redis -f Dockerfile-redis .

- name: ubuntu Docker image build
run: docker build -t ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:latest -f Dockerfile-server .

- name: Redis Docker image build
run: docker build -t ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:redis -f Dockerfile-redis .

- name: ubuntu docker Hub 푸쉬
run: docker push ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:latest
- name: Redis docker Hub 푸쉬
run: docker push ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:redis
# - name: ubuntu docker Hub 푸쉬
# run: docker push ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:latest
# - name: Redis docker Hub 푸쉬
# run: docker push ${{ secrets.DOCKER_NAME }}/we-share-wish-hair:redis

- name: Deploy with push
uses: appleboy/[email protected]
Expand Down
13 changes: 13 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: '3'
services:
server:
build:
context: ./
dockerfile: server/Dockerfile-server
image: eunchannam/we-share-wish-hair:latest

redis:
build:
context: ./
dockerfile: redis/Dockerfile-redis
image: eunchannam/we-share-wish-hair:redis
File renamed without changes.
File renamed without changes.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.inq.wishhair.wesharewishhair.global.config.database;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;

import com.zaxxer.hikari.HikariDataSource;

@Profile("dev")
@Configuration
public class DataSourceConfig {

private static final String MASTER_DATASOURCE = "masterDataSource";
private static final String SLAVE_DATASOURCE = "slaveDataSource";
private static final String ROUTING_DATASOURCE = "slaveDataSource";

@Bean(MASTER_DATASOURCE)
@ConfigurationProperties(prefix = "spring.datasource.master.hikari")
public DataSource masterDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}

@Bean(SLAVE_DATASOURCE)
@ConfigurationProperties(prefix = "spring.datasource.slave.hikari")
public DataSource slaveDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}

@Bean(ROUTING_DATASOURCE)
public DataSource routingDataSource(
@Qualifier(MASTER_DATASOURCE) DataSource masterDataSource,
@Qualifier(SLAVE_DATASOURCE) DataSource slaveDataSource
) {
RoutingDataSource routingDataSource = new RoutingDataSource();

Map<Object, Object> datasourceMap = new HashMap<>();
datasourceMap.put("master", masterDataSource);
datasourceMap.put("slave", slaveDataSource);

routingDataSource.setTargetDataSources(datasourceMap);
routingDataSource.setDefaultTargetDataSource(masterDataSource);

return routingDataSource;
}

@Bean
@Primary
public LazyConnectionDataSourceProxy dataSource(
@Qualifier(ROUTING_DATASOURCE) DataSource routingDataSource
){
return new LazyConnectionDataSourceProxy(routingDataSource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.inq.wishhair.wesharewishhair.global.config.database;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class RoutingDataSource extends AbstractRoutingDataSource {

@Override
protected Object determineCurrentLookupKey() {
return (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) ? "slave" : "master";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.inq.wishhair.wesharewishhair.review.application;

import org.springframework.stereotype.Service;

import com.inq.wishhair.wesharewishhair.global.utils.RedisUtils;
import com.inq.wishhair.wesharewishhair.review.domain.entity.Review;
import com.inq.wishhair.wesharewishhair.review.domain.likereview.LikeReview;
import com.inq.wishhair.wesharewishhair.review.domain.likereview.LikeReviewRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class LikeReviewTestService {

private final LikeReviewRepository likeReviewRepository;
private final ReviewFindService reviewFindService;
private final RedisUtils redisUtils;

public long count(Long reviewId) {
return likeReviewRepository.countByReviewId(reviewId);
}

public void clean() {
likeReviewRepository.deleteAll();
}

/**
* LikeReview 생성 후 Review 에 락걸고 likeCount 변수 update
*/
public void withLock(Long reviewId, Long userId) {
likeReviewRepository.save(LikeReview.addLike(userId, reviewId));

Review review = reviewFindService.getWithLockById(reviewId);
review.addLike();
}

/**
* 레디스에 좋아요 정보가 없으면 새로 등록하고 있으면 INCR 수행
*/
public void withoutLock(Long reviewId, Long userId) {
likeReviewRepository.save(LikeReview.addLike(userId, reviewId));

redisUtils.getData(reviewId)
.ifPresentOrElse(
likeCount -> redisUtils.increaseData(reviewId),
() -> {
Long likeCount = likeReviewRepository.countByReviewId(reviewId);
redisUtils.setData(reviewId, likeCount);
}
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ public Review findWithPhotosById(Long id) {
public List<Review> findWithPhotosByUserId(Long userId) {
return reviewRepository.findWithPhotosByWriterId(userId);
}

public Review getWithLockById(Long id) {
return reviewRepository.findWithLockById(id).orElseThrow();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ public static ReviewResponse toReviewResponse(Review review, Long likeCount) {
.build();
}

public static ReviewResponse toReviewResponse(Review review) {

return ReviewResponse.builder()
.reviewId(review.getId())
.hairStyleName(review.getHairStyle().getName())
.userNickname(review.getWriter().getNicknameValue())
.score(review.getScore().getValue())
.contents(review.getContentsValue())
.createdDate(review.getCreatedDate())
.photos(toPhotoResponses(review.getPhotos()))
.likes(review.getLikeCount())
.hashTags(toHashTagResponses(review.getHairStyle().getHashTags()))
.writerId(review.getWriter().getId())
.build();
}

public static ReviewDetailResponse toReviewDetailResponse(
Review review,
Long likeCount,
Expand All @@ -79,4 +95,14 @@ public static ResponseWrapper<ReviewResponse> toWrappedReviewResponse(

return new ResponseWrapper<>(reviewResponses);
}

public static ResponseWrapper<ReviewResponse> toWrappedReviewResponse(
List<Review> responses
) {
List<ReviewResponse> reviewResponses = responses.stream()
.map(ReviewResponseAssembler::toReviewResponse)
.toList();

return new ResponseWrapper<>(reviewResponses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public interface ReviewRepository {
void deleteByIdIn(List<Long> reviewIds);

void delete(Review review);

Optional<Review> findWithLockById(Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public class Review extends BaseEntity {
@JoinColumn(name = "hair_style_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private HairStyle hairStyle;

private long likeCount = 0;

private Review(User writer, String contents, Score score, List<String> photos, HairStyle hairStyle) {
this.writer = writer;
this.contents = new Contents(contents);
Expand Down Expand Up @@ -91,6 +93,10 @@ public void updateReview(Contents contents, Score score, List<String> storeUrls)
updatePhotos(storeUrls);
}

public void addLike() {
this.likeCount++;
}

private void updateContents(Contents contents) {
this.contents = contents;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ public interface LikeReviewRepository {
boolean existsByUserIdAndReviewId(Long userId, Long reviewId);

void deleteByReviewIdIn(List<Long> reviewIds);

void deleteAll();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ public interface LikeReviewJpaRepository extends LikeReviewRepository, JpaReposi
boolean existsByUserIdAndReviewId(Long userId, Long reviewId);

void deleteByReviewIdIn(@Param("reviewIds") List<Long> reviewIds);

void deleteAll();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;

import com.inq.wishhair.wesharewishhair.review.domain.ReviewRepository;
import com.inq.wishhair.wesharewishhair.review.domain.entity.Review;

import jakarta.persistence.LockModeType;

public interface ReviewJpaRepository extends ReviewRepository, JpaRepository<Review, Long> {

//review find service - 리뷰 단순 조회
Expand All @@ -19,4 +22,7 @@ public interface ReviewJpaRepository extends ReviewRepository, JpaRepository<Rev
List<Review> findWithPhotosByWriterId(Long writerId);

void deleteByIdIn(List<Long> reviewIds);

@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<Review> findWithLockById(Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
import com.inq.wishhair.wesharewishhair.review.domain.entity.QReview;
import com.inq.wishhair.wesharewishhair.review.domain.entity.Review;
import com.inq.wishhair.wesharewishhair.review.domain.likereview.QLikeReview;
import com.querydsl.core.types.ConstructorExpression;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;

Expand All @@ -33,6 +36,45 @@ public class ReviewQueryDslRepository implements ReviewQueryRepository {
private final QReview review = new QReview("r");
private final QLikeReview like = new QLikeReview("l");

private final NumberExpression<Long> likes = new CaseBuilder()
.when(like.id.sum().isNull())
.then(0L)
.otherwise(review.id.count());

public List<ReviewQueryResponse> joinWithLikeQuery() {
return factory
.select(assembleReviewProjection())
.from(review)
.leftJoin(like).on(review.id.eq(like.reviewId))
.leftJoin(review.hairStyle)
.fetchJoin()
.leftJoin(review.writer)
.fetchJoin()
.groupBy(review.id)
.orderBy(review.id.desc())
.offset(0)
.limit(20)
.fetch();
}

public List<Review> noJoinQuery() {
return factory
.select(review)
.from(review)
.leftJoin(review.hairStyle)
.fetchJoin()
.leftJoin(review.writer)
.fetchJoin()
.orderBy(review.id.desc())
.offset(0)
.limit(20)
.fetch();
}

private ConstructorExpression<ReviewQueryResponse> assembleReviewProjection() {
return new QReviewQueryResponse(review, likes.as("likes"));
}

@Override
public Optional<Review> findReviewById(Long id) {
return Optional.ofNullable(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.inq.wishhair.wesharewishhair.review.infrastructure;

import com.inq.wishhair.wesharewishhair.review.domain.entity.Review;
import com.querydsl.core.annotations.QueryProjection;

import lombok.Getter;

@Getter
public class ReviewQueryResponse {

private final Review review;
private final long likes;

@QueryProjection
public ReviewQueryResponse(Review review, long likes) {
this.review = review;
this.likes = likes;
}
}
Loading

0 comments on commit 0de2aad

Please sign in to comment.