โ๏ธ 1. ์๋ก
๊ฐ๋ฐ์๋ก์ ์คํ์์ค์ ๊ธฐ์ฌํ๋ ์ผ์ ์ธ์ ๊ฐ ๊ผญ ํด๋ณด๊ณ ์ถ์ ๋ชฉํ ์ค ํ๋์์ต๋๋ค. ํ์ง๋ง ๋ง์ ์๋ํ๋ ค๊ณ ํ๋ฉด, ์ด๋์๋ถํฐ ์์ํด์ผ ํ ์ง ๋ง๋งํ ๊ฒ์ด ํ์ค์ด์์ต๋๋ค.
๊ทธ๋ฌ๋ ์ค, ์คํ์์ค ๊ธฐ์ฌ ๋ฐฉ๋ฒ์ ์๋ดํด์ฃผ๋ ์ปค๋ฎค๋ํฐ๋ฅผ ์๊ฒ ๋์๊ณ , ๊ทธ๊ณณ์์ ๋ฉํ ๋ง ํ๋ก๊ทธ๋จ์ ์ฐธ์ฌํ๊ฒ ๋์์ต๋๋ค. ์ด์๋ฅผ ์ฐพ๊ณ , ๊ธฐ์ฌํ ๋ถ๋ถ์ ๊ณ ๋ฏผํ๋ฉฐ, ๋ณธ๊ฒฉ์ ์ผ๋ก ์คํ์์ค์ ์ธ๊ณ์ ๋ฐ์ ๋ค์ด๊ฒ ๋์์ฃ .
Kafka๋ฅผ ๊ณต๋ถํ๋ ์ด๋ ๋ , ๋ฌธ๋ Spring Kafka์ ๋ด๋ถ ๊ตฌํ์ด ๊ถ๊ธํด์ก๊ณ , ์์ฐ์ค๋ฝ๊ฒ GitHub ์ ์ฅ์๋ฅผ ๋ค์ฌ๋ค๋ณด๊ฒ ๋์์ต๋๋ค. ๊ฑฐ๊ธฐ์ ๋ฐ๊ฒฌํ ํ๋์ ์ด์ โ ์์ ๋ณด์ด์ง๋ง ์ฌ์ฉ์ ํผ๋์ ์ผ๊ธฐํ ์ ์๋ ๋ฌธ์ โ ๋ฅผ ๊ณ๊ธฐ๋ก ์์ ์ฒซ Pull Request๋ฅผ ๋ง๋ค๊ฒ ๋์๊ณ , ์ด ์ข๊ฒ๋ ๋จธ์ง๋๋ฉฐ Spring Kafka์ ๊ณต์ Contributor๊ฐ ๋ ์ ์์์ต๋๋ค.
์ด ๊ธ์์๋ ๊ทธ ์ฌ์ ์ ๊ณต์ ํ๋ฉฐ, ์ด๋ค ์ด์๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์ด๋ป๊ฒ ๊ธฐ์ฌํ๋์ง๋ฅผ ์ ๋ฆฌํด๋ณด๋ ค ํฉ๋๋ค. ์คํ์์ค ๊ธฐ์ฌ๋ฅผ ๊ฟ๊พธ์ง๋ง ๋ง์ฐํ ๋ถ๋ค๊ป ์๊ฒ๋๋ง ๋์์ด ๋์์ผ๋ฉด ํฉ๋๋ค.
โ๏ธ 2. ๋ณธ๋ก
๐ค 2.1. ์ด์ ์ ํ ๊ณผ์
์ด์๋ฅผ ์ ์ ํ ๋ ๊ฐ์ฅ ์ค์ํ๊ฒ ์๊ฐํ ๊ธฐ์ค์ ๊ธฐ์ฌ๊ฐ ๋น๊ต์ ์ฌ์ด๊ฐ?, ๊ทธ๋ฆฌ๊ณ ํด๋น ํ๋ก์ ํธ์ ์์ค ์ฝ๋๋ฅผ ๊น์ด ์ดํดํ์ง ์๋๋ผ๋ ์์ ์ด ๊ฐ๋ฅํ๊ฐ? ์์ต๋๋ค.
์ด๋ฌํ ๊ธฐ์ค์ ๋ฐ๋ผ Apache ํ๋ก์ ํธ, Spring ํ๋ก์ ํธ ๋ฑ ์ฌ๋ฌ ์คํ์์ค ์ ์ฅ์๋ฅผ ๋๋ฌ๋ณด๋ฉฐ ์ ์ ํ ์ด์๋ฅผ ์ฐพ๊ธฐ ์์ํ์ต๋๋ค.
์ฒ์์๋ Apache Jackrabbit Oak ํ๋ก์ ํธ์์ ๊ธฐ์ฌํ ๋งํ ์ด์๋ฅผ ์ฐพ์ ์ ์์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ ๋ค์์ ์ค๋ช ํ๊ฒ ์ง๋ง, ๊ฒฐ๊ณผ์ ์ผ๋ก ํด๋น PR์ ๋จธ์ง๋์ง ๋ชปํ๊ณ , ๋ค์ ์๋ก์ด ์ด์๋ฅผ ์ฐพ์ ๋์ฐ์ต๋๋ค.
๊ทธ๋ฌ๋ ์ค, Spring-Kafka ์์ KafkaTemplate Bean ์ด๋ฆ ๋ถ์ผ์น ์ด์ ๋ฅผ ๋ฐ๊ฒฌํ๊ฒ ๋์๊ณ , ๊ทธ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๊ธฐ์ฌํ๊ฒ ๋์์ต๋๋ค.
๐ 2.2. ์ด์: KafkaTemplate Bean ์ด๋ฆ ๋ถ์ผ์น
Spring Kafka์ ๊ณต์ ๋ฌธ์์์๋ @RetryableTopic
์ด ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉํ KafkaTemplate ๋น์ ์ด๋ฆ์ defaultRetryTopicKafkaTemplate
๋ผ๊ณ ๋ช
์ํ๊ณ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ค์ @RetryableTopic
์ JavaDoc์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ค๋ช
ํ๊ณ ์์์ต๋๋ค.
* If not specified, a bean with name {@code retryTopicDefaultKafkaTemplate} or {@code kafkaTemplate} will be looked up.
์ฆ, JavaDoc์๋ retryTopicDefaultKafkaTemplate, ๊ณต์ ๋ฌธ์์๋ defaultRetryTopicKafkaTemplate๋ผ๋ ์ด๋ฆ์ด ๋์ ์์๊ณ , ํ ์คํธ ์ฝ๋ ์ผ๋ถ๋ JavaDoc ์ชฝ ์ด๋ฆ์ ๋ฐ๋ผ๊ฐ๊ณ ์์์ต๋๋ค. ์ด๋ก ์ธํด ์ด๋ค ๋น ์ด๋ฆ์ ์ฌ์ฉํด์ผ ํ๋์ง ๋ช ํํ์ง ์์์ต๋๋ค.
๐ ํด๋น ์ด์๋ Spring Kafka GitHub Issue #3514 ์์ ๋ ผ์๋์์ต๋๋ค.
โ๐ป 2.3. PR: JavaDoc ์ ์
๋จผ์ , @RetryableTopic
์ด๋
ธํ
์ด์
ํด๋์ค์ JavaDoc์ ๊ณต์ ๋ฌธ์์ ์ผ์นํ๋๋ก ์์ ํ์ต๋๋ค.
Before:
* The bean name of the {@link org.springframework.kafka.core.KafkaTemplate} bean that
* will be used to forward the message to the retry and Dlt topics. If not specified,
* a bean with name {@code retryTopicDefaultKafkaTemplate} or {@code kafkaTemplate}
* will be looked up.
*
* @return the kafkaTemplate bean name.
After:
* The bean name of the {@link org.springframework.kafka.core.KafkaTemplate} bean that
* will be used to forward the message to the retry and Dlt topics. If not specified,
* a bean with name {@code defaultRetryTopicKafkaTemplate} or {@code kafkaTemplate}
* will be looked up.
*
* @return the kafkaTemplate bean name.
bean ์ผ๋ก retryTopicDefaultKafkaTemplate ๋์ defaultRetryTopicKafkaTemplate ์ฌ์ฉํ๋ค๊ณ ์์ ์ ํ์์ต๋๋ค.
์ด ์์ ์ผ๋ก ๊ณต์ ๋ฌธ์์ JavaDoc์ด ์ผ์นํ๊ฒ ๋์๊ณ , ์ค์ ์ฌ์ฉ์๋ค์ด ํผ๋ ์์ด ์ฌ๋ฐ๋ฅธ ๋น ์ด๋ฆ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ต๋๋ค.
๐ Commit Message โ Fix: Replace retryTopicDefaultKafkaTemplate with defaultRetryTopicKafkaTemplate in docs
๐ป 2.4. PR: ํ ์คํธ ์ฝ๋ ์ ์
๋ํ ํ
์คํธ ์ฝ๋ ๋ด์์๋ ์๋ชป๋ ๋น ์ด๋ฆ ๋ฌธ์์ด์ ์ง์ ๋ช
์ํ๊ณ ์์์ต๋๋ค. RetryTopicConfigurationProviderTests
๋ผ๋ ํ
์คํธ ํด๋์ค์์ ์ฌ์ฉ ์ค์ด์๋๋ฐ, ์ด ๋ถ๋ถ์ ์์ RetryTopicBeanNames.DEFAULT_KAFKA_TEMPLATE_BEAN_NAME
์ ์ฌ์ฉํ๋๋ก ๋ฆฌํฉํฐ๋งํ์ต๋๋ค.
Before:
@Test
void shouldProvideFromAnnotation() {
// setup
willReturn(kafkaOperations).given(beanFactory).getBean("retryTopicDefaultKafkaTemplate", KafkaOperations.class);
// given
RetryTopicConfigurationProvider provider = new RetryTopicConfigurationProvider(beanFactory);
RetryTopicConfiguration configuration = provider.findRetryConfigurationFor(topics, annotatedMethod, bean);
RetryTopicConfiguration configurationFromClass = provider
.findRetryConfigurationFor(topics, null, AnnotatedClass.class, bean);
// then
then(this.beanFactory).should(times(0)).getBeansOfType(RetryTopicConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(configurationFromClass).isNotNull();
}
@Test
void shouldProvideFromMetaAnnotation() {
// setup
willReturn(kafkaOperations).given(beanFactory).getBean("retryTopicDefaultKafkaTemplate", KafkaOperations.class);
// given
RetryTopicConfigurationProvider provider = new RetryTopicConfigurationProvider(beanFactory);
RetryTopicConfiguration configuration = provider.findRetryConfigurationFor(topics, metaAnnotatedMethod, bean);
RetryTopicConfiguration configurationFromClass = provider
.findRetryConfigurationFor(topics, null, MetaAnnotatedClass.class, bean);
// then
then(this.beanFactory).should(times(0)).getBeansOfType(RetryTopicConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(configuration.getConcurrency()).isEqualTo(3);
assertThat(configurationFromClass).isNotNull();
assertThat(configurationFromClass.getConcurrency()).isEqualTo(3);
}
After:
@Test
void shouldProvideFromAnnotation() {
// setup
willReturn(kafkaOperations).given(beanFactory).getBean(RetryTopicBeanNames.DEFAULT_KAFKA_TEMPLATE_BEAN_NAME, KafkaOperations.class);
// given
RetryTopicConfigurationProvider provider = new RetryTopicConfigurationProvider(beanFactory);
RetryTopicConfiguration configuration = provider.findRetryConfigurationFor(topics, annotatedMethod, bean);
RetryTopicConfiguration configurationFromClass = provider
.findRetryConfigurationFor(topics, null, AnnotatedClass.class, bean);
// then
then(this.beanFactory).should(times(0)).getBeansOfType(RetryTopicConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(configurationFromClass).isNotNull();
}
@Test
void shouldProvideFromMetaAnnotation() {
// setup
willReturn(kafkaOperations).given(beanFactory).getBean(RetryTopicBeanNames.DEFAULT_KAFKA_TEMPLATE_BEAN_NAME, KafkaOperations.class);
// given
RetryTopicConfigurationProvider provider = new RetryTopicConfigurationProvider(beanFactory);
RetryTopicConfiguration configuration = provider.findRetryConfigurationFor(topics, metaAnnotatedMethod, bean);
RetryTopicConfiguration configurationFromClass = provider
.findRetryConfigurationFor(topics, null, MetaAnnotatedClass.class, bean);
// then
then(this.beanFactory).should(times(0)).getBeansOfType(RetryTopicConfiguration.class);
assertThat(configuration).isNotNull();
assertThat(configuration.getConcurrency()).isEqualTo(3);
assertThat(configurationFromClass).isNotNull();
assertThat(configurationFromClass.getConcurrency()).isEqualTo(3);
}
"retryTopicDefaultKafkaTemplate"
์ผ๋ก ๋์ด์๋ Bean ์ด๋ฆ ๋ถ๋ถ์ RetryTopicBeanNames.DEFAULT_KAFKA_TEMPLATE_BEAN_NAME
๋ก ๋ณ๊ฒฝํ์์ต๋๋ค.
์ด๋ฅผ ํตํด ๋น ์ด๋ฆ๋ ๋ณ๊ฒฝํ์๊ณ , ํ๋์ฝ๋ฉ๋ ๋ฌธ์์ด์ ์ ๊ฑฐํ๋ฉด์, ์์๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก ์ฝ๋์ ์ผ๊ด์ฑ๊ณผ ์์ ์ฑ์ ํ๋ณดํ์ต๋๋ค. ์ถํ KafkaTemplate ๋น ์ด๋ฆ์ด ๋ณ๊ฒฝ๋๋๋ผ๋ ์์๋ง ์์ ํ๋ฉด ๋๋ฏ๋ก ์ ์ง๋ณด์๊ฐ ์ฉ์ดํด์ก์ต๋๋ค.
๐ Commit Message โ Fix: Replace retryTopicDefaultKafkaTemplate with RetryTopicBeanNames.DEFAULT_KAFKA_TEMPLATE_BEAN_NAME in Test Code
๊ทธ ๊ฒฐ๊ณผ, Spring Kafka ์ Contributor ๊ฐ ๋์๊ณ , ์ ์์ ์ฒซ ์คํ ์์ค ๊ธฐ์ฌ์ ์ฑ๊ณตํ๊ฒ ๋์์ต๋๋ค.
ํด๋น PR ๋ด์ฉ์ ํ์ธํ๊ณ ์ถ๋ค๋ฉด, Spring Kafka PR ๊ธฐ์ฌ ์ฑ๊ณต ํด๋น ๋งํฌ๋ฅผ ํด๋ฆญํ์๊ธฐ ๋ฐ๋๋๋ค.
๐คฃ 2.5. ๋ฐฐ์: Apache Jackrabbit Oak ๊ธฐ์ฌ ์คํจ ๊ฒฝํ
์์์ ์ธ๊ธํ์์ง๋ง, Spring Kafka ๋ฅผ ๊ธฐ์ฌํ๊ธฐ ์ ์, Apache Jackrabbit Oak ์ ์ด์๋ฅผ ํ๋ ๋จผ์ ์ฐพ์ ์ ์์์ต๋๋ค.
๐ก Apache Jackrabbit Oak ?
ํ์ผ์ด๋ ๋ฌธ์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ์ ์ฅํ๊ณ ๊ด๋ฆฌํด์ฃผ๋ ์์คํ ์ ๋๋ค.
Java๋ก ๋ง๋ค์ด์ก๊ณ , Adobe Experience Manager(AEM) ๊ฐ์ ์ ๋ช ํ ์ฝํ ์ธ ๊ด๋ฆฌ ์์คํ ์์ ๋ด๋ถ ์ ์ฅ์๋ก ์ฌ์ฉ๋๊ณ ์์ต๋๋ค.
์๋ฅผ ๋ค๋ฉด, ์น์ฌ์ดํธ์์ ํ์ด์ง๋ฅผ ๋ง๋ค๊ฑฐ๋ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ ๋, ๊ทธ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฐ Oak๊ฐ ์ฐ์ผ ์ ์์ต๋๋ค.
ํ์ผ์ด๋ ํด๋๋ฅผ ๋๋ ํ ๋ฆฌ์ฒ๋ผ ๊ด๋ฆฌํ๊ณ , ๋๊ฐ ์ธ์ ๋ฐ๊ฟจ๋์ง ๊ธฐ๋ก๋ ๋จ๊ธธ ์ ์์ด์, ๋ฌธ์ ๊ด๋ฆฌ ์์คํ ์ด๋ CMS์ ๋ฑ ๋ง๋ ๊ตฌ์กฐ์ ๋๋ค.
๋น์ ์ด์ ๋ด์ฉ์ ๋ค์๊ณผ ๊ฐ์๋๋ฐ
MAX_SEGMENT_SIZE
์์๊ฐ Segment์ SegmentDataUtil s์ ์ค๋ณต ์ ์๋์ด ์์ผ๋ ํ๋๋ก ๊ณต์ ํ ์ ์๊ฒ ํด๋ฌ๋ผ๋ ๋ด์ฉ์ด์์ต๋๋ค.
Segment ๋ผ๋ ํด๋์ค์์ static final int MAX_SEGMENT_SIZE = 1 << 18;
๋ก ๋์ด์๋ ๋ถ๋ถ์ public
์ ๋ฌ์์ฃผ๊ณ , ๊ทธ๊ฑธ SegmentDataUtil ์์ ์ฌ์ฉ ํ ์ ์๊ฒ ํด์ฃผ๋๊ฒ ์ ๋ถ์์ต๋๋ค.
๊ทธ๋์ ์๋์ ๊ฐ์ด, ์ฝ๋๋ฅผ ์์ ํ๋ ค PR ์ ๋ณด๋์ต๋๋ค.
ํ์ง๋ง ๊ฒฐ๊ณผ๋ Merge ์คํจ์์ต๋๋ค.
์ด์ ๋ ๊ฐ๋จํ์ต๋๋ค.
์ด๋ฏธ ์ ๋ณด๋ค ๋จผ์ ํด๋น ์ด์๋ฅผ ์บ์นํ๊ณ PR ์ ์ฌ๋ฆฐ ์ฌ๋์ด ์์๊ธฐ ๋๋ฌธ์ด์์ต๋๋ค.
์คํ ์์ค ์ํ๊ณ๋ฅผ ๋ณด๋ฉด, ์ฒ์์๋ ์ ๋ชฐ๋ผ์ ์ค์ ํ ์ ์๋ 2๊ฐ์ง๊ฐ ์์ต๋๋ค. ํ๋๋ ์๋ฃ ๋ ์ด์์ธ๋ฐ๋ ์ด์๊ฐ Close ๋์ด์์ง ์์๊ฒ ์์ ์ ์๋ค๋ ๊ฒ์ ๋๋ค.
๋ ๋ค๋ฅธ ํ๋๋ ๋๊ตฐ๊ฐ ํด๋น ์ด์๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Assign ์ ๋ฐ์๋๋ฐ, ๊ทธ๊ฒ์ ๊ฐ๋ก์ฑ๋ ํ๋์ ํด์๋ ์๋๋ค๋ ๊ฒ์ ๋๋ค.
๋ณดํต์ ์ด์์ ๋๊ธ๋ก ๋ณธ์ธ์ด ํด๊ฒฐํ๊ฒ ๋ค๊ณ ๋ฆฌํ์ ๋ฌ์๋๊ณ ์งํ์ ํฉ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ์ด๋ ํ ์ด์๋ฅผ ํด๊ฒฐํ๊ณ ์ ํ๋ค๋ฉด, ๊ผญ ๋๊ธ์ ๋ณธ์ธ์ด ํด๊ฒฐํ๊ฒ ๋ค๋ ์์ฌ๋ฅผ ๋ฐํ๊ณ , ์งํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ฌ๋์ด ์งํ์ ํ๊ฒ ๋ค๊ณ ๋๊ธ์ ์ด๋ฏธ ๋จ๊ธด ์ํ๋ผ๋ฉด, ๊ทธ๊ฑธ ๊ฐ๋ก์ฑ์๋ ์๋ฉ๋๋ค. ๋ค๋ง, ๋๊ธ์ด ๋ฌ๋ฆฌ๊ณ ํ์ฐธ์ด ์ง๋ฌ๋๋ฐ๋ ํด๊ฒฐ์ด ๋์ง ์์ ์ด์๋ผ๋ฉด, ํด๋น ์ด์๊ฐ ํ์ฌ ํด๊ฒฐ ์ค์ธ์ง, ๊ทธ๊ฒ ์๋๋ผ๋ฉด ๋ด๊ฐ ํด๊ฒฐ์ ํด๋ ๋๋์ง ๋๊ธ๋ก ๋ฌผ์ด๋ณด๊ณ ์งํ์ ํ๋ ๋ฐฉํฅ๋ ์์ต๋๋ค.
โ๏ธ 3. ๊ฒฐ๋ก
์ด๋ฒ ๊ธฐ์ฌ๋ฅผ ํตํด Spring Kafka ๋ฌธ์์ ์ฝ๋์ ๋ถ์ผ์น ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ฉฐ, ๋ณด๋ค ๋ช ํํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ๊ธฐ๋ฐ์ ๋ง๋ จํ๋ ๋ฐ ์์ ๋ณดํฌ์ด ๋ ์ ์์์ต๋๋ค.
๋น๋ก ์๊ณ ์ฌ์ํ ๋ณ๊ฒฝ์ฒ๋ผ ๋ณด์ผ ์ ์์ง๋ง, ์ค์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ง์ ์ ์ธ ์ํฅ์ ์ค ์ ์๋ค๋ ์ ์์ ๊ทธ ์๋ฏธ๋ ๊ฒฐ์ฝ ์์ง ์์์ต๋๋ค. ๋ฌด์๋ณด๋ค ์คํ์์ค ์ํ๊ณ์ ๋ฌธํ, ํ๋ก์ธ์ค, ํ์ ๋ฐฉ์์ ์ค์ ๋ก ์ฒดํํ๊ณ ๋ฐฐ์ธ ์ ์์๋ ๊ฐ์ง ๊ฒฝํ์ด์์ต๋๋ค.
์คํ์์ค ๊ธฐ์ฌ๋ฅผ ์ด๋ ต๊ฒ๋ง ๋๋ผ๋ ๋ถ๋ค๋ ๋ง๊ฒ ์ง๋ง, ์๋ฒฝํ๊ฒ ์๋ ์ํ์์ ์์ํ๋ ์ฌ๋์ ์์ต๋๋ค. ์ ์ญ์ ๋ฌธ์ ํ๋ ๊ณ ์น๊ณ , ํ ์คํธ ์ฝ๋ ํ ์ค ๋ฐ๊พธ๋ ๋ฐ์ ์์ํ์ต๋๋ค.
์์ ๊ธฐ์ฌ๋ ์ถฉ๋ถํ ์๋ฏธ ์๊ณ , ์คํ์์ค ์ปค๋ฎค๋ํฐ๋ ๊ทธ๋ฐ ๊ธฐ์ฌ๋ฅผ ์์คํ ์ฌ๊น๋๋ค.
๐จ โ์ค์ํ ๊ฑด ์๋ฒฝํจ์ด ์๋๋ผ ์์์ ๋๋ค. ์ง๊ธ ์ฌ๋ฌ๋ถ๋ ์ฒซ ๋ฐ์ ๋ด๋๋๋ณด์ธ์.โ