-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor/338 : Rate Limiter 토큰 대기방식 개선 #422
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
Changes from 14 commits
780ef58
fe27fab
2b66a85
1ae5ad6
602493d
78c3000
28beb39
5954fd0
142f305
df00655
9a5c4c6
a1c9f9f
d959733
f44108d
2a0f547
839f75b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,10 @@ | ||
| package com.example.cs25batch.sender; | ||
|
|
||
| import com.example.cs25batch.batch.dto.MailDto; | ||
| import io.github.bucket4j.Bucket; | ||
|
|
||
| public interface MailSenderStrategy { | ||
| void sendQuizMail(MailDto mailDto); | ||
|
|
||
| boolean tryConsume(Long num); | ||
| Bucket getBucket(); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,10 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.example.cs25batch.batch.service.SesMailService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.github.bucket4j.Bandwidth; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.github.bucket4j.Bucket; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.github.bucket4j.ConsumptionProbe; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.ThreadLocalRandom; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.TimeUnit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.locks.LockSupport; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Component; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -32,4 +36,18 @@ public void sendQuizMail(MailDto mailDto) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public boolean tryConsume(Long num){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return bucket.tryConsume(num); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void acquirePermitOrWait(){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while (true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (probe.isConsumed()) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long nanos = probe.getNanosToWaitForRefill(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| long jitter = TimeUnit.MILLISECONDS.toNanos( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ThreadLocalRandom.current().nextInt(0, 50) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LockSupport.parkNanos(Math.min(nanos + jitter, TimeUnit.SECONDS.toNanos(1))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+41
to
+51
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion acquirePermitOrWait에서도 인터럽트 신호 처리 추가 권장 JavaMail 전략과 동일한 이유로 인터럽트 플래그를 확인하여 루프를 탈출하세요. @Override
public void acquirePermitOrWait(){
while (true) {
+ if (Thread.currentThread().isInterrupted()) {
+ return;
+ }
ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1);
if (probe.isConsumed()) return;
long nanos = probe.getNanosToWaitForRefill();
long jitter = TimeUnit.MILLISECONDS.toNanos(
ThreadLocalRandom.current().nextInt(0, 50)
);
LockSupport.parkNanos(Math.min(nanos + jitter, TimeUnit.SECONDS.toNanos(1)));
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,4 +30,8 @@ private MailSenderStrategy getValidStrategy(String strategyKey) { | |
| return strategy; | ||
| } | ||
|
|
||
| public void acquirePermitOrWait(String strategyKey) { | ||
| MailSenderStrategy strategy = getValidStrategy(strategyKey); | ||
| strategy.acquirePermitOrWait(); | ||
| } | ||
|
Comment on lines
+33
to
+36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 토큰 획득 시점은 Writer(실제 발송 직전) 배치가 적합합니다 Reader 단계에서 토큰을 확보하면 메시지가 없을 때도 토큰이 소모되어 실제 처리량이 줄 수 있습니다. MailWriter.send 직전에서 acquirePermitOrWait를 호출하도록 표준화하세요(Reader에서 호출 제거 권장). 🤖 Prompt for AI Agents |
||
| } | ||
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.
🛠️ Refactor suggestion
Reader에서 토큰을 선점하면 ‘메시지 없음’에도 토큰이 소모됩니다 — 토큰 획득 호출 제거 권장(Writer로 이동)
현재 acquirePermitOrWait는 토큰을 실제로 소비합니다(tryConsumeAndReturnRemaining 사용). 메시지가 없거나 빈 레코드일 때도 처리량을 깎는 문제가 생깁니다. 토큰 획득은 MailWriter에서 실제 발송 직전에 수행하세요.
MailWriter 측 반영은 별도 코멘트의 diff를 참고하세요.
📝 Committable suggestion
🤖 Prompt for AI Agents