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

com.microsoft.azure.kusto.ingest.ManagedStreamingIngestClient Maven / Gradle / Ivy

There is a newer version: 6.0.0
Show newest version
package com.microsoft.azure.kusto.ingest;

import com.azure.core.http.HttpClient;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobClientBuilder;
import com.azure.storage.blob.models.BlobStorageException;
import com.azure.storage.common.policy.RequestRetryOptions;
import com.microsoft.azure.kusto.data.Ensure;
import com.microsoft.azure.kusto.data.HttpClientProperties;
import com.microsoft.azure.kusto.data.StreamingClient;
import com.microsoft.azure.kusto.data.auth.ConnectionStringBuilder;
import com.microsoft.azure.kusto.data.exceptions.DataServiceException;
import com.microsoft.azure.kusto.data.exceptions.DataWebException;
import com.microsoft.azure.kusto.data.exceptions.OneApiError;
import com.microsoft.azure.kusto.ingest.exceptions.IngestionClientException;
import com.microsoft.azure.kusto.ingest.exceptions.IngestionServiceException;
import com.microsoft.azure.kusto.ingest.result.IngestionResult;
import com.microsoft.azure.kusto.ingest.source.*;

import com.microsoft.azure.kusto.ingest.utils.ExponentialRetry;
import com.microsoft.azure.kusto.ingest.utils.IngestionUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.lang.invoke.MethodHandles;
import java.net.URISyntaxException;
import java.util.UUID;

