-
Notifications
You must be signed in to change notification settings - Fork 1
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/#215] article_view_his 테이블 추가 및 조회수 응답 추가 #216
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 부탁드려요
# DB schema migration path (except data module) | ||
data/src/main/resources/**/*.sql | ||
api/**/*.sql | ||
api-repo/**/*.sql | ||
batch/**/*.sql | ||
email/**/*.sql | ||
storage/**/*.sql |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
각 모듈별로 jooq 필드시 생성되는 .sql 파일이 gitignore되어있었는데, 최상위 위치에 통합관리하도록 변경했습니다
fun insertArticleViewHis(command: ArticleViewHisCommand) { | ||
dslContext.insertInto( | ||
ArticleViewHis.ARTICLE_VIEW_HIS, | ||
ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID, | ||
ArticleViewHis.ARTICLE_VIEW_HIS.MEMBER_ID | ||
).values( | ||
command.articleId, | ||
command.memberId | ||
).execute() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
article_view_his
테이블 insert. articleId에 인덱스 추가해야겠다...
fun selectArticleViews(query: ArticleViewHisCountQuery): Long { | ||
return dslContext.selectCount() | ||
.from(ArticleViewHis.ARTICLE_VIEW_HIS) | ||
.where(ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID.eq(query.articleId)) | ||
.fetchOne(0, Long::class.java) ?: 0L | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아티클 별 view 개수 조회. 이거때문에 article id에 인덱스 해야겠네요 추가커밋 할게요
import org.jooq.DSLContext | ||
import org.springframework.stereotype.Repository | ||
|
||
@Repository |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
종준님은 DAO에 @Repository
쓰셨던데 저는 @Component
썼거든요 뭐가 더 어울릴까요? 통일하는게 좋을거 같아서
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CREATE TABLE ARTICLE_VIEW_HIS | ||
( | ||
id BIGINT NOT NULL AUTO_INCREMENT, | ||
article_mst_id BIGINT NOT NULL, | ||
member_id BIGINT NOT NULL, | ||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
deleted_at TIMESTAMP NULL DEFAULT NULL, | ||
PRIMARY KEY (id) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ARTICLE_VIEW_HIS
테이블 스키마
-- [인덱스 추가] -- | ||
CREATE INDEX article_view_his_idx1 ON PROBLEM (article_mst_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
article_mst_id
컬럼에 대한 인덱스 추가입니다. 조회수 가져오는 쿼리에서 쓰여요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테이블명 잘못되어서 수정함
).execute() | ||
} | ||
|
||
fun selectArticleViews(query: ArticleViewHisCountQuery): Long { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -19,6 +22,7 @@ class ReadArticleUseCase( | |||
private val articleDao: ArticleDao, | |||
private val readArticleWriterRecordService: ReadArticleWriterRecordService, | |||
private val browseArticleProblemsService: BrowseArticleProblemsService, | |||
private val articleViewHisService: ArticleViewHisService, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
articleView 도 article 도메인에 포함된다고 생각해서 저는 ArticleViewDao를 바로 사용해도 괜찮지 않을까 생각하는데 어떻게 생각하시는지 궁금합니다
return dslContext.selectCount() | ||
.from(ArticleViewHis.ARTICLE_VIEW_HIS) | ||
.where(ArticleViewHis.ARTICLE_VIEW_HIS.ARTICLE_MST_ID.eq(query.articleId)) | ||
.fetchOne(0, Long::class.java) ?: 0L |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서는 Long?
타입으로 반환하고 ?: 0L
로 처리한 부분을 UC에서 하는게 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로직은 최대한 UC로 뺴는게 좋겠네요
articleViewHisService.addArticleViewHis(AddArticleViewHisInDto(useCaseIn.articleId, useCaseIn.memberId)) | ||
val views = articleViewHisService.readArticleViews(ReadArticleViewsInDto(useCaseIn.articleId)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요거는 아이디어 차원의 이야기인데 아래 방법은 어떨지 궁금합니다.
기존:
- addArticleViewHis로 뷰 확인 기록을 추가한다.
- readArticleViews로 뷰 확인 기록을 조회한다.
제안:
- readArticleViews로 뷰 확인 기록을 조회한다.
- 조회한 값에 +1 하여 views를 반환한다.
- addArticleViewHis로 뷰 확인 기록을 추가는 별도의 스레드에서 비동기로 진행한다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제안한 방식 나도 생각했던건데.. Insert는 테이블 락 + 인덱스 재구조화 때문에 항상 신중해야하는데, 안그래도 엄청 자주 호출될 SQL이라... 그게 좋은거 같아요
@@ -27,7 +27,7 @@ class ArticleController( | |||
@Min(value = 1, message = "{min.id}") | |||
articleId: Long, | |||
): ApiResponse<ApiResponse.SuccessBody<ReadArticleResponse>> { | |||
val useCaseOut = ReadArticleUseCaseIn(articleId).let { useCaseIn: ReadArticleUseCaseIn -> | |||
val useCaseOut = ReadArticleUseCaseIn(articleId = articleId, memberId = 1L).let { useCaseIn: ReadArticleUseCaseIn -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요거는 관련 처리를 하지 않아서 1L로 한것이죠?
1L은 값이 있어서 0L로 하고 todo로 표시하는건 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네 유저관련 기능 개발해주시면서 해당 부분 변경 부탁드립니다! 일단 0으로 해둘게요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2차 리뷰 부탁드립니다
@Configuration | ||
class DatabaseAccessThreadPoolConfig { | ||
private val log = KotlinLogging.logger {} | ||
|
||
companion object { | ||
const val DATABASE_ACCESS_POOL = "database-task-" | ||
} | ||
|
||
@Bean | ||
@ConfigurationProperties(prefix = "database.thread-pool") | ||
fun databaseAccessThreadPoolProperties(): ThreadPoolProperties { | ||
return ThreadPoolProperties() | ||
} | ||
|
||
@Bean(DATABASE_ACCESS_POOL) | ||
fun databaseAccessThreadPool() = ThreadPoolTaskExecutor().apply { | ||
val properties = databaseAccessThreadPoolProperties() | ||
corePoolSize = properties.getCorePoolSize() | ||
maxPoolSize = properties.getMaxPoolSize() | ||
queueCapacity = properties.getQueueCapacity() | ||
setWaitForTasksToCompleteOnShutdown(properties.getWaitForTasksToCompleteOnShutdown()) | ||
setAwaitTerminationSeconds(properties.getAwaitTerminationSeconds()) | ||
setThreadNamePrefix("databaseAccessThreadPool-") | ||
setRejectedExecutionHandler { r, _ -> | ||
log.warn { "Database Access Task Rejected: $r" } | ||
} | ||
initialize() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
디코 훅 쓰레드 풀이랑 똑같이 디비 접근용 쓰레드 풀 생성. 추후 이걸 활용할 것들이 많아보여서 네이밍을 좀 일반적이게 했어요(조회수랑 관련없이)
browseArticleProblemsService.execute(query) | ||
} | ||
|
||
val views = (articleViewHisDao.countArticleViews(ArticleViewHisCountQuery(useCaseIn.articleId)) ?: 0L) + 1L |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
조회수 가져온 뒤에 우선 + 1 하도록 변경
@Async(value = DATABASE_ACCESS_POOL) | ||
@Transactional | ||
fun insertArticleViewHisAsync(articleId: Long, memberId: Long) { | ||
try { | ||
articleViewHisDao.insertArticleViewHis(ArticleViewHisCommand(articleId, memberId)) | ||
log.debug { "Successfully inserted article view history for articleId: $articleId and memberId: $memberId" } | ||
} catch (e: Exception) { | ||
log.error { "Failed to insert article view history for articleId: $articleId and memberId: $memberId" } | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Insert 는 DATABASE_ACCESS_POOL 풀에서 수행하도록 변경. 어차피 다른 쓰레드에서 수행되기 때문에 기존 아티클을 조회하던 쓰레드와 트랜잭션 컨텍스트를 공유하지 않고, 따라서 트랜잭션 전파 레벨 지정은 필요 없음. (전파 자체가 안됨, new transaction)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 로직은 일단 UC 내부에 넣어뒀는데 서비스로 뺴기도 애매한게, 같은 도메인이여서,, 좋은 생각있음 공유좀요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일단 handler로 함...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handler 가 안보이는데 혹시 어디 있을까요?
아아 죄송합니다..
확인했어요!
database: | ||
thread-pool: | ||
core-pool-size: 10 | ||
max-pool-size: 30 | ||
queue-capacity: 70 | ||
wait-for-tasks-to-complete-on-shutdown: true | ||
await-termination-seconds: 60 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 VM 환경변수로 뺼까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 설정하면서 든 생각이, 만약 조회가 엄청 많이 발생하는 상황에선 max 개수인 30 + queue 대기 70 해서 100 명 까지만 Insert가 될 거 같음...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그럼 databaseAccessThreadPool의 setRejectedExecutionHandler 부분을 수정하면 되지 않을까요?
제가 디스코드에 해둔 것은 단순히 실패 로그를 남기는 것 뿐인데 다른 기본 정책들도 있더라고요
예를들어 CallerRunsPolicy 요런 정책이 있습니다.
추가로 액션이 성공 못하는 것은 app 모듈의 test의 application-test.yml에 현 PR에서 추가된 설정들이 추가되지 않아서 그런 것 같아요! |
@hun-ca 테스트 통과합니다~ |
🎫 연관 이슈
resolved #215
💁♂️ PR 내용
🙏 작업
🙈 PR 참고 사항
📸 스크린샷
🤖 테스트 체크리스트