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

Release 1.2.0 #92

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Automized Deployment to QA server
name: Automized Deployment to Develop server

on:
push:
Expand Down Expand Up @@ -39,7 +39,7 @@ jobs:
push: true
tags: wedowhatwedo/mall-qa:latest

- name: Deploy
- name: Deploy to server 1
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST }}
Expand All @@ -52,6 +52,28 @@ jobs:
sudo docker rm backend || true
sudo docker run -d --name backend -p 8080:8080 \
-v /home/ubuntu/logs:/var/logs \
-v /home/ubuntu/dumps:/var/dumps \
-e COOLSMS_API_KEY="${{ secrets.COOLSMS_KEY }}" \
-e COOLSMS_API_SECRET="${{ secrets.COOLSMS_SECRET }}" \
-e COOLSMS_API_NUMBER="${{ secrets.COOLSMS_NUMBER }}" \
-e SPRING_MAIL_USERNAME="${{ secrets.GMAIL_USERNAME }}" \
-e SPRING_MAIL_PASSWORD="${{ secrets.GMAIL_PASSWORD }}" \
wedowhatwedo/mall-qa:latest

- name: Deploy to server 2
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST2 }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
port: ${{ secrets.EC2_SSH_PORT }}
script: |
sudo docker pull wedowhatwedo/mall-qa:latest
sudo docker stop backend || true
sudo docker rm backend || true
sudo docker run -d --name backend -p 8080:8080 \
-v /home/ubuntu/logs:/var/logs \
-v /home/ubuntu/dumps:/var/dumps \
-e COOLSMS_API_KEY="${{ secrets.COOLSMS_KEY }}" \
-e COOLSMS_API_SECRET="${{ secrets.COOLSMS_SECRET }}" \
-e COOLSMS_API_NUMBER="${{ secrets.COOLSMS_NUMBER }}" \
Expand Down
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ FROM openjdk:17-jdk-slim
ARG JAR_FILE=/build/libs/*.jar
COPY ${JAR_FILE} app.jar

RUN mkdir -p /var/logs
RUN mkdir -p /var/logs /var/dumps

ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-jar", "/app.jar"]
ENTRYPOINT ["java", \
"-Xlog:gc=info:file=/var/logs/gc-%t.log:time", \
"-XX:+HeapDumpOnOutOfMemoryError", \
"-XX:HeapDumpPath=/var/dumps", \
"-XX:OnOutOfMemoryError=kill -9 %p", \
"-Dspring.profiles.active=prod", \
"-jar", "/app.jar"]
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableAsync
@EnableCaching
@EnableTransactionManagement
public class ShoppingMallFashionApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
package com.example.flab.soft.shoppingmallfashion.admin.service;

import com.example.flab.soft.shoppingmallfashion.admin.dto.CreatedDataInfo;
import com.example.flab.soft.shoppingmallfashion.admin.dto.IdNameDto;
import com.example.flab.soft.shoppingmallfashion.admin.dto.TestItemDto;
import com.example.flab.soft.shoppingmallfashion.admin.util.IdNameRowMapper;
import com.example.flab.soft.shoppingmallfashion.admin.util.ItemDtoGenerator;
import com.example.flab.soft.shoppingmallfashion.item.controller.ItemOptionDto;
import com.example.flab.soft.shoppingmallfashion.item.domain.SaleState;
import com.example.flab.soft.shoppingmallfashion.store.repository.Store;
import com.example.flab.soft.shoppingmallfashion.user.domain.User;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -83,80 +92,78 @@ public void bulkInsertStores(List<Store> stores, SaleState saleState) {
);
}

public void bulkInsertItems(List<TestItemDto> testItemDtos) {
public void bulkInsertItems(Integer itemCount, CreatedDataInfo userCreatedDataInfo,
CreatedDataInfo storeCreatedDataInfo,
CreatedDataInfo categoryCreatedDataInfo) {
String itemSql = "INSERT INTO items "
+ "(name, original_price, sale_price, description, "
+ "sex, sale_state, store_id, category_id, lastly_modified_by, order_count)" +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ "sex, sale_state, store_id, category_id, lastly_modified_by, order_count) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

String itemOptionSql = "INSERT INTO item_options "
+ "(name, size, item_id, sale_state, stocks_count) "
+ "VALUES (?, ?, ?, ?, ?)";

Map<String, List<ItemOptionDto>> itemNameOptionsMap =
new HashMap<>(testItemDtos.size() * 2);

int groupSize = 500;
int numberOfItemGroups = (int) Math.ceil((double) testItemDtos.size() / groupSize);
int groupSize = 200;
int numberOfItemGroups = (int) Math.ceil((double) itemCount / groupSize);

long before = System.currentTimeMillis();

IntStream.range(0, numberOfItemGroups)
.mapToObj(i -> testItemDtos.subList(i * groupSize, Math.min((i + 1) * groupSize, testItemDtos.size())))
.parallel()
.mapToObj(group -> IntStream.range(0, groupSize)
.mapToObj(i -> ItemDtoGenerator.generateItemTestDtos(userCreatedDataInfo,
storeCreatedDataInfo, categoryCreatedDataInfo))
.toList())
.forEach(itemGroup -> {
List<Object[]> batchArgs = new ArrayList<>();
for (TestItemDto item : itemGroup) {
batchArgs.add(new Object[]{
item.getName(),
item.getOriginalPrice(),
item.getSalePrice(),
item.getDescription(),
item.getSex().name(),
item.getSaleState().name(),
item.getStoreId(),
item.getCategoryId(),
item.getIsModifiedBy(),
item.getOrderCount()
});
itemNameOptionsMap.put(item.getName(), item.getItemOptions());
KeyHolder keyHolder = new GeneratedKeyHolder();
PreparedStatementCreator psc = connection ->
connection.prepareStatement(itemSql, Statement.RETURN_GENERATED_KEYS);
jdbcTemplate.batchUpdate(psc, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
TestItemDto item = itemGroup.get(i);
ps.setString(1, item.getName());
ps.setInt(2, item.getOriginalPrice());
ps.setInt(3, item.getSalePrice());
ps.setString(4, item.getDescription());
ps.setString(5, item.getSex().name());
ps.setString(6, item.getSaleState().name());
ps.setLong(7, item.getStoreId());
ps.setLong(8, item.getCategoryId());
ps.setLong(9, item.getIsModifiedBy());
ps.setInt(10, item.getOrderCount());
}

@Override
public int getBatchSize() {
return itemGroup.size();
}
}, keyHolder);

List<Map<String, Object>> generatedKeys = keyHolder.getKeyList();

List<Object[]> optionBatchArgs = new ArrayList<>();
for (int i = 0; i < generatedKeys.size(); i++) {
Long generatedItemId = ((Number) generatedKeys.get(i).get("GENERATED_KEY")).longValue();
TestItemDto item = itemGroup.get(i);
List<ItemOptionDto> options = item.getItemOptions();

for (ItemOptionDto option : options) {
optionBatchArgs.add(new Object[]{
option.getName(),
option.getSize(),
generatedItemId,
option.getSaleState().name(),
option.getStocksCount()
});
}
}
jdbcTemplate.batchUpdate(itemSql, batchArgs);

jdbcTemplate.batchUpdate(itemOptionSql, optionBatchArgs);
});

log.info("아이템 배치에 걸린 시간: {}", System.currentTimeMillis() - before);

String findAllItemsSql = "SELECT id, name FROM items";

before = System.currentTimeMillis();

List<IdNameDto> idNameDtos = jdbcTemplate.query(findAllItemsSql, new IdNameRowMapper());

log.info("전체 아이템 조회에 걸린 시간: {}", System.currentTimeMillis() - before);

before = System.currentTimeMillis();

int itemOptionGroupSize = 100;
int numberOfGrops = (int) Math.ceil((double) testItemDtos.size() / itemOptionGroupSize);

IntStream.range(0, numberOfGrops)
.mapToObj(i -> idNameDtos.subList(i * itemOptionGroupSize, Math.min((i + 1) * itemOptionGroupSize, idNameDtos.size())))
.parallel()
.forEach(itemGroup -> {
List<Object[]> batchArgs = new ArrayList<>();
for (IdNameDto idNameDto : itemGroup) {
List<ItemOptionDto> itemOptions = itemNameOptionsMap.get(idNameDto.getName());
itemOptions.forEach(itemOption -> batchArgs.add(new Object[]{
itemOption.getName(),
itemOption.getSize(),
idNameDto.getId(),
itemOption.getSaleState().name(),
itemOption.getStocksCount()
}));
}
jdbcTemplate.batchUpdate(itemOptionSql, batchArgs);
});
log.info("아이템 옵션 배치에 걸린 시간: {}", System.currentTimeMillis() - before);
}

public void encryptPassword() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ public void createTestItems(Integer itemCount,
CreatedDataInfo userCreatedDataInfo,
CreatedDataInfo storeCreatedDataInfo,
CreatedDataInfo categoryCreatedDataInfo) {
List<TestItemDto> itemDtos = IntStream.range(0, itemCount)
.mapToObj(i -> ItemDtoGenerator.generateItemTestDtos(userCreatedDataInfo,
storeCreatedDataInfo, categoryCreatedDataInfo))
.toList();
adminBatchService.bulkInsertItems(itemDtos);
adminBatchService.bulkInsertItems(itemCount, userCreatedDataInfo, storeCreatedDataInfo, categoryCreatedDataInfo);
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
package com.example.flab.soft.shoppingmallfashion.common;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class RedisConfig {
Expand Down Expand Up @@ -35,6 +45,32 @@ public RedisTemplate<String, Object> redisTemplate() {

redisTemplate.setDefaultSerializer(new StringRedisSerializer());

redisTemplate.setEnableTransactionSupport(true);

return redisTemplate;
}

@Bean
public CacheManager cacheManager() {
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

Map<String, RedisCacheConfiguration> redisCacheConfigMap = new HashMap<>();
redisCacheConfigMap.put("ITEM_LIST", defaultConfig.entryTtl(Duration.ofMinutes(5)));
redisCacheConfigMap.put("ITEM_DETAILS", defaultConfig.entryTtl(Duration.ofDays(1)));
redisCacheConfigMap.put("ITEM_LIST_COUNT", defaultConfig.entryTtl(Duration.ofDays(1)));
redisCacheConfigMap.put("TOP_ITEMS_STORE", defaultConfig.entryTtl(Duration.ofMinutes(5)));
redisCacheConfigMap.put("TOP_ITEMS_CATEGORY", defaultConfig.entryTtl(Duration.ofMinutes(5)));
redisCacheConfigMap.put("STOCKS", defaultConfig.entryTtl(Duration.ofMinutes(5)));

return RedisCacheManager.builder(redisConnectionFactory())
.withInitialCacheConfigurations(redisCacheConfigMap)
.build();
}

@Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
Loading
Loading