aws-lambdaspring-data-elasticsearchgraalvmaws-elasticsearchspring-native

Spring Native with RestHighLevelClient not connecting to AWS OpenSearch


I have a Lambda configured and running on "Custom runtime on Amazon Linux 2". GraalVM is the runtime used for our application.

When Lambda tries to make a connection with OpenSearch, it always results in "java.net.SocketTimeoutException: 60,000 milliseconds timeout on connection http-outgoing-0 [ACTIVE]".

Below are some key points used in our app:

  1. Both Lambda and OpenSearch are in same VPC
  2. Security groups are configured to allow all inbound and outbound traffic (for now)
  3. We use Elasticsearch's 'RestHighLevelClient' library to make a connection with OpenSearch
  4. Lambda's permissions has full OpenSearch Access.

Below is RestHighLevelClient Configuration:

public class OpenSearchConfig extends AbstractElasticsearchConfiguration {

  @Value("${aws.es.endpoint:defaultEndpoint}")
  private String endpoint;

  @Override
  public RestHighLevelClient elasticsearchClient() {
    return new RestHighLevelClient(RestClient.builder(HttpHost.create(endpoint)));
  }
}

Note: The Lambda to OpenSearch connection was working fine when we used 'Java 11 (Corretto)' as Runtime. Since we were facing cold start issues in Lambda, we planned to use GraalVM (supported by Spring Native) for faster start up and ended up in this road block. Increasing the socket connection timeout to any limit is not helping either.

Any suggestions is much appreciated. Thanks in advance.


Solution

  • It seems Spring's magic isn't much compatible with GraalVM. I have modified the configurations as below and it worked fine.

    @Configuration
    public class OpenSearchConfig {
    
      @Value("${aws.es.endpoint:defaultEndpoint}")
      private String endpoint;
    
      @Bean
      public RestHighLevelClient elasticsearchClient() {
        return new RestHighLevelClient(RestClient.builder(HttpHost.create(endpoint)));
      }
    }
    

    Above I have marked RestHighLevelClient as a bean explicitly and created the ElasticsearchOperations instance as below:

      private RestHighLevelClient restHighLevelClient;
      private ElasticsearchOperations elasticsearchOperations;
    
      @Autowired
      public Foo(RestHighLevelClient restHighLevelClient){
        this.restHighLevelClient = restHighLevelClient;
        this.elasticsearchOperations = new ElasticsearchRestTemplate(this.restHighLevelClient);
      }
    

    Hope this helps someone.