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

πŸ› [Fix] redis 문제 #361

Merged
merged 13 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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
1 change: 0 additions & 1 deletion src/main/java/com/gg/server/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public GameLogListAdminResponseDto findGamesByIntraId(String intraId, Pageable p
@CacheEvict(value = "rankGameListByIntra", allEntries = true),
@CacheEvict(value = "rankGameList", allEntries = true),
@CacheEvict(value = "allGameList", allEntries = true),
@CacheEvict(value = "allGameListByUser", allEntries = true)
@CacheEvict(value = "allGameListByUser", allEntries = true),
@CacheEvict(value = "ranking", allEntries = true)
})
public void rankResultEdit(RankGamePPPModifyReqDto reqDto, Long gameId) {
// κ²Œμž„μ΄ 두λͺ… λ‹€ κ°€μž₯ λ§ˆμ§€λ§‰ κ²Œμž„μΈμ§€ 확인 (κ·Έ game에 ν•΄λ‹Ήν•˜λŠ” νŒ€μ΄ λ§žλŠ”μ§€ 확인)
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/com/gg/server/domain/game/service/GameService.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.gg.server.global.exception.custom.InvalidParameterException;
import com.gg.server.global.utils.ExpLevelCalculator;
import lombok.RequiredArgsConstructor;
import org.hibernate.annotations.Cache;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
Expand All @@ -43,7 +44,7 @@ public class GameService {
private final GameFindService gameFindService;
private final UserCoinChangeService userCoinChangeService;
private final TierService tierService;
private final RedisUploadService redisUploadService;
//private final RedisUploadService redisUploadService;

/**
* κ²Œμž„ 정보λ₯Ό κ°€μ Έμ˜¨λ‹€.
Expand All @@ -67,7 +68,9 @@ public GameTeamInfo getUserGameInfo(Long gameId, Long userId) {
@CacheEvict(value = "rankGameListByIntra", allEntries = true),
@CacheEvict(value = "rankGameList", allEntries = true),
@CacheEvict(value = "allGameList", allEntries = true),
@CacheEvict(value = "allGameListByUser", allEntries = true)
@CacheEvict(value = "allGameListByUser", allEntries = true),
@CacheEvict(value = "ranking", allEntries = true),
@CacheEvict(value = "expRanking", allEntries = true)
})

/**
Expand Down Expand Up @@ -96,7 +99,9 @@ public Boolean createRankResult(RankResultReqDto scoreDto, Long userId) {
@CacheEvict(value = "normalGameListByIntra", allEntries = true),
@CacheEvict(value = "normalGameList", allEntries = true),
@CacheEvict(value = "allGameList", allEntries = true),
@CacheEvict(value = "allGameListByUser", allEntries = true)
@CacheEvict(value = "allGameListByUser", allEntries = true),
@CacheEvict(value = "ranking", allEntries = true),
@CacheEvict(value = "expRanking", allEntries = true)
})
public Boolean normalExpResult(NormalResultReqDto normalResultReqDto, Long loginUserId) {
Game game = gameFindService.findGameWithPessimisticLockById(normalResultReqDto.getGameId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@
import io.swagger.v3.oas.annotations.Parameter;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -28,7 +26,7 @@ public class RankController {
public ExpRankPageResponseDto getExpRankPage(@Valid PageRequestDto pageRequestDto, @Parameter(hidden = true) @Login UserDto user) {
PageRequest pageRequest = PageRequest.of(pageRequestDto.getPage() - 1, pageRequestDto.getSize(),
Sort.by("totalExp").descending().and(Sort.by("intraId")));
return rankService.getExpRankPage(pageRequest, user);
return rankService.getExpRankPageByRedis(pageRequest, user);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.gg.server.domain.rank.controller;

import com.gg.server.domain.rank.dto.ExpRankPageResponseDto;
import com.gg.server.domain.rank.dto.RankPageResponseDto;
import com.gg.server.domain.rank.service.RankService;
import com.gg.server.domain.user.dto.UserDto;
import com.gg.server.global.dto.PageRequestDto;
import com.gg.server.global.utils.argumentresolver.Login;
import io.swagger.v3.oas.annotations.Parameter;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/pingpong/v2")
public class RankV2Controller {
private final RankService rankService;
@GetMapping("/exp")
public ExpRankPageResponseDto getExpRankPage(@Valid PageRequestDto pageRequestDto, @Parameter(hidden = true) @Login UserDto user) {
PageRequest pageRequest = PageRequest.of(pageRequestDto.getPage() - 1, pageRequestDto.getSize(),
Sort.by("totalExp").descending().and(Sort.by("intraId")));
return rankService.getExpRankPage(pageRequest, user);
}
/**
*
* @param pageRequestDto
* @param user
* @param season
* @param gameType
*
* gameTypeλŠ” single둜 κ³ μ •λ˜μ–΄ μ˜€κ³ μžˆλŠ”λ° ν˜„μž¬ rankκ²Œμž„μ€ single만 κ΅¬ν˜„λ˜μ–΄μžˆμ–΄μ„œ μ‚¬μš© μ•ˆν•˜κΈ°λ‘œ
*/
@GetMapping("/ranks/{gameType}")
public RankPageResponseDto getRankPage(@Valid PageRequestDto pageRequestDto, @Parameter(hidden = true) @Login UserDto user,
Long season, String gameType){
kokomong2 marked this conversation as resolved.
Show resolved Hide resolved
PageRequest pageRequest = PageRequest.of(pageRequestDto.getPage() - 1, pageRequestDto.getSize());
return rankService.getRankPageV2(pageRequest, user, season);
}
}
45 changes: 45 additions & 0 deletions src/main/java/com/gg/server/domain/rank/data/RankRepository.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gg.server.domain.rank.data;

import com.gg.server.domain.rank.dto.RankV2Dto;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
Expand Down Expand Up @@ -27,4 +28,48 @@ public interface RankRepository extends JpaRepository<Rank, Long> {

@Query(value = "select count(r) from Rank r where r.season.id=:seasonId and not (r.wins = 0 and r.losses = 0)")
Long countRealRankPlayers(@Param("seasonId") Long seasonId);

@Query(value = "SELECT u.intra_id intraId, r.status_message statusMessage, r.ppp, "
+ "t.image_uri tierImageUri, u.text_color textColor, "
+ "RANK() OVER(ORDER BY r.ppp DESC, pg.created_at ASC, u.total_exp DESC) AS ranking "
+ "FROM Ranks r "
+ "INNER JOIN Tier t "
+ "ON r.tier_id = t.id "
+ "INNER JOIN User u "
+ "ON r.user_id = u.id "
+ "INNER JOIN (SELECT MAX(p.created_at) created_at, p.user_id user_id "
+ " FROM PChange p"
+ " INNER JOIN Game g"
+ " ON p.game_id = g.id"
+ " WHERE g.season_id = :seasonId"
+ " GROUP BY p.user_id) pg "
+ "ON pg.user_id = u.id "
+ "WHERE r.season_id = :seasonId AND (r.losses > 0 OR r.wins > 0) "
+ "LIMIT :pageSize OFFSET :pageNum ", nativeQuery = true)
List<RankV2Dto> findPppRankBySeasonId(@Param("pageNum")int pageNum, @Param("pageSize")int pageSize, @Param("seasonId") Long seasonId);
AYoungSn marked this conversation as resolved.
Show resolved Hide resolved

@Query(value = "SELECT count(*) "
+ "FROM Ranks r "
+ "INNER JOIN User u "
+ "ON r.user_id = u.id "
+ "WHERE r.season_id = :seasonId AND (r.losses > 0 OR r.wins > 0) ", nativeQuery = true)
int countRankUserBySeasonId(@Param("seasonId")Long seasonId);

@Query(value = "SELECT ranked.ranking "
+ "FROM ("
+ "SELECT u.id userId, RANK() OVER(ORDER BY r.ppp DESC, pg.created_at ASC, u.total_exp DESC) AS ranking "
+ "FROM Ranks r "
+ "INNER JOIN User u "
+ "ON r.user_id = u.id "
+ "INNER JOIN (SELECT MAX(p.created_at) created_at, p.user_id user_id "
+ " FROM PChange p"
+ " INNER JOIN Game g"
+ " ON p.game_id = g.id"
+ " WHERE g.season_id = :seasonId"
+ " GROUP BY p.user_id) pg "
+ "ON pg.user_id = u.id "
+ "WHERE r.season_id = :seasonId AND (r.losses > 0 OR r.wins > 0) "
+ ") ranked "
+ "WHERE ranked.userId = :userId", nativeQuery = true)
Optional<Integer> findRankByUserIdAndSeasonId(@Param("userId")Long userId, @Param("seasonId")Long seasonId);
}
12 changes: 11 additions & 1 deletion src/main/java/com/gg/server/domain/rank/dto/ExpRankDto.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.gg.server.domain.rank.dto;

import com.gg.server.domain.user.data.User;
import com.gg.server.domain.user.data.UserImage;
import com.gg.server.global.utils.ExpLevelCalculator;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -29,4 +28,15 @@ public static ExpRankDto from (User user, Integer rank, String statusMessage){
.build();
return dto;
}
public static ExpRankDto from(ExpRankV2Dto dto) {
return ExpRankDto.builder()
.intraId(dto.getIntraId())
.rank(dto.getRanking())
.statusMessage(dto.getStatusMessage())
.level(ExpLevelCalculator.getLevel(dto.getTotalExp()))
.exp(dto.getTotalExp())
.userImageUri(dto.getImageUri())
.textColor(dto.getTextColor())
.build();
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/gg/server/domain/rank/dto/ExpRankV2Dto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.gg.server.domain.rank.dto;

public interface ExpRankV2Dto {
String getIntraId();
String getStatusMessage();
Integer getTotalExp();
String getImageUri();
String getTextColor();
Integer getRanking();

}
10 changes: 10 additions & 0 deletions src/main/java/com/gg/server/domain/rank/dto/RankDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,14 @@ public static RankDto from(User user, RankRedis rankRedis, Integer rank) {
.build();
return dto;
}
public static RankDto from(RankV2Dto dto) {
return RankDto.builder()
.intraId(dto.getIntraId())
.rank(dto.getRanking())
.ppp(dto.getPpp())
.statusMessage(dto.getStatusMessage())
.tierImageUri(dto.getTierImageUri())
.textColor(dto.getTextColor())
.build();
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/gg/server/domain/rank/dto/RankV2Dto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gg.server.domain.rank.dto;

public interface RankV2Dto {

String getIntraId();
String getStatusMessage();
Integer getPpp();
String getTierImageUri();
String getTextColor();
Integer getRanking();

}
82 changes: 73 additions & 9 deletions src/main/java/com/gg/server/domain/rank/service/RankService.java
Original file line number Diff line number Diff line change
@@ -1,47 +1,48 @@
package com.gg.server.domain.rank.service;

import com.gg.server.domain.rank.data.Rank;
import com.gg.server.domain.rank.data.RankRepository;
import com.gg.server.domain.rank.dto.ExpRankDto;
import com.gg.server.domain.rank.dto.ExpRankPageResponseDto;
import com.gg.server.domain.rank.dto.ExpRankV2Dto;
import com.gg.server.domain.rank.dto.RankDto;
import com.gg.server.domain.rank.dto.RankPageResponseDto;
import com.gg.server.domain.rank.dto.RankV2Dto;
import com.gg.server.domain.rank.exception.RedisDataNotFoundException;
import com.gg.server.domain.rank.redis.RankRedis;
import com.gg.server.domain.rank.redis.RankRedisRepository;
import com.gg.server.domain.rank.redis.RedisKeyManager;
import com.gg.server.domain.season.data.Season;
import com.gg.server.domain.season.service.SeasonFindService;
import com.gg.server.domain.user.data.User;
import com.gg.server.domain.user.data.UserImage;
import com.gg.server.domain.user.data.UserImageRepository;
import com.gg.server.domain.user.data.UserRepository;
import com.gg.server.domain.user.dto.UserDto;
import com.gg.server.domain.user.exception.UserNotFoundException;
import com.gg.server.global.exception.ErrorCode;
import com.gg.server.global.exception.custom.PageNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Configuration
@RequiredArgsConstructor
public class RankService {
private final UserRepository userRepository;
private final RankRedisRepository redisRepository;
private final SeasonFindService seasonFindService;
private final RankRepository rankRepository;

@Transactional(readOnly = true)
public ExpRankPageResponseDto getExpRankPage(PageRequest pageRequest, UserDto curUser) {
public ExpRankPageResponseDto getExpRankPageByRedis(PageRequest pageRequest, UserDto curUser) {

Long myRank = curUser.getTotalExp() == 0 ? -1 : userRepository.findExpRankingByIntraId(curUser.getIntraId());
Page<User> users = userRepository.findAllByTotalExpGreaterThan(pageRequest, 0);
Expand All @@ -65,6 +66,64 @@ public ExpRankPageResponseDto getExpRankPage(PageRequest pageRequest, UserDto cu
return new ExpRankPageResponseDto(myRank.intValue(), pageRequest.getPageNumber() + 1, users.getTotalPages(), expRankDtos);
}

/**
* ExpRankPage v2 (redis 쑰회 제거 - db 쑰회둜만 ν•˜λŠ” κΈ°λŠ₯)
* @param pageRequest
* @param curUser
* @return
*/
@Transactional(readOnly = true)
public ExpRankPageResponseDto getExpRankPage(PageRequest pageRequest, UserDto curUser) {
middlefitting marked this conversation as resolved.
Show resolved Hide resolved

Long myRank = curUser.getTotalExp() == 0 ? -1 : userRepository.findExpRankingByIntraId(curUser.getIntraId());
Page<User> users = userRepository.findAllByTotalExpGreaterThan(pageRequest, 0);
if(pageRequest.getPageNumber() + 1 > users.getTotalPages())
throw new PageNotFoundException("νŽ˜μ΄μ§€κ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.", ErrorCode.PAGE_NOT_FOUND);
List<ExpRankDto> expRankDtos = getExpRankList(pageRequest);

return new ExpRankPageResponseDto(myRank.intValue(),
pageRequest.getPageNumber() + 1,
users.getTotalPages(),
expRankDtos);
}
@Cacheable(value = "expRanking", cacheManager = "gameCacheManager",
key = "#pageRequest.pageNumber + #pageRequest.pageSize")
public List<ExpRankDto> getExpRankList(PageRequest pageRequest) {
Season curSeason = seasonFindService.findCurrentSeason(LocalDateTime.now());
List<ExpRankV2Dto> expRankV2Dtos = userRepository.findExpRank(pageRequest.getPageNumber(), pageRequest.getPageSize(), curSeason.getId());
return expRankV2Dtos.stream().map(ExpRankDto::from).collect(Collectors.toList());
}

/**
* rank νŽ˜μ΄μ§€ 쑰회 v2
* @param pageRequest
* @param curUser
* @param seasonId
* @return
*/
@Transactional(readOnly = true)
@Cacheable(value = "ranking", cacheManager = "gameCacheManager",
key = "#pageRequest.pageSize + #pageRequest.pageNumber + #curUser.id + #seasonId")
public RankPageResponseDto getRankPageV2(PageRequest pageRequest, UserDto curUser, Long seasonId) {
Season season;
if (seasonId == null || seasonId == 0) {
season = seasonFindService.findCurrentSeason(LocalDateTime.now());
} else {
season = seasonFindService.findSeasonById(seasonId);
}
int totalPage = calcTotalPageV2(season, pageRequest.getPageSize());
if (totalPage == 0)
return returnEmptyRankPage();
if (pageRequest.getPageNumber() + 1 > totalPage)
throw new PageNotFoundException("νŽ˜μ΄μ§€κ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.", ErrorCode.PAGE_NOT_FOUND);
kokomong2 marked this conversation as resolved.
Show resolved Hide resolved
middlefitting marked this conversation as resolved.
Show resolved Hide resolved

int myRank = rankRepository.findRankByUserIdAndSeasonId(curUser.getId(), season.getId())
.orElse(-1);
List<RankDto> rankList = rankRepository.findPppRankBySeasonId(pageRequest.getPageNumber(), pageRequest.getPageSize(), season.getId())
.stream().map(RankDto::from).collect(Collectors.toList());
return new RankPageResponseDto(myRank, pageRequest.getPageNumber() + 1, totalPage, rankList);
}

@Transactional(readOnly = true)
public RankPageResponseDto getRankPage(PageRequest pageRequest, UserDto curUser, Long seasonId) {
Season season;
Expand All @@ -89,7 +148,6 @@ public RankPageResponseDto getRankPage(PageRequest pageRequest, UserDto curUser,
private RankPageResponseDto returnEmptyRankPage() {
return new RankPageResponseDto(-1, 1, 1, new ArrayList<>());
}

private int findMyRank(UserDto curUser, Season season) {
String zSetKey = RedisKeyManager.getZSetKey(season.getId());
try {
Expand All @@ -99,7 +157,6 @@ private int findMyRank(UserDto curUser, Season season) {
return -1;
}
}

private int calcTotalPage(Season season, int pageSize) {
String zSetKey = RedisKeyManager.getZSetKey(season.getId());
try{
Expand All @@ -109,7 +166,14 @@ private int calcTotalPage(Season season, int pageSize) {
return 0;
}
}

private int calcTotalPageV2(Season season, int pageSize) {
try{
Integer totalUserCount = rankRepository.countRankUserBySeasonId(season.getId());
return (int) Math.ceil((double) totalUserCount / pageSize);
} catch (RedisDataNotFoundException e) {
return 0;
}
}
private List<RankDto> createRankList(int startRank, int endRank, Season season) {
String zSetKey = RedisKeyManager.getZSetKey(season.getId());
String hashKey = RedisKeyManager.getHashKey(season.getId());
Expand Down
Loading