ES에서 데이터를 SELECT, INSERT 할 때 위와 같은 에러가 발생했다.
항상 발생했던 것은 아니고, 특정 조건에서만 발생했다.
FE에서 조회 시
, consumer 에서 삽입 시
였다.나는 커넥션 문제를 가장 유력한 후보로 생각하고 있었다.
그 이유는
처음 조회하면 5.4초가 걸리던 반면
두번째 조회에서는 0.24초가 걸렸기 때문
나는 구글형님을 믿는다.
그 어떠한 문제라도 '나만 모르는 것'은 없기 때문이다.
이미 누군가는 예전에 몰랐던 문제고, 그 해결책은 구글에 있다.
이번에도 도움을 받은 블로그가 있다.
위 블로그의 필자는 RestHighClient 를 사용하며 겪은 이슈이지만,
에러의 원인은 내가 생각하던것과 동일했다.
(참고로 본인은 ReactiveElasticsearchClient를 사용중이다.)
그리고 결론부터 말하자면,
ReactiveRestClients의 conenctTimeout, socketTimeout 설정과
HttpClient의 keep-alive 관련 설정 (SO_KEEPALIVE, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT)을 수정함으로써 문제를 해결할 수 있었다.
keep-alive와 관련된 각 옵션별 설명은 이 블로그를 참조하자!
이제 문제는 해결했으니, 원인을 파악해야 한다.
TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT
TCP Client의 keep-alive 설정은 기본적으로 7200초 (2시간) 이다.
그래서 계속적인 Request/Response가 없어서, ES와의 연결이 끊겼다면
Client에서는 이전에 가지고 있던 (끊어진) 세션으로 통신을 계속 재시도 하면서 timeout 이 난다.
그림으로 표현해보자면 아래와 같은 느낌이다.
그래서 나는 애초에 Client의 세션 유지 시간을 짧게 조정했다.
새로운 Connection을 맺는 비용이 생기더라도, 타임아웃보단 나으니까.
다시 그림으로 보면 아래와 같다.
ReactiveElasticsearchClient 의 configuration 을 수정한다.
전체 코드는 아니고, 이번 포스팅을 위해 필요한 코드만 예제로 첨부했다.
@Bean
override fun reactiveElasticsearchClient(): ReactiveElasticsearchClient {
val clientConfiguration = ClientConfiguration.builder()
// 뭔가 필요한 설정...
.withConnectTimeout(Connection Timeout 시간, MilSec 단위)
.withSocketTimeout(Socket Timeout 시간, MilSec 단위)
.withWebClientConfigurer {
it.mutate().clientConnector(
ReactorClientHttpConnector(
HttpClient.create().tcpConfiguration { tcpConfig ->
tcpConfig.option(ChannelOption.SO_KEEPALIVE, true)
.option(EpollChannelOption.TCP_KEEPIDLE, 적당한 시간)
.option(EpollChannelOption.TCP_KEEPINTVL, 적당한 시간)
.option(EpollChannelOption.TCP_KEEPCNT, 적당한 시간)
}
)
).build()
}
.build()
return ReactiveRestClients.create(clientConfiguration)
}
Reference
https://taes-k.github.io/2021/04/29/elasticsearch-timeout/
https://noritor82.tistory.com/entry/SOCKETTCP-KEEPALIVE-%EC%98%B5%EC%85%98-%EC%84%A4%EC%A0%95