All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.snowflake.ingest.streaming.internal.StreamingIngestUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved.
 */

package net.snowflake.ingest.streaming.internal;

import static net.snowflake.ingest.utils.Constants.MAX_STREAMING_INGEST_API_CHANNEL_RETRY;
import static net.snowflake.ingest.utils.Constants.RESPONSE_ERR_GENERAL_EXCEPTION_RETRY_REQUEST;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.function.Function;
import net.snowflake.client.jdbc.internal.apache.http.client.methods.CloseableHttpResponse;
import net.snowflake.client.jdbc.internal.apache.http.client.methods.HttpUriRequest;
import net.snowflake.client.jdbc.internal.apache.http.impl.client.CloseableHttpClient;
import net.snowflake.ingest.connection.IngestResponseException;
import net.snowflake.ingest.connection.RequestBuilder;
import net.snowflake.ingest.connection.ServiceResponseHandler;
import net.snowflake.ingest.utils.ErrorCode;
import net.snowflake.ingest.utils.Logging;
import net.snowflake.ingest.utils.SFException;

public class StreamingIngestUtils {

  private static class DefaultStatusGetter
      implements Function {
    public DefaultStatusGetter() {}

    public Long apply(T input) {
      return input.getStatusCode();
    }
  }

  private static final DefaultStatusGetter defaultStatusGetter = new DefaultStatusGetter();

  private static final Logging LOGGER = new Logging(StreamingIngestUtils.class);

  private static final ObjectMapper objectMapper = new ObjectMapper();

  /**
   * How many milliseconds of exponential backoff to sleep before retrying the request again:
   *
   * 
    *
  • 0 or 1 failure => no sleep *
  • 2 failures => 1s *
  • 3 failures => 2s *
  • 4 or more failures => 4s *
* * @param executionCount How many unsuccessful attempts have been attempted * @return Sleep time in ms */ static long getSleepForRetryMs(int executionCount) { if (executionCount < 0) { throw new IllegalArgumentException( String.format( "executionCount must be a non-negative integer, passed: %d", executionCount)); } else if (executionCount < 2) { return 0; } else { final int effectiveExecutionCount = Math.min(executionCount, 4); return (1 << (effectiveExecutionCount - 2)) * 1000L; } } public static void sleepForRetry(int executionCount) { long sleepForRetryMs = getSleepForRetryMs(executionCount); if (sleepForRetryMs == 0) { return; } try { Thread.sleep(sleepForRetryMs); } catch (InterruptedException e) { throw new SFException(ErrorCode.INTERNAL_ERROR, e.getMessage()); } } static T executeWithRetries( Class targetClass, String endpoint, IStreamingIngestRequest payload, String message, ServiceResponseHandler.ApiName apiName, CloseableHttpClient httpClient, RequestBuilder requestBuilder) throws IOException, IngestResponseException { String payloadInString = serializeRequestToString(payload, message); return executeWithRetries( targetClass, endpoint, payloadInString, message, apiName, httpClient, requestBuilder); } static T executeWithRetries( Class targetClass, String endpoint, String payload, String message, ServiceResponseHandler.ApiName apiName, CloseableHttpClient httpClient, RequestBuilder requestBuilder) throws IOException, IngestResponseException { return (T) executeWithRetries( targetClass, endpoint, payload, message, apiName, httpClient, requestBuilder, defaultStatusGetter); } static T executeWithRetries( Class targetClass, String endpoint, String payload, String message, ServiceResponseHandler.ApiName apiName, CloseableHttpClient httpClient, RequestBuilder requestBuilder, Function statusGetter) throws IOException, IngestResponseException { int retries = 0; T response; HttpUriRequest request = requestBuilder.generateStreamingIngestPostRequest(payload, endpoint, message); do { try (CloseableHttpResponse httpResponse = httpClient.execute(request)) { response = ServiceResponseHandler.unmarshallStreamingIngestResponse( httpResponse, targetClass, apiName, httpClient, request, requestBuilder); } if (statusGetter.apply(response) == RESPONSE_ERR_GENERAL_EXCEPTION_RETRY_REQUEST) { LOGGER.logDebug( "Retrying request for streaming ingest, endpoint={}, retryCount={}, responseCode={}", endpoint, retries, statusGetter.apply(response)); retries++; sleepForRetry(retries); } else { return response; } } while (retries <= MAX_STREAMING_INGEST_API_CHANNEL_RETRY); return response; } public static String getShortname(final String fullname) { final String[] parts = fullname.split("/"); return parts[parts.length - 1]; } /** * Serialize the given Request to string. If there is any json processing exception, return the * given message with BUILD_REQUEST_FAILURE * * @param payload the request to serialize * @param message the message to return when facing json processing exception * @return the serialized request */ static String serializeRequestToString(IStreamingIngestRequest payload, String message) { try { return objectMapper.writeValueAsString(payload); } catch (JsonProcessingException e) { throw new SFException(e, ErrorCode.BUILD_REQUEST_FAILURE, message); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy