[Spring-Data-Elasticsearch] Timeout Exception

조갱·2022년 11월 26일
0

이슈 해결

목록 보기
5/15

ReadTimeoutException

ES에서 데이터를 SELECT, INSERT 할 때 위와 같은 에러가 발생했다.
항상 발생했던 것은 아니고, 특정 조건에서만 발생했다.

이상했던 점

  • (동일한 쿼리를) Kibana에서 SELECT 하면 빠르게 조회됨
  • ES에 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

profile
A fast learner.

0개의 댓글