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

io.rainfall.store.controllers.OutputLogController Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014-2019 Aurélien Broszniowski
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.rainfall.store.controllers;

import io.rainfall.store.data.Payload;
import io.rainfall.store.dataset.JobRecord;
import io.rainfall.store.dataset.OutputLogDataset;
import io.rainfall.store.dataset.OutputLogRecord;
import io.rainfall.store.dataset.RunDataset;
import io.rainfall.store.hdr.HdrData;
import io.rainfall.store.hdr.HistogramService;
import io.rainfall.store.values.ChangeReport;
import io.rainfall.store.values.Comparison;
import io.rainfall.store.values.OutputLog;
import io.rainfall.store.values.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.gson.Gson;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.LongStream;

import static io.rainfall.store.data.CompressionServiceFactory.compressionService;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

@Controller
@SuppressWarnings("unused")
public class OutputLogController extends ChildController<
    OutputLog, OutputLogRecord, JobRecord, OutputLogDataset> {

  private final HistogramService histogramService = new HistogramService();

  @Autowired
  private RunDataset runDataset;

  @Autowired
  private Gson gson;

  OutputLogController(OutputLogDataset dataset) {
    super(dataset, "Output log", "/outputs");
  }

  @PostMapping("/outputs/{jobId}")
  public ResponseEntity postRun(@PathVariable long jobId, @RequestBody OutputLog outputLog) {
    return super.post(jobId, outputLog);
  }

  @GetMapping("/jobs/{parentId}/outputs")
  @ResponseBody
  public List listOutputLogsByJobId(@PathVariable long parentId) {
    return dataset()
        .findOutputLogsByRunId(parentId);
  }

  @GetMapping(value = "/outputs/{id}/hdr", produces = APPLICATION_JSON_UTF8_VALUE)
  @ResponseBody
  public String getOutputHdrData(@PathVariable long id) {
    Payload payload = getRecord(id)
        .getPayloadRecord()
        .getValue();
    byte[] data = uncompress(payload);
    HdrData hdrData = hdrData(data);
    return toJson(hdrData);
  }

  @GetMapping(value = "/runs/{runId}/aggregate/{operation}", produces = APPLICATION_JSON_UTF8_VALUE)
  @ResponseBody
  public String getAggregateHdrData(@PathVariable long runId,
                                    @PathVariable String operation) {
    HdrData hdrData = getHdrData(runId, operation);
    return toJson(hdrData);
  }

  private HdrData getHdrData(long runId, String operation) {
    List records = dataset()
        .findOutputLogsByRunIdAndOperation(runId, operation);
    List> suppliers = records.stream()
        .map(this::uncompress)
        .map(this::streamSupplier)
        .collect(toList());
    return histogramService.aggregateHdrData(suppliers);
  }

  @GetMapping(value = "/compare/{sids}/{operation}", produces = APPLICATION_JSON_UTF8_VALUE)
  @ResponseBody
  public String getComparativeHdrData(@PathVariable String sids,
                                      @PathVariable String operation) {
    long[] ids = parseIds(sids);
    Map runs = LongStream.of(ids)
        .boxed()
        .collect(toMap(
            Function.identity(),
            id -> getHdrData(id, operation),
            (id1, id2) -> id1,
            LinkedHashMap::new
        ));
    Map pvalues = IntStream.range(0, ids.length)
        .boxed()
        .flatMap(i -> IntStream.range(i + 1, ids.length)
            .mapToObj(j -> new Pair(ids[i], ids[j]))
        )
        .collect(toMap(Function.identity(), pair -> {
          HdrData idata = runs.get(pair.getLeft());
          HdrData jdata = runs.get(pair.getRight());
          return histogramService.comparePercentiles(idata, jdata);
        }));
    Comparison comparison = new Comparison(runs, pvalues);
    return toJson(comparison);
  }

  private byte[] uncompress(Payload payload) {
    try {
      return compressionService(payload.getFormat())
          .decompress(payload);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  private Supplier streamSupplier(byte[] bytes) {
    return () -> new ByteArrayInputStream(bytes);
  }

  private HdrData hdrData(byte[] data) {
    return histogramService.readHdrData(() -> new ByteArrayInputStream(data));
  }

  @GetMapping(value = "/runs/{runId}/operations", produces = APPLICATION_JSON_UTF8_VALUE)
  @ResponseBody
  public List listOperationsByRunId(@PathVariable long runId) {
    return dataset().findOperationsByRunId(runId);
  }

  @GetMapping(value = "/runs/{sids}/common-operations", produces = APPLICATION_JSON_UTF8_VALUE)
  @ResponseBody
  public Set listCommonOperationsForRunsURL(@PathVariable String sids) {
    long[] runIds = parseIds(sids);
    return dataset().findCommonOperationsForRuns(runIds);
  }


  @GetMapping(path = "/runs/{runId}/regression/{threshold}", produces = APPLICATION_JSON_VALUE)
  @ResponseBody
  public String checkRegression(@PathVariable long runId,
                                @PathVariable float threshold) {
    long parentId = runDataset.getRecord(runId)
        .orElseThrow(() -> new IllegalArgumentException("Run not found for id: " + runId))
        .getParent()
        .getId();
    ChangeReport regressionCheck = runDataset.getLastBaselineID(parentId)
        .map(baselineID -> getChangeReport(baselineID, runId, threshold))
        .orElseGet(() -> new ChangeReport(threshold));
    return toJson(regressionCheck);
  }

  private ChangeReport getChangeReport(Long baselineID, long runId, double threshold) {
    List operations = dataset().findOperationsByRunId(runId);
    Map pvalues = operations.stream()
        .collect(toMap(Function.identity(), op ->
            compareHdrToBaseline(baselineID, runId, op)));
    Map belowThreshold = pvalues.entrySet()
        .stream()
        .filter(e -> e.getValue() < threshold)
        .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
    return new ChangeReport(baselineID, threshold, belowThreshold);
  }

  private double compareHdrToBaseline(long baselineID, long runID, String op) {
    HdrData baselineData = getHdrData(baselineID, op);
    HdrData hdrData = getHdrData(runID, op);
    return histogramService.comparePercentiles(baselineData, hdrData);
  }

  /**
   * Use Gson where Jackson fails
   */
  private String toJson(Object o) {
    return gson.toJson(o);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy