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

net.snowflake.client.ingest.PushTask Maven / Gradle / Ivy

/*
 * Copyright (c) 2012-2016 Snowflake Computing Inc. All right reserved.
 */

package net.snowflake.client.ingest;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.net.MediaType;
import net.snowflake.client.IngestFilesTester;
import net.snowflake.client.core.SFSession;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.nio.file.Path;
import net.snowflake.client.TestConnectionUtil;

/**
 * Created by hyu on 10/18/16.
 */

public class PushTask implements Callable
{
  private static final String JSON_UTF8_CT = MediaType.JSON_UTF_8.toString();
  private ConcurrentHashMap pushedFiles = new ConcurrentHashMap<>();
  private AtomicInteger batchesRemaining;
  private List> batchOfFiles;
  private String jwtToken;
  private int inter_batch_delay = 0;
  private CloseableHttpClient httpClient;
  private String fqPipeName;
  private ObjectMapper objectMapper = new ObjectMapper();

  public PushTask(String jwtToken,
                  List> batchOfFiles,
                  CloseableHttpClient httpClient,
                  String fqPipeName)
  {
    this.batchOfFiles = batchOfFiles;
    this.jwtToken = jwtToken;
    this.httpClient = httpClient;
    this.fqPipeName = fqPipeName;
    this.batchesRemaining = new AtomicInteger(batchOfFiles.size());
  }

  @Override
  public Integer call() throws Exception
  {
    final ThreadLocalRandom rnd = ThreadLocalRandom.current();
    int cnt = 0;
    while(batchesRemaining.decrementAndGet() >=0 )
    {
      int batchIndex = batchOfFiles.size() - batchesRemaining.get() - 1;
      List files = batchOfFiles.get(batchIndex);
      final Boolean success = postFiles(files, jwtToken);
      if (success) {
        cnt += files.size();
        for (Path file : files)
        {
          final String f = file.getFileName().toString();
          pushedFiles.put(f, f);
        }
      } else {
        throw new IllegalStateException("post failed");
      }
      final int sl = inter_batch_delay +
          rnd.nextInt(1 + inter_batch_delay / 10);
      Thread.sleep(sl);
    }
    //and random amout up to 10% to desync threads...
    return cnt;
  }

  private Boolean postFiles(Collection csv_files, String jwt_token)
      throws Exception
  {
    final URI insert_endpoint = insertFilesEndpoint();
    say("pushing " + csv_files.size() + " files to " + insert_endpoint);
    final HttpPost post = new HttpPost(insert_endpoint);

    addAuthnHeader(post, jwt_token);

    final String body = createPostJsonBody(csv_files);

    final StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
    entity.setContentType(JSON_UTF8_CT);
    post.setEntity(entity);

    final ResponseHandler handler = new ResponseHandler()
    {
      @Override
      public Boolean handleResponse(HttpResponse response)
          throws IOException
      {
        final StatusLine statusLine = response.getStatusLine();
        final int statusCode = statusLine.getStatusCode();
        final HttpEntity responseEntity = response.getEntity();
        final String str = EntityUtils.toString(responseEntity);
        if (statusCode == 200) {
          say("insert returned: " + str);
          return true;
        } else {
          say("postFiles request failed got: " + statusLine + "\n:" + str);
          return false;
        }
      }
    };

    final int attempts = 60;

    for (int i = 1; i <= attempts; i++) {
      try {
        final Boolean ok = httpClient.execute(post, handler);
        assert ok != null;
        if (ok) {
          return true; //else retry
        }
      } catch (Exception e) {
        say("got error " + e +
            " during file push on attempt " + i + " of " + attempts);

      }

      Thread.sleep(1000);
    }

    throw new IllegalStateException("giving up on post");
  }

  private URI insertFilesEndpoint() throws URISyntaxException
  {
    return IngestFilesTester.insertFilesEndpoint(fqPipeName);
  }

  private static void say(String arg)
  {
    System.out.println(System.currentTimeMillis() + ":" +
        Thread.currentThread().getId() + "  " + arg);
  }

  public void setInterBatchDelay(int inter_batch_delay)
  {
    this.inter_batch_delay = inter_batch_delay;
  }

  private String createPostJsonBody(Collection csv_files)
      throws IOException
  {
    IngestPushFilesRequest req = new IngestPushFilesRequest();
    final List files =
        new ArrayList<>(csv_files.size());

    for (Path csv_file : csv_files)
    {
      final IngestPushFilesRequest.IngestFile file =
          new IngestPushFilesRequest.IngestFile();
      file.path = csv_file.getFileName().toString();
      try
      {
        file.size = Files.size(csv_file);
      }
      catch (IOException ex)
      {
        file.size = 100L;
      }
      files.add(file);
    }

    req.files = files;

    return this.objectMapper.writeValueAsString(req);
  }

  public static class IngestPushFilesRequest {
    public List files;

    public static final class IngestFile
    {
      public String path;
      public Long size;  //optional
    }
  }

  public ConcurrentHashMap getPushedFiles()
  {
    return this.pushedFiles;
  }

  private static void addAuthnHeader(HttpUriRequest request,
                                    String jwt_token)
  {
    request.setHeader(SFSession.SF_HEADER_AUTHORIZATION,
        "Bearer " + jwt_token);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy