| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.kylin.rest.controller; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.kylin.common.util.JsonUtil; |
| import org.apache.kylin.common.util.Pair; |
| import org.apache.kylin.common.util.RandomUtil; |
| import org.apache.kylin.cube.CubeInstance; |
| import org.apache.kylin.cube.CubeManager; |
| import org.apache.kylin.cube.CubeSegment; |
| import org.apache.kylin.cube.cuboid.CuboidScheduler; |
| import org.apache.kylin.cube.cuboid.TreeCuboidScheduler; |
| import org.apache.kylin.cube.model.CubeBuildTypeEnum; |
| import org.apache.kylin.cube.model.CubeDesc; |
| import org.apache.kylin.cube.model.CubeJoinedFlatTableDesc; |
| import org.apache.kylin.cube.model.HBaseColumnDesc; |
| import org.apache.kylin.cube.model.HBaseColumnFamilyDesc; |
| import org.apache.kylin.cube.model.RowKeyColDesc; |
| import org.apache.kylin.dimension.DimensionEncoding; |
| import org.apache.kylin.dimension.DimensionEncodingFactory; |
| import org.apache.kylin.engine.mr.common.CuboidStatsReaderUtil; |
| import org.apache.kylin.job.JobInstance; |
| import org.apache.kylin.job.JoinedFlatTable; |
| import org.apache.kylin.job.exception.JobException; |
| import org.apache.kylin.metadata.model.IJoinedFlatTableDesc; |
| import org.apache.kylin.metadata.model.ISourceAware; |
| import org.apache.kylin.metadata.model.IEngineAware; |
| import org.apache.kylin.metadata.model.MeasureDesc; |
| import org.apache.kylin.metadata.model.SegmentRange; |
| import org.apache.kylin.metadata.model.SegmentRange.TSRange; |
| import org.apache.kylin.metadata.project.ProjectInstance; |
| import org.apache.kylin.metadata.realization.RealizationStatusEnum; |
| import org.apache.kylin.rest.exception.BadRequestException; |
| import org.apache.kylin.rest.exception.ForbiddenException; |
| import org.apache.kylin.rest.exception.InternalErrorException; |
| import org.apache.kylin.rest.exception.NotFoundException; |
| import org.apache.kylin.rest.exception.TooManyRequestException; |
| import org.apache.kylin.rest.msg.Message; |
| import org.apache.kylin.rest.msg.MsgPicker; |
| import org.apache.kylin.rest.request.CubeRequest; |
| import org.apache.kylin.rest.request.JobBuildRequest; |
| import org.apache.kylin.rest.request.JobBuildRequest2; |
| import org.apache.kylin.rest.request.JobOptimizeRequest; |
| import org.apache.kylin.rest.request.JobOptimizeRequest2; |
| import org.apache.kylin.rest.request.LookupSnapshotBuildRequest; |
| import org.apache.kylin.rest.response.CubeInstanceResponse; |
| import org.apache.kylin.rest.response.CuboidTreeResponse; |
| import org.apache.kylin.rest.response.EnvelopeResponse; |
| import org.apache.kylin.rest.response.GeneralResponse; |
| import org.apache.kylin.rest.response.HBaseResponse; |
| import org.apache.kylin.rest.response.ResponseCode; |
| import org.apache.kylin.rest.service.CubeService; |
| import org.apache.kylin.rest.service.JobService; |
| import org.apache.kylin.rest.service.ProjectService; |
| import org.apache.kylin.rest.service.QueryService; |
| import org.apache.kylin.rest.util.AclEvaluate; |
| import org.apache.kylin.rest.util.ValidateUtil; |
| import org.apache.kylin.source.kafka.util.KafkaClient; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.beans.factory.annotation.Qualifier; |
| import org.springframework.security.access.AccessDeniedException; |
| import org.springframework.security.core.context.SecurityContextHolder; |
| import org.springframework.stereotype.Controller; |
| import org.springframework.web.bind.annotation.PathVariable; |
| import org.springframework.web.bind.annotation.RequestBody; |
| 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.JsonParseException; |
| import com.fasterxml.jackson.core.JsonProcessingException; |
| import com.fasterxml.jackson.databind.JsonMappingException; |
| import org.apache.kylin.shaded.com.google.common.collect.Lists; |
| import org.apache.kylin.shaded.com.google.common.collect.Maps; |
| import org.apache.kylin.shaded.com.google.common.collect.Sets; |
| |
| /** |
| * CubeController is defined as Restful API entrance for UI. |
| */ |
| @Controller |
| @RequestMapping(value = "/cubes") |
| public class CubeController extends BasicController { |
| private static final Logger logger = LoggerFactory.getLogger(CubeController.class); |
| |
| @Autowired |
| @Qualifier("cubeMgmtService") |
| private CubeService cubeService; |
| |
| @Autowired |
| @Qualifier("jobService") |
| private JobService jobService; |
| |
| @Autowired |
| @Qualifier("projectService") |
| private ProjectService projectService; |
| |
| @Autowired |
| @Qualifier("queryService") |
| private QueryService queryService; |
| |
| @Autowired |
| @Qualifier("validateUtil") |
| private ValidateUtil validateUtil; |
| |
| @Autowired |
| private AclEvaluate aclEvaluate; |
| |
| @RequestMapping(value = "{cubeName}/validate", method = RequestMethod.GET, produces = { "application/json" }) |
| @ResponseBody |
| public EnvelopeResponse<Boolean> validateCubeName(@PathVariable String cubeName) { |
| return new EnvelopeResponse<>(ResponseCode.CODE_SUCCESS, cubeService.isCubeNameVaildate(cubeName), ""); |
| } |
| |
| @RequestMapping(value = "", method = { RequestMethod.GET }, produces = { "application/json" }) |
| @ResponseBody |
| public List<CubeInstanceResponse> getCubes(@RequestParam(value = "cubeName", required = false) String cubeName, |
| @RequestParam(value = "modelName", required = false) String modelName, |
| @RequestParam(value = "projectName", required = false) String projectName, |
| @RequestParam(value = "limit", required = false) Integer limit, |
| @RequestParam(value = "offset", required = false) Integer offset) { |
| List<CubeInstance> cubes = cubeService.listAllCubes(cubeName, projectName, modelName, false); |
| |
| List<CubeInstanceResponse> response = Lists.newArrayListWithExpectedSize(cubes.size()); |
| for (CubeInstance cube : cubes) { |
| try { |
| response.add(cubeService.createCubeInstanceResponse(cube)); |
| } catch (Exception e) { |
| logger.error("Error creating cube instance response, skipping.", e); |
| } |
| } |
| |
| int climit = (null == limit) ? response.size() : limit; |
| int coffset = (null == offset) ? 0 : offset; |
| |
| if (response.size() <= coffset) { |
| return Collections.emptyList(); |
| } |
| |
| if ((response.size() - coffset) < climit) { |
| return response.subList(coffset, response.size()); |
| } |
| |
| return response.subList(coffset, coffset + climit); |
| } |
| |
| @RequestMapping(value = "validEncodings", method = { RequestMethod.GET }, produces = { "application/json" }) |
| @ResponseBody |
| public Map<String, Integer> getValidEncodings() { |
| Map<String, Integer> encodings; |
| try { |
| encodings = DimensionEncodingFactory.getValidEncodings(); |
| } catch (Exception e) { |
| logger.error("Error when getting valid encodings", e); |
| return Maps.newHashMap(); |
| } |
| return encodings; |
| } |
| |
| @RequestMapping(value = "/{cubeName}", method = { RequestMethod.GET }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeInstance getCube(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| return cubeService.getCubeManager().getCube(cubeName); |
| } |
| |
| /** |
| * Get SQL of a Cube |
| * |
| * @param cubeName Cube Name |
| * @return |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/sql", method = { RequestMethod.GET }, produces = { "application/json" }) |
| @ResponseBody |
| public GeneralResponse getSql(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| IJoinedFlatTableDesc flatTableDesc = new CubeJoinedFlatTableDesc(cube.getDescriptor(), true); |
| String sql = JoinedFlatTable.generateSelectDataStatement(flatTableDesc); |
| |
| GeneralResponse response = new GeneralResponse(); |
| response.setProperty("sql", sql); |
| |
| return response; |
| } |
| |
| /** |
| * Get SQL of a Cube segment |
| * |
| * @param cubeName Cube Name |
| * @param segmentName Segment Name |
| * @return |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/segs/{segmentName}/sql", method = { RequestMethod.GET }, produces = { |
| "application/json" }) |
| @ResponseBody |
| public GeneralResponse getSql(@PathVariable String cubeName, @PathVariable String segmentName) { |
| |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| CubeSegment segment = cube.getSegment(segmentName, null); |
| if (segment == null) { |
| throw new NotFoundException("Cannot find segment " + segmentName); |
| } |
| |
| IJoinedFlatTableDesc flatTableDesc = new CubeJoinedFlatTableDesc(segment, true); |
| String sql = JoinedFlatTable.generateSelectDataStatement(flatTableDesc); |
| |
| GeneralResponse response = new GeneralResponse(); |
| response.setProperty("sql", sql); |
| |
| return response; |
| } |
| |
| /** |
| * Update cube notify list |
| * |
| * @param cubeName |
| * @param notifyList |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/notify_list", method = { RequestMethod.PUT }, produces = { |
| "application/json" }) |
| @ResponseBody |
| public void updateNotifyList(@PathVariable String cubeName, @RequestBody List<String> notifyList) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| try { |
| cubeService.updateCubeNotifyList(cube, notifyList); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| /** |
| * Update cube owner |
| * |
| * @param cubeName |
| * @param owner |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/owner", method = { RequestMethod.PUT }, produces = { |
| "application/json" }) |
| @ResponseBody |
| public CubeInstance updateCubeOwner(@PathVariable String cubeName, @RequestBody String owner) { |
| checkCubeExists(cubeName); |
| try { |
| validateUtil.checkIdentifiersExists(owner, true); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| return cubeService.updateCubeOwner(cube, owner); |
| } catch (AccessDeniedException accessDeniedException) { |
| throw new ForbiddenException("You don't have right to update this cube's owner."); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| @RequestMapping(value = "/{cubeName}/cost", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeInstance updateCubeCost(@PathVariable String cubeName, @RequestParam(value = "cost") int cost) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| try { |
| return cubeService.updateCubeCost(cube, cost); |
| } catch (Exception e) { |
| String message = "Failed to update cube cost: " + cubeName + " : " + cost; |
| logger.error(message, e); |
| throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Force rebuild a cube's lookup table snapshot |
| * |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/segs/{segmentName}/refresh_lookup", method = { |
| RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeInstance rebuildLookupSnapshot(@PathVariable String cubeName, @PathVariable String segmentName, |
| @RequestParam(value = "lookupTable") String lookupTable) { |
| try { |
| final CubeManager cubeMgr = cubeService.getCubeManager(); |
| final CubeInstance cube = cubeMgr.getCube(cubeName); |
| return cubeService.rebuildLookupSnapshot(cube, segmentName, lookupTable); |
| } catch (IOException e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| /** |
| * Force rebuild a cube's lookup table snapshot |
| * |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/refresh_lookup", method = { RequestMethod.PUT }, produces = { |
| "application/json" }) |
| @ResponseBody |
| public JobInstance rebuildLookupSnapshot(@PathVariable String cubeName, |
| @RequestBody LookupSnapshotBuildRequest request) { |
| try { |
| final CubeManager cubeMgr = cubeService.getCubeManager(); |
| final CubeInstance cube = cubeMgr.getCube(cubeName); |
| String submitter = SecurityContextHolder.getContext().getAuthentication().getName(); |
| return jobService.submitLookupSnapshotJob(cube, request.getLookupTableName(), request.getSegmentIDs(), |
| submitter); |
| } catch (IOException e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| /** |
| * Force change a cube's lookup table to be global |
| * |
| *@throws IOException |
| */ |
| @RequestMapping(value = "/{cubeNames}/{tableName}/change_lookup_global", method = { |
| RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public List<CubeInstance> globalLookupSnapshot(@PathVariable String cubeNames, @PathVariable String tableName) { |
| |
| List<CubeInstance> result = new ArrayList<>(); |
| |
| final CubeManager cubeMgr = cubeService.getCubeManager(); |
| String[] changeCubes = cubeNames.toUpperCase(Locale.ROOT).split(","); |
| for (String cubeName : changeCubes) { |
| try { |
| checkCubeExists(cubeName); |
| final CubeInstance cube = cubeMgr.getCube(cubeName); |
| CubeInstance cubeInstance = cubeService.changeLookupSnapshotBeGlobal(cube, tableName); |
| logger.info("cube {} change snapshotTable {} global Success", cubeName, tableName); |
| result.add(cubeInstance); |
| } catch (Exception e) { |
| logger.error("cube {} change snapshotTable {} global Fail", cubeName, tableName); |
| logger.error(e.getLocalizedMessage(), e); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Delete a cube segment |
| * |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/segs/{segmentName}", method = { RequestMethod.DELETE }, produces = { |
| "application/json" }) |
| @ResponseBody |
| public CubeInstance deleteSegment(@PathVariable String cubeName, @PathVariable String segmentName) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| CubeSegment segment = cube.getSegment(segmentName, null); |
| if (segment == null) { |
| throw new NotFoundException("Cannot find segment '" + segmentName + "'"); |
| } |
| |
| try { |
| return cubeService.deleteSegment(cube, segmentName); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| /** |
| * Delete a cube segment by UUID |
| * |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/segs2/{segmentID}", method = { RequestMethod.DELETE }, produces = { |
| "application/json" }) |
| @ResponseBody |
| public CubeInstance deleteSegmentByUUID(@PathVariable String cubeName, @PathVariable String segmentID) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| CubeSegment segment = cube.getSegmentById(segmentID); |
| if (segment == null) { |
| throw new NotFoundException("Cannot find segment by UUID '" + segmentID + "'"); |
| } |
| |
| try { |
| return cubeService.deleteSegmentById(cube, segmentID); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| |
| /** |
| * Build/Rebuild a cube segment |
| */ |
| @RequestMapping(value = "/{cubeName}/build", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public JobInstance build(@PathVariable String cubeName, @RequestBody JobBuildRequest req) { |
| return rebuild(cubeName, req); |
| } |
| |
| /** Build/Rebuild a cube segment */ |
| |
| /** |
| * Build/Rebuild a cube segment |
| */ |
| @RequestMapping(value = "/{cubeName}/rebuild", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public JobInstance rebuild(@PathVariable String cubeName, @RequestBody JobBuildRequest req) { |
| return buildInternal(cubeName, new TSRange(req.getStartTime(), req.getEndTime()), null, null, null, |
| req.getBuildType(), req.isForce() || req.isForceMergeEmptySegment(), req.getPriorityOffset()); |
| } |
| |
| /** |
| * Build/Rebuild a cube segment by source offset |
| */ |
| @RequestMapping(value = "/{cubeName}/build2", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public JobInstance build2(@PathVariable String cubeName, @RequestBody JobBuildRequest2 req) { |
| try { |
| Class<?> clazz = Class.forName("org.apache.kafka.clients.consumer.KafkaConsumer"); |
| if (clazz == null) { |
| throw new ClassNotFoundException(); |
| } |
| } catch (ClassNotFoundException e) { |
| throw new InternalErrorException("Could not find Kafka dependency"); |
| } |
| return rebuild2(cubeName, req); |
| } |
| |
| /** |
| * Build/Rebuild a cube segment by source offset |
| */ |
| @RequestMapping(value = "/{cubeName}/rebuild2", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public JobInstance rebuild2(@PathVariable String cubeName, @RequestBody JobBuildRequest2 req) { |
| return buildInternal(cubeName, null, new SegmentRange(req.getSourceOffsetStart(), req.getSourceOffsetEnd()), |
| req.getSourcePartitionOffsetStart(), req.getSourcePartitionOffsetEnd(), req.getBuildType(), |
| req.isForce(), req.getPriorityOffset()); |
| } |
| |
| private JobInstance buildInternal(String cubeName, TSRange tsRange, SegmentRange segRange, // |
| Map<Integer, Long> sourcePartitionOffsetStart, Map<Integer, Long> sourcePartitionOffsetEnd, |
| String buildType, boolean force, Integer priorityOffset) { |
| try { |
| String submitter = SecurityContextHolder.getContext().getAuthentication().getName(); |
| CubeInstance cube = jobService.getCubeManager().getCube(cubeName); |
| |
| checkBuildingSegment(cube); |
| return jobService.submitJob(cube, tsRange, segRange, sourcePartitionOffsetStart, sourcePartitionOffsetEnd, |
| CubeBuildTypeEnum.valueOf(buildType), force, submitter, priorityOffset); |
| } catch (Throwable e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| /** |
| * Send a auto merge cube job |
| * |
| * @param cubeName Cube ID |
| * @return JobInstance of merging cube |
| */ |
| @RequestMapping(value = "/{cubeName}/automerge", method = { RequestMethod.PUT }) |
| @ResponseBody |
| public JobInstance autoMerge(@PathVariable String cubeName) { |
| try { |
| checkCubeExists(cubeName); |
| |
| CubeInstance cube = jobService.getCubeManager().getCube(cubeName); |
| aclEvaluate.checkProjectAdminPermission(cube.getProject()); |
| |
| String jobID = cubeService.mergeCubeSegment(cubeName); |
| if (jobID == null) { |
| throw new BadRequestException(String.format(Locale.ROOT, |
| "Cube: %s merging is not supported or no segments to merge", cubeName)); |
| } |
| return jobService.getJobInstance(jobID); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage()); |
| } |
| } |
| |
| /** |
| * Send a optimize cube job |
| * |
| * @param cubeName Cube ID |
| * @return JobInstance of CheckpointExecutable |
| */ |
| @RequestMapping(value = "/{cubeName}/optimize", method = { RequestMethod.PUT }) |
| @ResponseBody |
| public JobInstance optimize(@PathVariable String cubeName, @RequestBody JobOptimizeRequest jobOptimizeRequest) { |
| try { |
| String submitter = SecurityContextHolder.getContext().getAuthentication().getName(); |
| CubeInstance cube = jobService.getCubeManager().getCube(cubeName); |
| |
| checkCubeExists(cubeName); |
| logger.info("cuboid recommend:" + jobOptimizeRequest.getCuboidsRecommend()); |
| return jobService.submitOptimizeJob(cube, jobOptimizeRequest.getCuboidsRecommend(), submitter).getFirst(); |
| } catch (BadRequestException e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw e; |
| } catch (JobException e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new BadRequestException(e.getLocalizedMessage()); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| /** |
| * Send a optimize cube job for delete or add cuboid |
| * |
| * @param cubeName Cube ID |
| * @param jobOptimizeRequest method (add or delete), cuboidsRecommend |
| * @return JobInstance of CheckpointExecutable |
| */ |
| @RequestMapping(value = "/{cubeName}/optimize2", method = {RequestMethod.PUT}) |
| @ResponseBody |
| public JobInstance optimize(@PathVariable String cubeName, @RequestBody JobOptimizeRequest2 jobOptimizeRequest) { |
| try { |
| String submitter = SecurityContextHolder.getContext().getAuthentication().getName(); |
| |
| checkCubeExists(cubeName); |
| CubeInstance cube = jobService.getCubeManager().getCube(cubeName); |
| |
| Set<Long> cuboidIds = cube.getCuboidScheduler().getAllCuboidIds(); |
| Set<Long> cuboidsAdd = jobOptimizeRequest.getCuboidsAdd(); |
| Set<Long> cuboidsDelete = jobOptimizeRequest.getCuboidsDelete(); |
| Set<Long> result = new HashSet<>(cuboidIds); |
| |
| if (cuboidsAdd == null && cuboidsDelete == null) { |
| throw new BadRequestException("must use cuboidsAdd or cuboidsDelete in request body."); |
| } |
| |
| if (cuboidsAdd != null && cuboidsAdd.size() != 0) { |
| result.addAll(cuboidsAdd); |
| logger.info( |
| "Add cuboid cubeName: " + cubeName + " contained cuboids: " + Sets.intersection(cuboidIds, cuboidsAdd)); |
| cuboidsAdd.removeAll(cuboidIds); |
| logger.info("Add cuboid cubeName: " + cubeName + " add cuboids: " + cuboidsAdd); |
| } else { |
| logger.info(cubeName + " no cuboids to add."); |
| } |
| |
| if (cuboidsDelete != null && cuboidsDelete.size() != 0) { |
| result.removeAll(cuboidsDelete); |
| logger.info("Remove cuboid cubeName: " + cubeName + " remove cuboids: " |
| + Sets.intersection(cuboidIds, cuboidsDelete)); |
| cuboidsDelete.removeAll(cuboidIds); |
| logger.info("Remove cuboid cubeName: " + cubeName + " missing cuboids: " + cuboidsDelete); |
| } else { |
| logger.info(cubeName + " no cuboids to delete."); |
| } |
| |
| return jobService.submitOptimizeJob(cube, result, submitter).getFirst(); |
| } catch (BadRequestException e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw e; |
| } catch (JobException e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new BadRequestException(e.getLocalizedMessage()); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| /** |
| * Send a optimize cube segment job |
| * |
| * @param cubeName Cube ID |
| * @param segmentID for segment to be optimized |
| */ |
| @RequestMapping(value = "/{cubeName}/recover_segment_optimize/{segmentID}", method = { RequestMethod.PUT }) |
| @ResponseBody |
| public JobInstance recoverSegmentOptimize(@PathVariable String cubeName, @PathVariable String segmentID) { |
| try { |
| String submitter = SecurityContextHolder.getContext().getAuthentication().getName(); |
| CubeInstance cube = jobService.getCubeManager().getCube(cubeName); |
| |
| CubeSegment segment = cube.getSegmentById(segmentID); |
| if (segment == null) { |
| throw new NotFoundException("Cannot find segment '" + segmentID + "'"); |
| } |
| |
| return jobService.submitRecoverSegmentOptimizeJob(segment, submitter); |
| } catch (JobException e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new BadRequestException(e.getLocalizedMessage()); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| @RequestMapping(value = "/{cubeName}/disable", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeInstance disableCube(@PathVariable String cubeName) { |
| try { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| return cubeService.disableCube(cube); |
| } catch (Exception e) { |
| String message = "Failed to disable cube: " + cubeName; |
| logger.error(message, e); |
| throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); |
| } |
| } |
| |
| @RequestMapping(value = "/{cubeName}/purge", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeInstance purgeCube(@PathVariable String cubeName) { |
| try { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| return cubeService.purgeCube(cube); |
| } catch (Exception e) { |
| String message = "Failed to purge cube: " + cubeName; |
| logger.error(message, e); |
| throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); |
| } |
| } |
| |
| @RequestMapping(value = "/{cubeName}/clone", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeInstance cloneCube(@PathVariable String cubeName, @RequestBody CubeRequest cubeRequest) { |
| String newCubeName = cubeRequest.getCubeName(); |
| String projectName = cubeRequest.getProject(); |
| |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| if (cube.getStatus() == RealizationStatusEnum.DESCBROKEN) { |
| throw new BadRequestException("Broken cube can't be cloned"); |
| } |
| if (!ValidateUtil.isAlphanumericUnderscore(newCubeName)) { |
| throw new BadRequestException("Invalid Cube name, only letters, numbers and underscore supported."); |
| } |
| |
| ProjectInstance project = cubeService.getProjectManager().getProject(projectName); |
| if (project == null) { |
| throw new NotFoundException("Project " + projectName + " doesn't exist"); |
| } |
| // KYLIN-1925, forbid cloning cross projects |
| if (!project.getName().equals(cube.getProject())) { |
| throw new BadRequestException("Cloning cubes across projects is not supported."); |
| } |
| |
| // explicitly convert the input parameter |
| String newName = ValidateUtil.convertStringToBeAlphanumericUnderscore(newCubeName); |
| CubeDesc cubeDesc = cube.getDescriptor(); |
| CubeDesc newCubeDesc = CubeDesc.getCopyOf(cubeDesc); |
| newCubeDesc.setName(newName); |
| |
| CubeInstance newCube; |
| try { |
| newCube = cubeService.createCubeAndDesc(project, newCubeDesc); |
| |
| //reload to avoid shallow clone |
| cubeService.getCubeDescManager().reloadCubeDescLocal(newName); |
| } catch (IOException e) { |
| throw new InternalErrorException("Failed to clone cube ", e); |
| } |
| |
| return newCube; |
| |
| } |
| |
| @RequestMapping(value = "/{cubeName}/enable", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeInstance enableCube(@PathVariable String cubeName) { |
| try { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| cubeService.checkEnableCubeCondition(cube); |
| |
| return cubeService.enableCube(cube); |
| } catch (Exception e) { |
| String message = "Failed to enable cube: " + cubeName; |
| logger.error(message, e); |
| throw new InternalErrorException(message + " Caused by: " + e.getMessage(), e); |
| } |
| } |
| |
| @RequestMapping(value = "/{cubeName}", method = { RequestMethod.DELETE }, produces = { "application/json" }) |
| @ResponseBody |
| public void deleteCube(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| //drop Cube |
| try { |
| cubeService.deleteCube(cube); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException("Failed to delete cube. " + " Caused by: " + e.getMessage(), e); |
| } |
| |
| } |
| |
| @RequestMapping(value = "/{cubeName}/fast", method = {RequestMethod.DELETE}) |
| @ResponseBody |
| public void deleteCubeFast(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| //drop Cube |
| try { |
| cubeService.deleteCubeFast(cube); |
| } catch (Exception e) { |
| logger.error(e.getLocalizedMessage(), e); |
| throw new InternalErrorException("Failed to delete cube. " + " Caused by: " + e.getMessage(), e); |
| } |
| |
| } |
| |
| /** |
| * save cubeDesc |
| * |
| * @return Table metadata array |
| * @throws IOException |
| */ |
| @RequestMapping(value = "", method = { RequestMethod.POST }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeRequest saveCubeDesc(@RequestBody CubeRequest cubeRequest) { |
| |
| CubeDesc desc = deserializeCubeDesc(cubeRequest); |
| |
| if (desc == null) { |
| cubeRequest.setMessage("CubeDesc is null."); |
| return cubeRequest; |
| } |
| String name = desc.getName(); |
| if (StringUtils.isEmpty(name)) { |
| logger.info("Cube name should not be empty."); |
| throw new BadRequestException("Cube name should not be empty."); |
| } |
| if (!ValidateUtil.isAlphanumericUnderscore(name)) { |
| throw new BadRequestException("Invalid Cube name, only letters, numbers and underscore supported."); |
| } |
| |
| validateColumnFamily(desc); |
| |
| try { |
| desc.setUuid(RandomUtil.randomUUID().toString()); |
| String projectName = (null == cubeRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME |
| : cubeRequest.getProject(); |
| ProjectInstance project = cubeService.getProjectManager().getProject(projectName); |
| if (project == null) { |
| throw new NotFoundException("Project " + projectName + " doesn't exist"); |
| } |
| cubeService.createCubeAndDesc(project, desc); |
| } catch (Exception e) { |
| logger.error("Failed to deal with the request.", e); |
| throw new InternalErrorException(e.getLocalizedMessage(), e); |
| } |
| |
| cubeRequest.setUuid(desc.getUuid()); |
| cubeRequest.setSuccessful(true); |
| return cubeRequest; |
| } |
| |
| //column family metrics may not match the real metrics when editing cube by json,see MTHDP-5091 |
| private void validateColumnFamily(CubeDesc cubeDesc) { |
| Set<String> columnFamilyMetricsSet = Sets.newHashSet(); |
| for (HBaseColumnFamilyDesc hBaseColumnFamilyDesc : cubeDesc.getHbaseMapping().getColumnFamily()) { |
| for (HBaseColumnDesc hBaseColumnDesc : hBaseColumnFamilyDesc.getColumns()) { |
| for (String columnName : hBaseColumnDesc.getMeasureRefs()) { |
| columnFamilyMetricsSet.add(columnName); |
| } |
| } |
| } |
| for (MeasureDesc measureDesc : cubeDesc.getMeasures()) { |
| if (!columnFamilyMetricsSet.contains(measureDesc.getName())) { |
| throw new BadRequestException("column family lack measure:" + measureDesc.getName()); |
| } |
| } |
| if (cubeDesc.getMeasures().size() != columnFamilyMetricsSet.size()) { |
| throw new BadRequestException( |
| "the number of input measure and the number of measure defined in cubedesc are not consistent"); |
| } |
| |
| for (RowKeyColDesc rowKeyColDesc : cubeDesc.getRowkey().getRowKeyColumns()) { |
| Object[] encodingConf = DimensionEncoding.parseEncodingConf(rowKeyColDesc.getEncoding()); |
| String encodingName = (String) encodingConf[0]; |
| String[] encodingArgs = (String[]) encodingConf[1]; |
| |
| if (!DimensionEncodingFactory.isValidEncoding(encodingName, encodingArgs, |
| rowKeyColDesc.getEncodingVersion())) { |
| throw new BadRequestException("Illegal row key column desc: " + rowKeyColDesc); |
| } |
| } |
| } |
| |
| /** |
| * update CubDesc |
| * |
| * @return Table metadata array |
| * @throws JsonProcessingException |
| * @throws IOException |
| */ |
| @RequestMapping(value = "", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public CubeRequest updateCubeDesc(@RequestBody CubeRequest cubeRequest) throws JsonProcessingException { |
| |
| CubeDesc desc = deserializeCubeDesc(cubeRequest); |
| if (desc == null) { |
| return cubeRequest; |
| } |
| |
| String projectName = (null == cubeRequest.getProject()) ? ProjectInstance.DEFAULT_PROJECT_NAME |
| : cubeRequest.getProject(); |
| try { |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeRequest.getCubeName()); |
| |
| if (cube == null) { |
| String error = "The cube named " + cubeRequest.getCubeName() + " does not exist "; |
| updateRequest(cubeRequest, false, error); |
| return cubeRequest; |
| } |
| |
| validateColumnFamily(desc); |
| |
| //cube renaming is not allowed |
| if (!cube.getDescriptor().getName().equalsIgnoreCase(desc.getName())) { |
| String error = "Cube Desc renaming is not allowed: desc.getName(): " + desc.getName() |
| + ", cubeRequest.getCubeName(): " + cubeRequest.getCubeName(); |
| updateRequest(cubeRequest, false, error); |
| return cubeRequest; |
| } |
| |
| if (cube.getSegments().size() != 0 && !cube.getDescriptor().consistentWith(desc)) { |
| String error = "CubeDesc " + desc.getName() |
| + " is inconsistent with existing. Try purge that cube first or avoid updating key cube desc fields."; |
| updateRequest(cubeRequest, false, error); |
| return cubeRequest; |
| } |
| |
| desc = cubeService.updateCubeAndDesc(cube, desc, projectName, true); |
| |
| } catch (AccessDeniedException accessDeniedException) { |
| throw new ForbiddenException("You don't have right to update this cube."); |
| } catch (Exception e) { |
| logger.error("Failed to deal with the request:" + e.getLocalizedMessage(), e); |
| throw new InternalErrorException("Failed to deal with the request: " + e.getLocalizedMessage(), e); |
| } |
| |
| if (desc.isBroken()) { |
| updateRequest(cubeRequest, false, desc.getErrorsAsString()); |
| return cubeRequest; |
| } |
| |
| String descData = JsonUtil.writeValueAsIndentString(desc); |
| cubeRequest.setCubeDescData(descData); |
| cubeRequest.setSuccessful(true); |
| return cubeRequest; |
| } |
| |
| @RequestMapping(value = "/{cubeName}/engine/{engineType}", method = RequestMethod.PUT) |
| @ResponseBody |
| public void updateCubeEngineType(@PathVariable String cubeName, @PathVariable String engineType) throws IOException { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| CubeDesc desc = cube.getDescriptor(); |
| int engineTypeID = desc.getEngineType(); |
| switch(engineType) { |
| case "MR_V2": |
| engineTypeID = IEngineAware.ID_MR_V2; |
| break; |
| case "SPARK": |
| engineTypeID = IEngineAware.ID_SPARK; |
| break; |
| case "FLINK": |
| engineTypeID = IEngineAware.ID_FLINK; |
| break; |
| default: |
| logger.warn("Engine type {} is not support", engineType); |
| } |
| desc.setEngineType(engineTypeID); |
| cubeService.updateCubeAndDesc(cube, desc, cube.getProject(), true); |
| } |
| |
| /** |
| * get Hbase Info |
| * |
| * @return true |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/hbase", method = { RequestMethod.GET }, produces = { "application/json" }) |
| @ResponseBody |
| public List<HBaseResponse> getHBaseInfo(@PathVariable String cubeName) { |
| List<HBaseResponse> hbase = new ArrayList<HBaseResponse>(); |
| |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| if (null == cube) { |
| throw new InternalErrorException("Cannot find cube " + cubeName); |
| } |
| |
| List<CubeSegment> segments = cube.getSegments(); |
| |
| for (CubeSegment segment : segments) { |
| String tableName = segment.getStorageLocationIdentifier(); |
| HBaseResponse hr = null; |
| |
| // Get info of given table. |
| try { |
| hr = cubeService.getHTableInfo(cubeName, tableName); |
| } catch (IOException e) { |
| logger.error("Failed to calcuate size of HTable \"" + tableName + "\".", e); |
| } |
| |
| if (null == hr) { |
| logger.info("Failed to calcuate size of HTable \"" + tableName + "\"."); |
| hr = new HBaseResponse(); |
| } |
| |
| hr.setTableName(tableName); |
| hr.setDateRangeStart(segment.getTSRange().start.v); |
| hr.setDateRangeEnd(segment.getTSRange().end.v); |
| hr.setSegmentName(segment.getName()); |
| hr.setSegmentStatus(segment.getStatus().toString()); |
| hr.setSourceCount(segment.getInputRecords()); |
| if (segment.isOffsetCube()) { |
| hr.setSourceOffsetStart((Long) segment.getSegRange().start.v); |
| hr.setSourceOffsetEnd((Long) segment.getSegRange().end.v); |
| } |
| hbase.add(hr); |
| } |
| |
| return hbase; |
| } |
| |
| /** |
| * get cube segment holes |
| * |
| * @return a list of CubeSegment, each representing a hole |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/holes", method = { RequestMethod.GET }, produces = { "application/json" }) |
| @ResponseBody |
| public List<CubeSegment> getHoles(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| return cubeService.getCubeManager().calculateHoles(cubeName); |
| } |
| |
| /** |
| * fill cube segment holes |
| * |
| * @return a list of JobInstances to fill the holes |
| * @throws IOException |
| */ |
| @RequestMapping(value = "/{cubeName}/holes", method = { RequestMethod.PUT }, produces = { "application/json" }) |
| @ResponseBody |
| public List<JobInstance> fillHoles(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| List<JobInstance> jobs = Lists.newArrayList(); |
| List<CubeSegment> holes = cubeService.getCubeManager().calculateHoles(cubeName); |
| |
| if (holes.size() == 0) { |
| logger.info("No hole detected for cube '" + cubeName + "'"); |
| return jobs; |
| } |
| |
| for (CubeSegment hole : holes) { |
| if (hole.isOffsetCube()) { |
| JobBuildRequest2 request = new JobBuildRequest2(); |
| request.setBuildType(CubeBuildTypeEnum.BUILD.toString()); |
| request.setSourceOffsetStart((Long) hole.getSegRange().start.v); |
| request.setSourceOffsetEnd((Long) hole.getSegRange().end.v); |
| request.setSourcePartitionOffsetStart(hole.getSourcePartitionOffsetStart()); |
| request.setSourcePartitionOffsetEnd(hole.getSourcePartitionOffsetEnd()); |
| try { |
| JobInstance job = build2(cubeName, request); |
| jobs.add(job); |
| } catch (Exception e) { |
| // it may exceed the max allowed job number |
| logger.info("Error to submit job for hole '" + hole.toString() + "', skip it now.", e); |
| continue; |
| } |
| } else { |
| JobBuildRequest request = new JobBuildRequest(); |
| request.setBuildType(CubeBuildTypeEnum.BUILD.toString()); |
| request.setStartTime(hole.getTSRange().start.v); |
| request.setEndTime(hole.getTSRange().end.v); |
| |
| try { |
| JobInstance job = build(cubeName, request); |
| jobs.add(job); |
| } catch (Exception e) { |
| // it may exceed the max allowed job number |
| logger.info("Error to submit job for hole '" + hole.toString() + "', skip it now.", e); |
| continue; |
| } |
| } |
| } |
| |
| return jobs; |
| |
| } |
| |
| @RequestMapping(value = "/{cubeName}/cuboids/export", method = RequestMethod.GET) |
| @ResponseBody |
| public void cuboidsExport(@PathVariable String cubeName, @RequestParam(value = "top") Integer top, |
| HttpServletResponse response) throws IOException { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| |
| Map<Long, Long> cuboidList = getRecommendCuboidList(cube); |
| List<Set<String>> dimensionSetList = Lists.newLinkedList(); |
| |
| if (cuboidList == null || cuboidList.isEmpty()) { |
| logger.info("Cannot get recommended cuboid list for cube " + cubeName); |
| } else { |
| if (cuboidList.size() < top) { |
| logger.info("Require " + top + " recommended cuboids, but only " + cuboidList.size() + " is found."); |
| } |
| Iterator<Long> cuboidIterator = cuboidList.keySet().iterator(); |
| RowKeyColDesc[] rowKeyColDescList = cube.getDescriptor().getRowkey().getRowKeyColumns(); |
| |
| while (top-- > 0 && cuboidIterator.hasNext()) { |
| Set<String> dimensionSet = Sets.newHashSet(); |
| dimensionSetList.add(dimensionSet); |
| long cuboid = cuboidIterator.next(); |
| for (int i = 0; i < rowKeyColDescList.length; i++) { |
| if ((cuboid & (1L << rowKeyColDescList[i].getBitIndex())) > 0) { |
| dimensionSet.add(rowKeyColDescList[i].getColumn()); |
| } |
| } |
| } |
| } |
| |
| response.setContentType("text/json;charset=utf-8"); |
| response.setHeader("Content-Disposition", |
| "attachment; filename=\"" + ValidateUtil.convertStringToBeAlphanumericUnderscore(cubeName) + ".json\""); |
| try (PrintWriter writer = response.getWriter()) { |
| writer.write(JsonUtil.writeValueAsString(dimensionSetList)); |
| } catch (IOException e) { |
| logger.error("", e); |
| throw new InternalErrorException("Failed to write: " + e.getLocalizedMessage(), e); |
| } |
| } |
| |
| @RequestMapping(value = "/{cubeName}/cuboids/current", method = RequestMethod.GET) |
| @ResponseBody |
| public CuboidTreeResponse getCurrentCuboids(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| // The cuboid tree displayed should be consistent with the current one |
| CuboidScheduler cuboidScheduler = cube.getCuboidScheduler(); |
| Map<Long, Long> cuboidStatsMap = cube.getCuboids(); |
| if (cuboidStatsMap == null) { |
| cuboidStatsMap = CuboidStatsReaderUtil.readCuboidStatsFromCube(cuboidScheduler.getAllCuboidIds(), cube); |
| } |
| |
| Map<Long, Long> hitFrequencyMap = null; |
| Map<Long, Long> queryMatchMap = null; |
| try { |
| hitFrequencyMap = getTargetCuboidHitFrequency(cubeName); |
| queryMatchMap = cubeService.getCuboidQueryMatchCount(cubeName); |
| } catch (Exception e) { |
| logger.warn("Fail to query on system cube due to " + e); |
| } |
| |
| Set<Long> currentCuboidSet = cube.getCuboidScheduler().getAllCuboidIds(); |
| return cubeService.getCuboidTreeResponse(cuboidScheduler, cuboidStatsMap, hitFrequencyMap, queryMatchMap, |
| currentCuboidSet); |
| } |
| |
| @RequestMapping(value = "/{cubeName}/cuboids/recommend", method = RequestMethod.GET) |
| @ResponseBody |
| public CuboidTreeResponse getRecommendCuboids(@PathVariable String cubeName) throws IOException { |
| checkCubeExists(cubeName); |
| CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); |
| Map<Long, Long> recommendCuboidStatsMap = getRecommendCuboidList(cube); |
| if (recommendCuboidStatsMap == null || recommendCuboidStatsMap.isEmpty()) { |
| return new CuboidTreeResponse(); |
| } |
| CuboidScheduler cuboidScheduler = new TreeCuboidScheduler(cube.getDescriptor(), |
| Lists.newArrayList(recommendCuboidStatsMap.keySet()), |
| new TreeCuboidScheduler.CuboidCostComparator(recommendCuboidStatsMap)); |
| |
| // Get cuboid target info for displaying heat map of cuboid hit |
| Map<Long, Long> displayHitFrequencyMap = getTargetCuboidHitFrequency(cubeName); |
| // Get exactly matched cuboid query count |
| Map<Long, Long> queryMatchMap = cubeService.getCuboidQueryMatchCount(cubeName); |
| |
| Set<Long> currentCuboidSet = cube.getCuboidScheduler().getAllCuboidIds(); |
| return cubeService.getCuboidTreeResponse(cuboidScheduler, recommendCuboidStatsMap, displayHitFrequencyMap, |
| queryMatchMap, currentCuboidSet); |
| } |
| |
| private Map<Long, Long> getRecommendCuboidList(CubeInstance cube) throws IOException { |
| // Get cuboid source info |
| Map<Long, Long> optimizeHitFrequencyMap = getSourceCuboidHitFrequency(cube.getName()); |
| Map<Long, Map<Long, Pair<Long, Long>>> rollingUpCountSourceMap = cubeService |
| .getCuboidRollingUpStats(cube.getName()); |
| return cubeService.getRecommendCuboidStatistics(cube, optimizeHitFrequencyMap, rollingUpCountSourceMap); |
| } |
| |
| private Map<Long, Long> getSourceCuboidHitFrequency(String cubeName) { |
| return cubeService.getCuboidHitFrequency(cubeName, true); |
| } |
| |
| private Map<Long, Long> getTargetCuboidHitFrequency(String cubeName) { |
| return cubeService.getCuboidHitFrequency(cubeName, false); |
| } |
| |
| /** |
| * Initiate the very beginning of a streaming cube. Will seek the latest offests of each partition from streaming |
| * source (kafka) and record in the cube descriptor; In the first build job, it will use these offests as the start point. |
| * |
| * @param cubeName |
| * @return |
| */ |
| @RequestMapping(value = "/{cubeName}/init_start_offsets", method = { RequestMethod.PUT }, produces = { |
| "application/json" }) |
| @ResponseBody |
| public GeneralResponse initStartOffsets(@PathVariable String cubeName) { |
| checkCubeExists(cubeName); |
| CubeInstance cubeInstance = cubeService.getCubeManager().getCube(cubeName); |
| if (cubeInstance.getSourceType() != ISourceAware.ID_STREAMING) { |
| String msg = "Cube '" + cubeName + "' is not a Streaming Cube."; |
| throw new IllegalArgumentException(msg); |
| } |
| |
| final GeneralResponse response = new GeneralResponse(); |
| try { |
| final Map<Integer, Long> startOffsets = KafkaClient.getLatestOffsets(cubeInstance); |
| CubeDesc desc = cubeInstance.getDescriptor(); |
| desc.setPartitionOffsetStart(startOffsets); |
| cubeService.getCubeDescManager().updateCubeDesc(desc); |
| response.setProperty("result", "success"); |
| response.setProperty("offsets", startOffsets.toString()); |
| } catch (Throwable e) { |
| throw new RuntimeException(e); |
| } |
| |
| return response; |
| } |
| |
| private CubeDesc deserializeCubeDesc(CubeRequest cubeRequest) { |
| CubeDesc desc = null; |
| try { |
| logger.debug("Saving cube " + cubeRequest.getCubeDescData()); |
| desc = JsonUtil.readValue(cubeRequest.getCubeDescData(), CubeDesc.class); |
| } catch (JsonParseException e) { |
| logger.error("The cube definition is not valid.", e); |
| updateRequest(cubeRequest, false, e.getMessage()); |
| } catch (JsonMappingException e) { |
| logger.error("The cube definition is not valid.", e); |
| updateRequest(cubeRequest, false, e.getMessage()); |
| } catch (IOException e) { |
| logger.error("Failed to deal with the request.", e); |
| throw new InternalErrorException("Failed to deal with the request:" + e.getMessage(), e); |
| } |
| return desc; |
| } |
| |
| private void updateRequest(CubeRequest request, boolean success, String message) { |
| request.setCubeDescData(""); |
| request.setSuccessful(success); |
| request.setMessage(message); |
| } |
| |
| private void checkCubeExists(String cubeName) { |
| CubeInstance cubeInstance = cubeService.getCubeManager().getCube(cubeName); |
| if (cubeInstance == null) { |
| Message msg = MsgPicker.getMsg(); |
| throw new NotFoundException(String.format(Locale.ROOT, msg.getCUBE_NOT_FOUND(), cubeName)); |
| } |
| } |
| |
| private void checkBuildingSegment(CubeInstance cube) { |
| checkBuildingSegment(cube, cube.getConfig().getMaxBuildingSegments()); |
| } |
| |
| private void checkBuildingSegment(CubeInstance cube, int maxBuildingSeg) { |
| if (cube.getBuildingSegments().size() >= maxBuildingSeg) { |
| throw new TooManyRequestException( |
| "There is already " + cube.getBuildingSegments().size() + " building segment; "); |
| } |
| } |
| |
| public void setCubeService(CubeService cubeService) { |
| this.cubeService = cubeService; |
| } |
| |
| public void setJobService(JobService jobService) { |
| this.jobService = jobService; |
| } |
| |
| public void setValidateUtil(ValidateUtil validateUtil) { |
| this.validateUtil = validateUtil; |
| } |
| } |