/*
* Copyright 2014 Ran Meng
*
* 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 com.github.totyumengr.minicubes.cluster;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import org.hibernate.validator.constraints.NotBlank;
import org.roaringbitmap.RoaringBitmap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author mengran
*
*/
@Controller
public class BootTimeSeriesMiniCubeController {
private static final Logger LOGGER = LoggerFactory.getLogger(BootTimeSeriesMiniCubeController.class);
private ObjectMapper objectMapper = new ObjectMapper();
public static final String OK = "ok";
@Autowired
private TimeSeriesMiniCubeManager manager;
@RequestMapping(value="/status", method=RequestMethod.GET)
public @ResponseBody Map<String, List<String>> status() {
Map<String, List<String>> status = new LinkedHashMap<>();
Collection<String> allCubeIds = manager.allCubeIds();
status.put("working", allCubeIds.stream().filter(e -> !e.startsWith("?")).collect(Collectors.toList()));
status.put("awaiting", allCubeIds.stream().filter(e -> e.startsWith("?")).collect(Collectors.toList()));
LOGGER.info("All miniCube size in cluster: {}, working/awaiting is {}/{}",
allCubeIds.size(), status.get("working").size(), status.get("awaiting").size());
return status;
}
@RequestMapping(value="/mode", method=RequestMethod.GET)
public @ResponseBody String mode(@NotBlank @RequestParam String timeSeries, @NotBlank @RequestParam boolean mode) {
manager.aggs(timeSeries).setMode(mode);
LOGGER.info("Success to set mode {} to {}", mode, timeSeries);
return Boolean.toString(mode);
}
@RequestMapping(value="/reassign", method=RequestMethod.POST)
public @ResponseBody String reassign(@NotBlank @RequestParam String cubeId,
@NotBlank @RequestParam String timeSeries) {
LOGGER.info("Try to assign cubeId{} to handle{} request.", cubeId, timeSeries);
String newCubeId = manager.reassignRole(cubeId, timeSeries);
LOGGER.info("Sucess to assign cubeId{} to handle{} request.", newCubeId, timeSeries);
return newCubeId;
}
@RequestMapping(value="/merge", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody String merge(@NotBlank @RequestParam int version,
@NotBlank @RequestParam String timeSeries) {
LOGGER.info("Try to merge data of {} to {}.", version, timeSeries);
int result = manager.merge(timeSeries, version);
LOGGER.info("Success for merge data of {} to {}, result is {}", version, timeSeries, result);
return OK;
}
@RequestMapping(value="/sum", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody BigDecimal sum(@NotBlank @RequestParam String indName,
@RequestParam(required=false) String filterDims,
@NotBlank @RequestParam String... timeSeries) throws Throwable {
LOGGER.info("Try to sum {} on {} with filter {}.", indName, ObjectUtils.getDisplayString(timeSeries), filterDims);
long timing = System.currentTimeMillis();
Map<String, List<Integer>> filter = (filterDims == null || "".equals(filterDims)) ? null
: objectMapper.readValue(filterDims, new TypeReference<Map<String, List<Integer>>>() {});
BigDecimal sum = manager.aggs(timeSeries).sum(indName, filter);
LOGGER.info("Sucess to sum {} on {} result is {} using {}ms.", indName, timeSeries, sum, System.currentTimeMillis() - timing);
return sum;
}
@RequestMapping(value="/groupsum", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody Map<Integer, BigDecimal> groupsum(@NotBlank @RequestParam String indName,
@RequestParam(required=false) String filterDims,
@RequestParam String groupbyDim,
@NotBlank @RequestParam String... timeSeries) throws Throwable {
LOGGER.info("Try to sum {} on {} with filter {}.", indName, ObjectUtils.getDisplayString(timeSeries), filterDims);
long timing = System.currentTimeMillis();
Map<String, List<Integer>> filter = (filterDims == null || "".equals(filterDims)) ? null
: objectMapper.readValue(filterDims, new TypeReference<Map<String, List<Integer>>>() {});
Map<Integer, BigDecimal> sum = manager.aggs(timeSeries).sum(indName, groupbyDim, filter);
LOGGER.info("Sucess to sum {} on {} result size is {} using {}ms.", indName, timeSeries, sum.size(), System.currentTimeMillis() - timing);
LOGGER.debug("Sucess to sum {} on {} result is {}.", indName, timeSeries, sum);
return sum;
}
@RequestMapping(value="/count", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody Long count(@NotBlank @RequestParam String indName,
@RequestParam(required=false) String filterDims,
@NotBlank @RequestParam String... timeSeries) throws Throwable {
LOGGER.info("Try to count {} on {} with filter {}.", indName, ObjectUtils.getDisplayString(timeSeries), filterDims);
long timing = System.currentTimeMillis();
Map<String, List<Integer>> filter = (filterDims == null || "".equals(filterDims)) ? null
: objectMapper.readValue(filterDims, new TypeReference<Map<String, List<Integer>>>() {});
long count = manager.aggs(timeSeries).count(indName, filter);
LOGGER.info("Sucess to count {} on {} result is {} using {}ms.", indName, timeSeries, count, System.currentTimeMillis() - timing);
return count;
}
@RequestMapping(value="/groupcount", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody Map<Integer, Long> groupcount(@NotBlank @RequestParam String indName,
@RequestParam(required=false) String filterDims,
@RequestParam String groupbyDim,
@NotBlank @RequestParam String... timeSeries) throws Throwable {
LOGGER.info("Try to count {} on {} with filter {}.", indName, ObjectUtils.getDisplayString(timeSeries), filterDims);
long timing = System.currentTimeMillis();
Map<String, List<Integer>> filter = (filterDims == null || "".equals(filterDims)) ? null
: objectMapper.readValue(filterDims, new TypeReference<Map<String, List<Integer>>>() {});
Map<Integer, Long> count = manager.aggs(timeSeries).count(indName, groupbyDim, filter);
LOGGER.info("Sucess to count {} on {} result size is {} using {}ms.", indName, timeSeries, count.size(), System.currentTimeMillis() - timing);
LOGGER.debug("Sucess to count {} on {} result is {}.", indName, timeSeries, count);
return count;
}
@RequestMapping(value="/distinct", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody Map<Integer, Set<Integer>> distinct(@NotBlank @RequestParam String indName,
@NotBlank @RequestParam(required=false) Boolean isDim,
@RequestParam(required=false) String filterDims,
@RequestParam String groupbyDim,
@NotBlank @RequestParam String... timeSeries) throws Throwable {
LOGGER.info("Try to distinct {} on {} with filter {}.", indName, ObjectUtils.getDisplayString(timeSeries), filterDims);
long timing = System.currentTimeMillis();
Map<String, List<Integer>> filter = (filterDims == null || "".equals(filterDims)) ? null
: objectMapper.readValue(filterDims, new TypeReference<Map<String, List<Integer>>>() {});
Map<Integer, RoaringBitmap> distinct = manager.aggs(timeSeries).distinct(indName, isDim == null ? true : isDim, groupbyDim, filter);
LOGGER.info("Sucess to distinct {} on {} result size is {} using {}ms.", indName, timeSeries, distinct.size(), System.currentTimeMillis() - timing);
LOGGER.debug("Sucess to distinct {} on {} result is {}.", indName, timeSeries, distinct);
Map<Integer, Set<Integer>> result = new HashMap<Integer, Set<Integer>>();
distinct.forEach(new BiConsumer<Integer, RoaringBitmap>() {
@Override
public void accept(Integer t, RoaringBitmap u) {
result.put(t, Arrays.stream(u.toArray()).collect(HashSet<Integer> :: new, Set :: add, (l, r) -> {}));
}
});
return result;
}
@RequestMapping(value="/distinctcount", method={RequestMethod.POST, RequestMethod.GET})
public @ResponseBody Map<Integer, Integer> distinctCount(@NotBlank @RequestParam String indName,
@NotBlank @RequestParam(required=false) Boolean isDim,
@RequestParam(required=false) String filterDims,
@RequestParam String groupbyDim,
@NotBlank @RequestParam String... timeSeries) throws Throwable {
LOGGER.info("Try to distinct-count {} on {} with filter {}.", indName, ObjectUtils.getDisplayString(timeSeries), filterDims);
long timing = System.currentTimeMillis();
Map<String, List<Integer>> filter = (filterDims == null || "".equals(filterDims)) ? null
: objectMapper.readValue(filterDims, new TypeReference<Map<String, List<Integer>>>() {});
Map<Integer, Integer> distinct = manager.aggs(timeSeries).discnt(indName, isDim == null ? true : isDim, groupbyDim, filter);
LOGGER.info("Sucess to distinct-count {} on {} result size is {} using {}ms.", indName, timeSeries, distinct.size(), System.currentTimeMillis() - timing);
LOGGER.debug("Sucess to distinct-count {} on {} result is {}.", indName, timeSeries, distinct);
return distinct;
}
}