/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.diqube.server.querymaster.query; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.diqube.execution.ExecutablePlan; import org.diqube.execution.ExecutablePlanStep; import org.diqube.queries.QueryStats; import org.diqube.remote.cluster.thrift.RExecutionPlan; import org.diqube.remote.cluster.thrift.RExecutionPlanStep; import org.diqube.remote.query.thrift.RQueryStatistics; import org.diqube.remote.query.thrift.RQueryStatisticsDetails; /** * Merges the statistics from various query remotes and the query master into a final {@link RQueryStatistics} object * which can be presented to the user. * * @author Bastian Gloeckle */ public class MasterQueryStatisticsMerger { private ExecutablePlan masterPlan; private RExecutionPlan remotePlan; public MasterQueryStatisticsMerger(ExecutablePlan masterPlan, RExecutionPlan remotePlan) { this.masterPlan = masterPlan; this.remotePlan = remotePlan; } public RQueryStatistics merge(QueryStats masterStats, List<QueryStats> remoteStats) { RQueryStatistics res = new RQueryStatistics(); Map<Integer, String> stepDescription = new HashMap<>(); masterPlan.getSteps().forEach(s -> stepDescription.put(s.getStepId(), "master-" + getStepDescription(s))); res.setMaster(createDetails(stepDescription, masterStats)); Map<Integer, String> remoteStepDescription = new HashMap<>(); remotePlan.getSteps() .forEach(s -> remoteStepDescription.put(s.getStepId(), "remote-" + getRemoteStepDescription(s))); List<RQueryStatisticsDetails> remoteDetails = new ArrayList<>(); for (QueryStats remote : remoteStats) remoteDetails.add(createDetails(remoteStepDescription, remote)); res.setRemotes(remoteDetails); return res; } private RQueryStatisticsDetails createDetails(Map<Integer, String> stepDescription, QueryStats stats) { RQueryStatisticsDetails res = new RQueryStatisticsDetails(); res.setNode(stats.getNodeName()); res.setStartedUntilDoneMs(stats.getStartedUntilDoneMs()); res.setNumberOfThreads(stats.getNumberOfThreads()); res.setNumberOfTemporaryColumnShardsCreated(stats.getNumberOfTemporaryColumnShardsCreated()); res.setNumberOfTemporaryColumnShardsFromCache(stats.getNumberOfTemporaryColumnShardsFromCache()); Map<String, Long> timesInSteps = new HashMap<>(); for (int stepId : stats.getStepThreadActiveMs().keySet()) timesInSteps.put(stepDescription.get(stepId), stats.getStepThreadActiveMs().get(stepId)); res.setStepsActiveMs(timesInSteps); res.setNumberOfPageAccesses(stats.getPageAccess()); res.setNumberOfTemporaryPageAccesses(stats.getTemporaryPageAccess()); res.setNumberOfPagesInTable(stats.getNumberOfPagesInTable()); res.setNumberOfTemporaryPages(stats.getNumberOfTemporaryPages()); res.setNumberOfTemporaryVersionsPerColName(stats.getNumberOfTemporaryVersionsPerColName()); return res; } private String getStepDescription(ExecutablePlanStep step) { StringBuilder sb = new StringBuilder(); sb.append(String.format("%02d", step.getStepId())); sb.append("-"); sb.append(step.getClass().getSimpleName()); String additionalDetails = step.getDetailsDescription(); if (additionalDetails != null && !additionalDetails.equals("")) { sb.append("["); sb.append(additionalDetails); sb.append("]"); } return sb.toString(); } private String getRemoteStepDescription(RExecutionPlanStep step) { StringBuilder sb = new StringBuilder(); sb.append(String.format("%02d", step.getStepId())); sb.append("-"); sb.append(step.getType().toString().toLowerCase()); if (step.isSetDetailsRowId()) { sb.append("["); sb.append(step.getDetailsRowId().getColumn().getColName()); sb.append(","); if (step.getDetailsRowId().isSetOtherColumn()) sb.append(step.getDetailsRowId().getOtherColumn().getColName()); else sb.append(step.getDetailsRowId().getSortedValues().toString()); sb.append("]"); } if (step.isSetDetailsOrder()) { sb.append("["); sb.append(step.getDetailsOrder().getOrderColumns().stream().map(orderCol -> orderCol.getColumn().getColName()) .collect(Collectors.toList()).toString()); if (step.getDetailsOrder().isSetLimit() && step.getDetailsOrder().getLimit().isSetLimit()) { sb.append(",limit="); sb.append(step.getDetailsOrder().getLimit().getLimit()); } if (step.getDetailsOrder().isSetLimit() && step.getDetailsOrder().getLimit().isSetLimitStart()) { sb.append(",limitStart="); sb.append(step.getDetailsOrder().getLimit().getLimitStart()); } if (step.getDetailsOrder().isSetSoftLimit()) { sb.append(",softLimit="); sb.append(step.getDetailsOrder().getSoftLimit()); } sb.append("]"); } if (step.isSetDetailsFunction()) { sb.append("["); sb.append(step.getDetailsFunction().getResultColumn().getColName()); sb.append("]"); } if (step.isSetDetailsResolve()) { sb.append("["); sb.append(step.getDetailsResolve().getColumn().getColName()); sb.append("]"); } return sb.toString(); } }