/**
 * 

ManagedStreamingIngestClient

*

* This class combines a managed streaming client with a queued streaming client, to create an optimized experience. * Since the streaming client communicates directly with the engine, it's more prone to failure, so this class * holds both a streaming client and a queued client. * It tries {@value ATTEMPT_COUNT} times using the streaming client, after which it falls back to the queued streaming client in case of failure. * If the size of the stream is bigger than {@value MAX_STREAMING_SIZE_BYTES}, it will fall back to the queued streaming client. *

*/ public class ManagedStreamingIngestClient extends IngestClientBase implements QueuedIngestClient { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static final int ATTEMPT_COUNT = 3; public static final int MAX_STREAMING_SIZE_BYTES = 4 * 1024 * 1024; public static final String CLASS_NAME = ManagedStreamingIngestClient.class.getSimpleName(); final QueuedIngestClientImpl queuedIngestClient; final StreamingIngestClient streamingIngestClient; private final ExponentialRetry exponentialRetryTemplate; private CloseableHttpClient httpClient = null; /** * @param dmConnectionString dm connection string * @return a new ManagedStreamingIngestClient * @throws URISyntaxException if the connection string is invalid * @deprecated - Ingest clients now automatically deduce the endpoint, use {@link #ManagedStreamingIngestClient(ConnectionStringBuilder, HttpClientProperties)} instead. * Creates a new ManagedStreamingIngestClient from a DM connection string, with default http client properties. * This method infers the engine connection string from the DM connection string. * For advanced usage, use {@link ManagedStreamingIngestClient#ManagedStreamingIngestClient(ConnectionStringBuilder, ConnectionStringBuilder)} */ public static ManagedStreamingIngestClient fromDmConnectionString(ConnectionStringBuilder dmConnectionString) throws URISyntaxException { return fromDmConnectionString(dmConnectionString, (HttpClientProperties) null); } /** * @param dmConnectionString dm connection string * @param properties additional properties to configure the http client * @return a new ManagedStreamingIngestClient * @throws URISyntaxException if the connection string is invalid * @deprecated - Ingest clients now automatically deduce the endpoint, use {@link #ManagedStreamingIngestClient(ConnectionStringBuilder, HttpClientProperties)} instead. * Creates a new ManagedStreamingIngestClient from a DM connection string. * This method infers the engine connection string from the DM connection string. * For advanced usage, use {@link ManagedStreamingIngestClient#ManagedStreamingIngestClient(ConnectionStringBuilder, ConnectionStringBuilder)} */ public static ManagedStreamingIngestClient fromDmConnectionString(ConnectionStringBuilder dmConnectionString, @Nullable HttpClientProperties properties) throws URISyntaxException { ConnectionStringBuilder engineConnectionString = new ConnectionStringBuilder(dmConnectionString); engineConnectionString.setClusterUrl(IngestClientBase.getQueryEndpoint(engineConnectionString.getClusterUrl())); return new ManagedStreamingIngestClient(dmConnectionString, engineConnectionString, properties); } /** * @param engineConnectionString engine connection string * @return a new ManagedStreamingIngestClient * @throws URISyntaxException if the connection string is invalid * @deprecated - Ingest clients now automatically deduce the endpoint, use {@link #ManagedStreamingIngestClient(ConnectionStringBuilder, HttpClientProperties)} instead. * Creates a new ManagedStreamingIngestClient from an engine connection string, with default http client properties. * This method infers the DM connection string from the engine connection string. * For advanced usage, use {@link ManagedStreamingIngestClient#ManagedStreamingIngestClient(ConnectionStringBuilder, ConnectionStringBuilder)} */ public static ManagedStreamingIngestClient fromEngineConnectionString(ConnectionStringBuilder engineConnectionString) throws URISyntaxException { return fromEngineConnectionString(engineConnectionString, null); } /** * @param engineConnectionString engine connection string * @param properties additional properties to configure the http client * @return a new ManagedStreamingIngestClient * @throws URISyntaxException if the connection string is invalid * @deprecated - Ingest clients now automatically deduce the endpoint, use {@link #ManagedStreamingIngestClient(ConnectionStringBuilder, HttpClientProperties)} instead. * Creates a new ManagedStreamingIngestClient from an engine connection string. * This method infers the DM connection string from the engine connection string. * For advanced usage, use {@link ManagedStreamingIngestClient#ManagedStreamingIngestClient(ConnectionStringBuilder, ConnectionStringBuilder)} */ public static ManagedStreamingIngestClient fromEngineConnectionString(ConnectionStringBuilder engineConnectionString, @Nullable HttpClientProperties properties) throws URISyntaxException { ConnectionStringBuilder dmConnectionString = new ConnectionStringBuilder(engineConnectionString); dmConnectionString.setClusterUrl(IngestClientBase.getIngestionEndpoint(engineConnectionString.getClusterUrl())); return new ManagedStreamingIngestClient(dmConnectionString, engineConnectionString, properties); } /** * @param ingestionEndpointConnectionStringBuilder - Endpoint for ingesting data, usually starts with "https://ingest-" * @param queryEndpointConnectionStringBuilder - Endpoint for querying data, does not include "ingest-" * @throws URISyntaxException if the connection string is invalid * @deprecated - This method is slated to be private. Use * {@link IngestClientFactory#createManagedStreamingIngestClient(ConnectionStringBuilder, ConnectionStringBuilder)} * instead. */ public ManagedStreamingIngestClient(ConnectionStringBuilder ingestionEndpointConnectionStringBuilder, ConnectionStringBuilder queryEndpointConnectionStringBuilder) throws URISyntaxException { this(ingestionEndpointConnectionStringBuilder, queryEndpointConnectionStringBuilder, null); } /** * @param ingestionEndpointConnectionStringBuilder - Endpoint for ingesting data, usually starts with "https://ingest-" * @param queryEndpointConnectionStringBuilder - Endpoint for querying data, does not include "ingest-" * @param properties - Additional properties to configure the http client * @throws URISyntaxException if the connection string is invalid * @deprecated - This method is slated to be private. Use * {@link IngestClientFactory#createManagedStreamingIngestClient(ConnectionStringBuilder, ConnectionStringBuilder, HttpClientProperties)} instead. * This constructor should only be used for advanced cases. If your endpoints are standard, or you do not know, use * {@link #ManagedStreamingIngestClient(ConnectionStringBuilder, HttpClientProperties)})} instead. */ public ManagedStreamingIngestClient(ConnectionStringBuilder ingestionEndpointConnectionStringBuilder, ConnectionStringBuilder queryEndpointConnectionStringBuilder, @Nullable HttpClientProperties properties) throws URISyntaxException { log.info("Creating a new ManagedStreamingIngestClient from connection strings"); queuedIngestClient = new QueuedIngestClientImpl(ingestionEndpointConnectionStringBuilder, properties); streamingIngestClient = new StreamingIngestClient(queryEndpointConnectionStringBuilder, properties); exponentialRetryTemplate = new ExponentialRetry(ATTEMPT_COUNT); } ManagedStreamingIngestClient(ConnectionStringBuilder connectionStringBuilder, @Nullable HttpClientProperties properties) throws URISyntaxException { log.info("Creating a new ManagedStreamingIngestClient from connection strings"); queuedIngestClient = new QueuedIngestClientImpl(connectionStringBuilder, properties); streamingIngestClient = new StreamingIngestClient(connectionStringBuilder, properties); exponentialRetryTemplate = new ExponentialRetry(ATTEMPT_COUNT); } public ManagedStreamingIngestClient(ConnectionStringBuilder connectionStringBuilder, @Nullable CloseableHttpClient httpClient) throws URISyntaxException { log.info("Creating a new ManagedStreamingIngestClient from connection strings"); queuedIngestClient = new QueuedIngestClientImpl(connectionStringBuilder, httpClient); streamingIngestClient = new StreamingIngestClient(connectionStringBuilder, httpClient); this.httpClient = httpClient; exponentialRetryTemplate = new ExponentialRetry(ATTEMPT_COUNT); } /** * @param resourceManager ingestion resources manager * @param storageClient - storage utilities * @param streamingClient - the streaming client * @deprecated - This method is slated to be private. Use * {@link IngestClientFactory#createManagedStreamingIngestClient(ConnectionStringBuilder)} instead. */ public ManagedStreamingIngestClient(ResourceManager resourceManager, AzureStorageClient storageClient, StreamingClient streamingClient) { log.info("Creating a new ManagedStreamingIngestClient from raw parts"); queuedIngestClient = new QueuedIngestClientImpl(resourceManager, storageClient); streamingIngestClient = new StreamingIngestClient(streamingClient); exponentialRetryTemplate = new ExponentialRetry(ATTEMPT_COUNT); } ManagedStreamingIngestClient(ResourceManager resourceManager, AzureStorageClient storageClient, StreamingClient streamingClient, ExponentialRetry retryTemplate) { log.info("Creating a new ManagedStreamingIngestClient from raw parts"); queuedIngestClient = new QueuedIngestClientImpl(resourceManager, storageClient); streamingIngestClient = new StreamingIngestClient(streamingClient); exponentialRetryTemplate = retryTemplate; } @Override protected IngestionResult ingestFromFileImpl(FileSourceInfo fileSourceInfo, IngestionProperties ingestionProperties) throws IngestionClientException, IngestionServiceException { Ensure.argIsNotNull(fileSourceInfo, "fileSourceInfo"); Ensure.argIsNotNull(ingestionProperties, "ingestionProperties"); fileSourceInfo.validate(); ingestionProperties.validate(); try { StreamSourceInfo streamSourceInfo = IngestionUtils.fileToStream(fileSourceInfo, true); return ingestFromStream(streamSourceInfo, ingestionProperties); } catch (FileNotFoundException e) { log.error("File not found when ingesting a file.", e); throw new IngestionClientException("IO exception - check file path.", e); } } /** * {@inheritDoc} *

*/ @Override protected IngestionResult ingestFromBlobImpl(BlobSourceInfo blobSourceInfo, IngestionProperties ingestionProperties) throws IngestionClientException, IngestionServiceException { Ensure.argIsNotNull(blobSourceInfo, "blobSourceInfo"); Ensure.argIsNotNull(ingestionProperties, "ingestionProperties"); blobSourceInfo.validate(); ingestionProperties.validate(); BlobClientBuilder blobClientBuilder = new BlobClientBuilder().endpoint(blobSourceInfo.getBlobPath()); if (httpClient != null) { blobClientBuilder.httpClient((HttpClient) httpClient); } BlobClient blobClient = blobClientBuilder.buildClient(); if (blobSourceInfo.getRawSizeInBytes() <= 0) { try { blobSourceInfo.setRawSizeInBytes(blobClient.getProperties().getBlobSize()); } catch (BlobStorageException e) { throw new IngestionServiceException( blobSourceInfo.getBlobPath(), "Failed getting blob properties: " + e.getMessage(), e); } } if (blobSourceInfo.getRawSizeInBytes() > MAX_STREAMING_SIZE_BYTES) { log.info("Blob size is greater than max streaming size ({} bytes). Falling back to queued.", blobSourceInfo.getRawSizeInBytes()); return queuedIngestClient.ingestFromBlob(blobSourceInfo, ingestionProperties); } IngestionResult result = streamWithRetries(blobSourceInfo, ingestionProperties, blobClient); if (result != null) { return result; } return queuedIngestClient.ingestFromBlob(blobSourceInfo, ingestionProperties); } private IngestionResult streamWithRetries(SourceInfo sourceInfo, IngestionProperties ingestionProperties, @Nullable BlobClient blobClient) throws IngestionClientException, IngestionServiceException { ExponentialRetry retry = new ExponentialRetry(exponentialRetryTemplate); return retry.execute(currentAttempt -> { try { if (blobClient != null) { String clientRequestId = String.format("KJC.executeManagedStreamingIngest.ingestFromBlob;%s;%d", sourceInfo.getSourceId(), currentAttempt); return streamingIngestClient.ingestFromBlob((BlobSourceInfo) sourceInfo, ingestionProperties, blobClient, clientRequestId); } else { String clientRequestId = String.format("KJC.executeManagedStreamingIngest.ingestFromStream;%s;%d", sourceInfo.getSourceId(), currentAttempt); return streamingIngestClient.ingestFromStream((StreamSourceInfo) sourceInfo, ingestionProperties, clientRequestId); } } catch (Exception e) { if (e instanceof IngestionServiceException && e.getCause() != null && e.getCause() instanceof DataServiceException && e.getCause().getCause() != null && e.getCause().getCause() instanceof DataWebException) { DataWebException webException = (DataWebException) e.getCause().getCause(); OneApiError oneApiError = webException.getApiError(); if (oneApiError.isPermanent()) { throw e; } } log.info(String.format("Streaming ingestion failed attempt %d", currentAttempt), e); if (sourceInfo instanceof StreamSourceInfo) { try { ((StreamSourceInfo) sourceInfo).getStream().reset(); } catch (IOException ioException) { throw new IngestionClientException("Failed to reset stream", ioException); } } } return null; }); } @Override protected IngestionResult ingestFromResultSetImpl(ResultSetSourceInfo resultSetSourceInfo, IngestionProperties ingestionProperties) throws IngestionClientException, IngestionServiceException { Ensure.argIsNotNull(resultSetSourceInfo, "resultSetSourceInfo"); Ensure.argIsNotNull(ingestionProperties, "ingestionProperties"); resultSetSourceInfo.validate(); ingestionProperties.validateResultSetProperties(); try { StreamSourceInfo streamSourceInfo = IngestionUtils.resultSetToStream(resultSetSourceInfo); return ingestFromStream(streamSourceInfo, ingestionProperties); } catch (IOException ex) { String msg = "Failed to read from ResultSet."; log.error(msg, ex); throw new IngestionClientException(msg, ex); } } @Override protected IngestionResult ingestFromStreamImpl(StreamSourceInfo streamSourceInfo, IngestionProperties ingestionProperties) throws IngestionClientException, IngestionServiceException { Ensure.argIsNotNull(streamSourceInfo, "streamSourceInfo"); Ensure.argIsNotNull(ingestionProperties, "ingestionProperties"); streamSourceInfo.validate(); ingestionProperties.validate(); UUID sourceId = streamSourceInfo.getSourceId(); if (sourceId == null) { sourceId = UUID.randomUUID(); } byte[] streamingBytes; try { streamingBytes = IngestionUtils.readBytesFromInputStream(streamSourceInfo.getStream(), MAX_STREAMING_SIZE_BYTES + 1); } catch (IOException e) { throw new IngestionClientException("Failed to read from stream.", e); } // ByteArrayInputStream's close method is a no-op, so we don't need to close it. ByteArrayInputStream byteArrayStream = new ByteArrayInputStream(streamingBytes); if (streamingBytes.length > MAX_STREAMING_SIZE_BYTES) { log.info("Stream size is greater than max streaming size ({} bytes). Falling back to queued.", streamingBytes.length); StreamSourceInfo managedSourceInfo = new StreamSourceInfo(new SequenceInputStream(byteArrayStream, streamSourceInfo.getStream()), streamSourceInfo.isLeaveOpen(), sourceId, streamSourceInfo.getCompressionType()); return queuedIngestClient.ingestFromStream(managedSourceInfo, ingestionProperties); } if (!streamSourceInfo.isLeaveOpen()) { // From this point we don't need the original stream anymore, we cached it try { streamSourceInfo.getStream().close(); } catch (IOException e) { log.warn("Failed to close stream", e); } } StreamSourceInfo managedSourceInfo = new StreamSourceInfo(byteArrayStream, true, sourceId, streamSourceInfo.getCompressionType()); try { IngestionResult result = streamWithRetries(managedSourceInfo, ingestionProperties, null); if (result != null) { return result; } return queuedIngestClient.ingestFromStream(managedSourceInfo, ingestionProperties); } finally { try { managedSourceInfo.getStream().close(); } catch (IOException e) { log.warn("Failed to close byte stream", e); } } } @Override protected String getClientType() { return CLASS_NAME; } @Override public void close() throws IOException { queuedIngestClient.close(); streamingIngestClient.close(); } @Override public void setQueueRequestOptions(RequestRetryOptions queueRequestOptions) { queuedIngestClient.setQueueRequestOptions(queueRequestOptions); } @Override public IngestionResourceManager getResourceManager() { return queuedIngestClient.getResourceManager(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy