Skip to content

[UPLUS-134] Kafka Consumer 성능 개선 및 로직 구현#3

Merged
starboxxxx merged 5 commits into
developfrom
feat/UPLUS-134
Jan 25, 2026
Merged

[UPLUS-134] Kafka Consumer 성능 개선 및 로직 구현#3
starboxxxx merged 5 commits into
developfrom
feat/UPLUS-134

Conversation

@starboxxxx
Copy link
Copy Markdown
Contributor

🍀 이슈 번호

  • #이슈번호

UPLUS-134


✅ 작업 사항

Kafka Consumer 성능 개선 및 로직 구현


📋 체크리스트

  • 코드가 정상적으로 빌드됩니다.
  • 관련 테스트 코드를 작성했습니다.
  • 기존 테스트가 모두 통과합니다.
  • 코드 스타일(Spotless, Checkstyle)을 준수합니다.

⌨ 기타

@starboxxxx starboxxxx self-assigned this Jan 25, 2026
@starboxxxx starboxxxx added the feat 새로운 기능 label Jan 25, 2026
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @starboxxxx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 Kafka Consumer의 전반적인 성능을 개선하고 알림 처리 로직을 최적화하는 데 중점을 둡니다. 주요 변경 사항은 Kafka 메시지 처리 방식을 단일 메시지에서 배치 처리로 전환하고, Redis Lua 스크립트를 활용하여 중복 제거 로직을 효율적으로 구현하는 것입니다. 이를 통해 알림 시스템의 처리량과 안정성이 크게 향상될 것으로 기대됩니다.

Highlights

  • Kafka Consumer 설정 개선: Kafka 리스너 팩토리가 이제 배치 리스너를 지원하고, 동시성(concurrency)이 6으로 설정되어 처리량이 향상되었습니다.
  • Kafka 메시지 배치 처리 도입: NotificationConsumer가 단일 메시지 대신 메시지 목록을 처리하도록 변경되어, 대량의 알림 이벤트를 더욱 효율적으로 처리할 수 있게 되었습니다.
  • Redis Lua 스크립트를 이용한 배치 중복 제거: Redis Lua 스크립트(dedup_batch.lua)를 도입하여 여러 이벤트 ID에 대한 중복 제거를 Redis에서 단일 원자적 작업으로 효율적으로 처리합니다.
  • 새로운 Redis Lua 설정 추가: RedisLuaConfig 클래스가 추가되어 dedup_batch.lua 스크립트를 Spring 애플리케이션에 로드하고 관리합니다.
  • Kafka Consumer 속성 최적화: application.yml에서 max-poll-records, fetch.min.bytes, fetch.max.bytes 등 Kafka Consumer의 성능 관련 속성이 배치 처리에 맞게 조정되었습니다.
  • 로그 파일명 변경: 알림 로그 파일명이 notification-preview.log에서 notification.log로 변경되었습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 Kafka Consumer의 성능을 개선하기 위해 배치 처리와 Redis Lua 스크립트를 이용한 중복 제거 로직을 도입했습니다. 전반적으로 성능 개선 목표에 부합하는 좋은 변경이라고 생각합니다. 다만, 몇 가지 잠재적인 문제점과 개선 사항이 있어 리뷰 의견을 남깁니다. 주요 내용으로는 에러 처리 중 데이터 유실 가능성, Redis Lua 스크립트의 원자성 문제, 그리고 설정값 하드코딩 등이 있습니다. 아래의 상세 리뷰를 확인해주세요.

Comment on lines +49 to 51
} catch (Exception e) {
log.warn("[DESERIALIZE FAIL] offset={}", record.offset(), e);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

메시지 역직렬화에 실패했을 때 log.warn으로 기록만 하고 넘어간 뒤, 메소드 마지막에서 ack.acknowledge()를 호출하고 있습니다. 이 경우 역직렬화에 실패한 메시지는 처리되지 않고 유실될 수 있습니다. 데이터 유실은 심각한 문제로 이어질 수 있으므로, 실패한 메시지를 별도의 Dead Letter Queue(DLQ)로 보내거나, 배치를 재처리하는 등의 강력한 에러 처리 전략을 도입해야 합니다. 최소한 실패한 메시지의 원본 값이라도 로그로 남겨 추후 복구를 용이하게 하는 것을 고려해주세요.

Comment on lines +6 to +11
if redis.call('SETNX', key, 1) == 1 then
redis.call('EXPIRE', key, ttl)
results[i] = 1
else
results[i] = 0
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

SETNXEXPIRE를 순차적으로 호출하고 있습니다. SETNX 성공 후 EXPIRE가 실행되기 전에 Redis 서버가 다운되거나 스크립트 실행이 중단되면, TTL이 설정되지 않은 키가 영구적으로 남게 될 수 있습니다. 이는 의도치 않은 중복 제거 및 메모리 누수로 이어질 수 있습니다. Redis 2.6.12 버전부터 지원되는 SET key value EX seconds NX 명령어를 사용하면 SETNXEXPIRE를 원자적으로 실행할 수 있어 이 문제를 해결할 수 있습니다.

	if redis.call('SET', key, 1, 'EX', ttl, 'NX') then
		results[i] = 1
	else
		results[i] = 0
	end

factory.setAutoStartup(isWorker);

factory.setBatchListener(true);
factory.setConcurrency(6); // 🔥 핵심
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Kafka 컨슈머의 동시성(concurrency)을 6으로 하드코딩하셨습니다. 이 값은 실행 환경에 따라 튜닝이 필요할 수 있으므로, application.yml 파일에서 설정으로 관리하는 것이 유지보수 측면에서 더 좋습니다. Environment 객체를 사용하여 프로퍼티 값을 주입받는 것을 권장합니다. 예를 들어 application.ymlspring.kafka.listener.concurrency: 6과 같이 설정할 수 있습니다.

Suggested change
factory.setConcurrency(6); // 🔥 핵심
factory.setConcurrency(env.getProperty("spring.kafka.listener.concurrency", Integer.class, 6)); // 🔥 핵심

Comment on lines +15 to +16
public RedisScript<List> dedupBatchScript() {
DefaultRedisScript<List> script = new DefaultRedisScript<>();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

RedisScriptDefaultRedisScript에 raw type인 List를 사용하고 있습니다. NotificationSendDedupService에서 List<Long>으로 캐스팅하는 것을 고려할 때, RedisScript<List<Long>>과 같이 타입을 명시해주는 것이 타입 안정성을 높이고 코드 가독성을 향상시킵니다.

Suggested change
public RedisScript<List> dedupBatchScript() {
DefaultRedisScript<List> script = new DefaultRedisScript<>();
public RedisScript<List<Long>> dedupBatchScript() {
DefaultRedisScript<List<Long>> script = new DefaultRedisScript<>();

}

UsageNotificationEvent event = events.get(i);
String message = formatter.format(event, LocalDateTime.now(ZoneId.of("Asia/Seoul")));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

ZoneId.of("Asia/Seoul")과 같이 타임존을 하드코딩하면 다른 시간대에서 애플리케이션을 실행해야 할 때 문제가 될 수 있습니다. 타임존 정보는 application.yml과 같은 설정 파일로 분리하여 유연성을 확보하는 것이 좋습니다.

ack.acknowledge();

long elapsedMs = System.currentTimeMillis() - batchStart;
double tps = processed / (elapsedMs / 1000.0);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

TPS를 계산하는 과정에서 elapsedMs가 0이 될 경우 Division by zero 예외가 발생할 수 있습니다. 배치 처리가 매우 빠르게 완료될 경우 가능한 시나리오입니다. elapsedMs가 0인지 확인하는 방어 코드를 추가하는 것이 안전합니다.

Suggested change
double tps = processed / (elapsedMs / 1000.0);
double tps = (elapsedMs > 0) ? (double) processed / (elapsedMs / 1000.0) : 0.0;

@github-actions github-actions Bot added fix 버그 수정 size/M PR size: M labels Jan 25, 2026
@github-actions
Copy link
Copy Markdown

SonarQube Quality Summary (Community)

Quality Gate FAILED

Branch: feat/UPLUS-134
Compared to: default branch

Issues

  • 🐞 Bugs: 1
  • 🔐 Vulnerabilities: 2
  • 📎 Code Smells: 47

Measures

  • Coverage: 0%
  • Duplication: 0%

🔗 Dashboard: https://sonarqube.swthewhite.store/dashboard?id=api-message&branch=feat/UPLUS-134

Generated automatically by GitHub Actions.

@starboxxxx starboxxxx merged commit d1446e1 into develop Jan 25, 2026
6 of 8 checks passed
@starboxxxx starboxxxx deleted the feat/UPLUS-134 branch January 25, 2026 06:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 fix 버그 수정 size/M PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant