diff --git a/core/trino-main/src/main/java/io/trino/metadata/Split.java b/core/trino-main/src/main/java/io/trino/metadata/Split.java index 43695d41d44d..341e520f08c8 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/Split.java +++ b/core/trino-main/src/main/java/io/trino/metadata/Split.java @@ -81,6 +81,16 @@ public SplitWeight getSplitWeight() return connectorSplit.getSplitWeight(); } + public boolean isSkippedByIndex() + { + return connectorSplit.isSkippedByIndex(); + } + + public long getIndexReadTime() + { + return connectorSplit.getIndexReadTime(); + } + @Override public String toString() { diff --git a/core/trino-main/src/main/java/io/trino/operator/DriverContext.java b/core/trino-main/src/main/java/io/trino/operator/DriverContext.java index 80977dbc459d..9c93e5000010 100644 --- a/core/trino-main/src/main/java/io/trino/operator/DriverContext.java +++ b/core/trino-main/src/main/java/io/trino/operator/DriverContext.java @@ -335,6 +335,9 @@ public DriverStats getDriverStats() List operators = getOperatorStats(); OperatorStats inputOperator = getFirst(operators, null); + long skippedSplitsByIndex; + Duration indexReadTime; + DataSize physicalInputDataSize; long physicalInputPositions; Duration physicalInputReadTime; @@ -352,6 +355,9 @@ public DriverStats getDriverStats() DataSize outputDataSize; long outputPositions; if (inputOperator != null) { + skippedSplitsByIndex = inputOperator.getSkippedSplitsByIndex(); + indexReadTime = inputOperator.getIndexReadTime(); + physicalInputDataSize = inputOperator.getPhysicalInputDataSize(); physicalInputPositions = inputOperator.getPhysicalInputPositions(); physicalInputReadTime = inputOperator.getAddInputWall(); @@ -372,6 +378,9 @@ public DriverStats getDriverStats() outputPositions = outputOperator.getOutputPositions(); } else { + skippedSplitsByIndex = 0; + indexReadTime = new Duration(0, MILLISECONDS); + physicalInputDataSize = DataSize.ofBytes(0); physicalInputPositions = 0; physicalInputReadTime = new Duration(0, MILLISECONDS); @@ -415,6 +424,8 @@ public DriverStats getDriverStats() new Duration(totalBlockedTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), blockedMonitor != null, builder.build(), + skippedSplitsByIndex, + indexReadTime, physicalInputDataSize.succinct(), physicalInputPositions, physicalInputReadTime, diff --git a/core/trino-main/src/main/java/io/trino/operator/DriverStats.java b/core/trino-main/src/main/java/io/trino/operator/DriverStats.java index 757850fa3dc1..f761450d2748 100644 --- a/core/trino-main/src/main/java/io/trino/operator/DriverStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/DriverStats.java @@ -54,6 +54,9 @@ public class DriverStats private final boolean fullyBlocked; private final Set blockedReasons; + private final long skippedSplitsByIndex; + private final Duration indexReadTime; + private final DataSize physicalInputDataSize; private final long physicalInputPositions; private final Duration physicalInputReadTime; @@ -96,6 +99,9 @@ public DriverStats() this.fullyBlocked = false; this.blockedReasons = ImmutableSet.of(); + this.skippedSplitsByIndex = 0; + this.indexReadTime = new Duration(0, MILLISECONDS); + this.physicalInputDataSize = DataSize.ofBytes(0); this.physicalInputPositions = 0; this.physicalInputReadTime = new Duration(0, MILLISECONDS); @@ -139,6 +145,9 @@ public DriverStats( @JsonProperty("fullyBlocked") boolean fullyBlocked, @JsonProperty("blockedReasons") Set blockedReasons, + @JsonProperty("skippedSplitsByIndex") long skippedSplitsByIndex, + @JsonProperty("indexReadTime") Duration indexReadTime, + @JsonProperty("physicalInputDataSize") DataSize physicalInputDataSize, @JsonProperty("physicalInputPositions") long physicalInputPositions, @JsonProperty("physicalInputReadTime") Duration physicalInputReadTime, @@ -179,6 +188,9 @@ public DriverStats( this.fullyBlocked = fullyBlocked; this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); + this.skippedSplitsByIndex = skippedSplitsByIndex; + this.indexReadTime = requireNonNull(indexReadTime, "indexReadTIme is null"); + this.physicalInputDataSize = requireNonNull(physicalInputDataSize, "physicalInputDataSize is null"); checkArgument(physicalInputPositions >= 0, "physicalInputPositions is negative"); this.physicalInputPositions = physicalInputPositions; @@ -293,6 +305,18 @@ public Set getBlockedReasons() return blockedReasons; } + @JsonProperty + public long getSkippedSplitsByIndex() + { + return skippedSplitsByIndex; + } + + @JsonProperty + public Duration getIndexReadTime() + { + return indexReadTime; + } + @JsonProperty public DataSize getPhysicalInputDataSize() { diff --git a/core/trino-main/src/main/java/io/trino/operator/OperatorContext.java b/core/trino-main/src/main/java/io/trino/operator/OperatorContext.java index 9659434f75f2..540b89b953f7 100644 --- a/core/trino-main/src/main/java/io/trino/operator/OperatorContext.java +++ b/core/trino-main/src/main/java/io/trino/operator/OperatorContext.java @@ -56,6 +56,7 @@ import static java.lang.Math.max; import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; /** @@ -72,6 +73,9 @@ public class OperatorContext private final DriverContext driverContext; private final Executor executor; + private final AtomicLong skippedSplitsByIndex = new AtomicLong(); + private final AtomicLong indexReadTime = new AtomicLong(); + private final CounterStat physicalInputDataSize = new CounterStat(); private final CounterStat physicalInputPositions = new CounterStat(); @@ -165,6 +169,16 @@ public boolean isDone() return driverContext.isDone(); } + /** + * Record the number of splits skipped by index and the total time of reading indices in an operator. + * This metric is valid only for source operators reading iceberg tables. + */ + public void recordIndexStats(long skippedSplitsByIndex, long indexReadTime) + { + this.skippedSplitsByIndex.getAndAdd(skippedSplitsByIndex); + this.indexReadTime.getAndAdd(indexReadTime); + } + void recordAddInput(OperationTimer operationTimer, Page page) { operationTimer.recordOperationComplete(addInputTiming); @@ -553,6 +567,9 @@ private OperatorStats getOperatorStats() 1, + skippedSplitsByIndex.get(), + new Duration(indexReadTime.get(), MILLISECONDS).convertToMostSuccinctTimeUnit(), + addInputTiming.getCalls(), new Duration(addInputTiming.getWallNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(addInputTiming.getCpuNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), diff --git a/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java b/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java index bd360910424c..5641534f6313 100644 --- a/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/OperatorStats.java @@ -31,6 +31,7 @@ import static com.google.common.base.Verify.verify; import static java.lang.Math.max; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; @Immutable @@ -44,6 +45,9 @@ public class OperatorStats private final long totalDrivers; + private final long skippedSplitsByIndex; + private final Duration indexReadTime; + private final long addInputCalls; private final Duration addInputWall; private final Duration addInputCpu; @@ -99,6 +103,9 @@ public OperatorStats( @JsonProperty("totalDrivers") long totalDrivers, + @JsonProperty("skippedSplitsByIndex") long skippedSplitsByIndex, + @JsonProperty("indexReadTime") Duration indexReadTime, + @JsonProperty("addInputCalls") long addInputCalls, @JsonProperty("addInputWall") Duration addInputWall, @JsonProperty("addInputCpu") Duration addInputCpu, @@ -154,6 +161,9 @@ public OperatorStats( this.totalDrivers = totalDrivers; + this.skippedSplitsByIndex = skippedSplitsByIndex; + this.indexReadTime = requireNonNull(indexReadTime, "indexReadTime is null"); + this.addInputCalls = addInputCalls; this.addInputWall = requireNonNull(addInputWall, "addInputWall is null"); this.addInputCpu = requireNonNull(addInputCpu, "addInputCpu is null"); @@ -238,6 +248,18 @@ public long getTotalDrivers() return totalDrivers; } + @JsonProperty + public long getSkippedSplitsByIndex() + { + return skippedSplitsByIndex; + } + + @JsonProperty + public Duration getIndexReadTime() + { + return indexReadTime; + } + @JsonProperty public long getAddInputCalls() { @@ -452,6 +474,9 @@ public OperatorStats add(Iterable operators) { long totalDrivers = this.totalDrivers; + long skippedSplitsByIndex = this.skippedSplitsByIndex; + long indexReadTime = this.indexReadTime.roundTo(MILLISECONDS); + long addInputCalls = this.addInputCalls; long addInputWall = this.addInputWall.roundTo(NANOSECONDS); long addInputCpu = this.addInputCpu.roundTo(NANOSECONDS); @@ -501,6 +526,9 @@ public OperatorStats add(Iterable operators) totalDrivers += operator.totalDrivers; + skippedSplitsByIndex += operator.getSkippedSplitsByIndex(); + indexReadTime += operator.getIndexReadTime().roundTo(MILLISECONDS); + addInputCalls += operator.getAddInputCalls(); addInputWall += operator.getAddInputWall().roundTo(NANOSECONDS); addInputCpu += operator.getAddInputCpu().roundTo(NANOSECONDS); @@ -562,6 +590,9 @@ public OperatorStats add(Iterable operators) totalDrivers, + skippedSplitsByIndex, + new Duration(indexReadTime, MILLISECONDS).convertToMostSuccinctTimeUnit(), + addInputCalls, new Duration(addInputWall, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(addInputCpu, NANOSECONDS).convertToMostSuccinctTimeUnit(), @@ -636,6 +667,8 @@ public OperatorStats summarize() planNodeId, operatorType, totalDrivers, + skippedSplitsByIndex, + indexReadTime, addInputCalls, addInputWall, addInputCpu, diff --git a/core/trino-main/src/main/java/io/trino/operator/PipelineContext.java b/core/trino-main/src/main/java/io/trino/operator/PipelineContext.java index b2a3547ebd61..1ae1fa940d49 100644 --- a/core/trino-main/src/main/java/io/trino/operator/PipelineContext.java +++ b/core/trino-main/src/main/java/io/trino/operator/PipelineContext.java @@ -47,6 +47,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static io.airlift.units.DataSize.succinctBytes; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.stream.Collectors.toList; @@ -69,6 +70,9 @@ public class PipelineContext private final AtomicInteger completedDrivers = new AtomicInteger(); private final AtomicLong completedSplitsWeight = new AtomicLong(); + private final AtomicLong skippedSplitsByIndex = new AtomicLong(); + private final Distribution indexReadTime = new Distribution(); + private final AtomicReference executionStartTime = new AtomicReference<>(); private final AtomicReference lastExecutionStartTime = new AtomicReference<>(); private final AtomicReference lastExecutionEndTime = new AtomicReference<>(); @@ -192,6 +196,12 @@ public void driverFinished(DriverContext driverContext) completedSplitsWeight.addAndGet(driverContext.getSplitWeight()); } + skippedSplitsByIndex.getAndAdd(driverStats.getSkippedSplitsByIndex()); + long driverIndexReadTime = driverStats.getIndexReadTime().roundTo(MILLISECONDS); + if (driverIndexReadTime > 0) { + indexReadTime.add(driverIndexReadTime); + } + queuedTime.add(driverStats.getQueuedTime().roundTo(NANOSECONDS)); elapsedTime.add(driverStats.getElapsedTime().roundTo(NANOSECONDS)); @@ -353,6 +363,9 @@ public PipelineStats getPipelineStats() int totalDrivers = completedDrivers + driverContexts.size(); + long skippedSplitsByIndex = this.skippedSplitsByIndex.get(); + Distribution indexReadTime = this.indexReadTime.duplicate(); + Distribution queuedTime = this.queuedTime.duplicate(); Distribution elapsedTime = this.elapsedTime.duplicate(); @@ -469,6 +482,9 @@ public PipelineStats getPipelineStats() pipelineStatus.getBlockedDrivers(), completedDrivers, + skippedSplitsByIndex, + indexReadTime.snapshot(), + succinctBytes(pipelineMemoryContext.getUserMemory()), succinctBytes(pipelineMemoryContext.getRevocableMemory()), succinctBytes(pipelineMemoryContext.getSystemMemory()), diff --git a/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java b/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java index dd76e07b10ca..f1aecafa35f9 100644 --- a/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/PipelineStats.java @@ -53,6 +53,9 @@ public class PipelineStats private final int blockedDrivers; private final int completedDrivers; + private final long skippedSplitsByIndex; + private final DistributionSnapshot indexReadTime; + private final DataSize userMemoryReservation; private final DataSize revocableMemoryReservation; private final DataSize systemMemoryReservation; @@ -108,6 +111,9 @@ public PipelineStats( @JsonProperty("blockedDrivers") int blockedDrivers, @JsonProperty("completedDrivers") int completedDrivers, + @JsonProperty("skippedSplitsByIndex") long skippedSplitsByIndex, + @JsonProperty("indexReadTime") DistributionSnapshot indexReadTime, + @JsonProperty("userMemoryReservation") DataSize userMemoryReservation, @JsonProperty("revocableMemoryReservation") DataSize revocableMemoryReservation, @JsonProperty("systemMemoryReservation") DataSize systemMemoryReservation, @@ -170,6 +176,9 @@ public PipelineStats( checkArgument(completedDrivers >= 0, "completedDrivers is negative"); this.completedDrivers = completedDrivers; + this.skippedSplitsByIndex = skippedSplitsByIndex; + this.indexReadTime = requireNonNull(indexReadTime, "indexReadTime is null"); + this.userMemoryReservation = requireNonNull(userMemoryReservation, "userMemoryReservation is null"); this.revocableMemoryReservation = requireNonNull(revocableMemoryReservation, "revocableMemoryReservation is null"); this.systemMemoryReservation = requireNonNull(systemMemoryReservation, "systemMemoryReservation is null"); @@ -303,6 +312,18 @@ public int getCompletedDrivers() return completedDrivers; } + @JsonProperty + public long getSkippedSplitsByIndex() + { + return skippedSplitsByIndex; + } + + @JsonProperty + public DistributionSnapshot getIndexReadTime() + { + return indexReadTime; + } + @JsonProperty public DataSize getUserMemoryReservation() { @@ -465,6 +486,8 @@ public PipelineStats summarize() runningPartitionedSplitsWeight, blockedDrivers, completedDrivers, + skippedSplitsByIndex, + indexReadTime, userMemoryReservation, revocableMemoryReservation, systemMemoryReservation, diff --git a/core/trino-main/src/main/java/io/trino/operator/ScanFilterAndProjectOperator.java b/core/trino-main/src/main/java/io/trino/operator/ScanFilterAndProjectOperator.java index 1613011d11b5..b703f7313b46 100644 --- a/core/trino-main/src/main/java/io/trino/operator/ScanFilterAndProjectOperator.java +++ b/core/trino-main/src/main/java/io/trino/operator/ScanFilterAndProjectOperator.java @@ -76,6 +76,9 @@ public class ScanFilterAndProjectOperator @Nullable private ConnectorPageSource pageSource; + private long skippedSplitsByIndex; + private long indexReadTime; + private long processedPositions; private long processedBytes; private long physicalBytes; @@ -128,6 +131,18 @@ public Supplier> getUpdatablePageSourceSupplier() }; } + @Override + public long getSkippedSplitsByIndex() + { + return skippedSplitsByIndex; + } + + @Override + public long getIndexReadTime() + { + return indexReadTime; + } + @Override public DataSize getPhysicalInputDataSize() { @@ -266,6 +281,8 @@ public TransformationState> process(Split split) } else { source = pageSourceProvider.createPageSource(session, split, table, columns, dynamicFilter); + skippedSplitsByIndex += split.isSkippedByIndex() ? 1 : 0; + indexReadTime += split.getIndexReadTime(); } if (source instanceof RecordPageSource) { diff --git a/core/trino-main/src/main/java/io/trino/operator/TableScanOperator.java b/core/trino-main/src/main/java/io/trino/operator/TableScanOperator.java index 1dd54319ae21..47ae468c4dc4 100644 --- a/core/trino-main/src/main/java/io/trino/operator/TableScanOperator.java +++ b/core/trino-main/src/main/java/io/trino/operator/TableScanOperator.java @@ -306,6 +306,7 @@ public Page getOutput() operatorContext.recordDynamicFilterSplitProcessed(1L); } source = pageSourceProvider.createPageSource(operatorContext.getSession(), split, table, columns, dynamicFilter); + operatorContext.recordIndexStats(split.isSkippedByIndex() ? 1 : 0, split.getIndexReadTime()); } Page page = source.getNextPage(); diff --git a/core/trino-main/src/main/java/io/trino/operator/TableScanWorkProcessorOperator.java b/core/trino-main/src/main/java/io/trino/operator/TableScanWorkProcessorOperator.java index c2f70285256c..6f2d3c572e7b 100644 --- a/core/trino-main/src/main/java/io/trino/operator/TableScanWorkProcessorOperator.java +++ b/core/trino-main/src/main/java/io/trino/operator/TableScanWorkProcessorOperator.java @@ -88,6 +88,18 @@ public Supplier> getUpdatablePageSourceSupplier() return splitToPages.getUpdatablePageSourceSupplier(); } + @Override + public long getSkippedSplitsByIndex() + { + return splitToPages.skippedSplitsByIndex; + } + + @Override + public long getIndexReadTime() + { + return splitToPages.indexReadTime; + } + @Override public DataSize getPhysicalInputDataSize() { @@ -146,6 +158,9 @@ private static class SplitToPages final DynamicFilter dynamicFilter; final AggregatedMemoryContext aggregatedMemoryContext; + long skippedSplitsByIndex; + long indexReadTime; + long processedBytes; long processedPositions; long dynamicFilterSplitsProcessed; @@ -185,6 +200,8 @@ public TransformationState> process(Split split) } else { source = pageSourceProvider.createPageSource(session, split, table, columns, dynamicFilter); + skippedSplitsByIndex += split.isSkippedByIndex() ? 1 : 0; + indexReadTime += split.getIndexReadTime(); } return TransformationState.ofResult( diff --git a/core/trino-main/src/main/java/io/trino/operator/TaskContext.java b/core/trino-main/src/main/java/io/trino/operator/TaskContext.java index fd08d73d9f96..2dfbf1414f43 100644 --- a/core/trino-main/src/main/java/io/trino/operator/TaskContext.java +++ b/core/trino-main/src/main/java/io/trino/operator/TaskContext.java @@ -426,6 +426,9 @@ public TaskStats getTaskStats() int blockedDrivers = 0; int completedDrivers = 0; + long skippedSplitsByIndex = 0; + long totalIndexReadTime = 0; + long totalScheduledTime = 0; long totalCpuTime = 0; long totalBlockedTime = 0; @@ -478,6 +481,9 @@ public TaskStats getTaskStats() totalBlockedTime += pipeline.getTotalBlockedTime().roundTo(NANOSECONDS); if (pipeline.isInputPipeline()) { + skippedSplitsByIndex += pipeline.getSkippedSplitsByIndex(); + totalIndexReadTime += Math.round(pipeline.getIndexReadTime().getTotal()); + physicalInputDataSize += pipeline.getPhysicalInputDataSize().toBytes(); physicalInputPositions += pipeline.getPhysicalInputPositions(); physicalInputReadTime += pipeline.getPhysicalInputReadTime().roundTo(NANOSECONDS); @@ -553,6 +559,8 @@ public TaskStats getTaskStats() runningPartitionedSplitsWeight, blockedDrivers, completedDrivers, + skippedSplitsByIndex, + new Duration(totalIndexReadTime, MILLISECONDS).convertToMostSuccinctTimeUnit(), cumulativeUserMemory.get(), cumulativeSystemMemory.get(), succinctBytes(userMemory), diff --git a/core/trino-main/src/main/java/io/trino/operator/TaskStats.java b/core/trino-main/src/main/java/io/trino/operator/TaskStats.java index ea6658b09256..fb4adf30c724 100644 --- a/core/trino-main/src/main/java/io/trino/operator/TaskStats.java +++ b/core/trino-main/src/main/java/io/trino/operator/TaskStats.java @@ -51,6 +51,9 @@ public class TaskStats private final int blockedDrivers; private final int completedDrivers; + private final long skippedSplitsByIndex; + private final Duration totalIndexReadTime; + private final double cumulativeUserMemory; private final double cumulativeSystemMemory; private final DataSize userMemoryReservation; @@ -104,6 +107,8 @@ public TaskStats(DateTime createTime, DateTime endTime) 0L, 0, 0, + 0, + new Duration(0, MILLISECONDS), 0.0, 0.0, DataSize.ofBytes(0), @@ -151,6 +156,9 @@ public TaskStats( @JsonProperty("blockedDrivers") int blockedDrivers, @JsonProperty("completedDrivers") int completedDrivers, + @JsonProperty("skippedSplitsByIndex") long skippedSplitsByIndex, + @JsonProperty("totalIndexReadTime") Duration totalIndexReadTime, + @JsonProperty("cumulativeUserMemory") double cumulativeUserMemory, @JsonProperty("cumulativeSystemMemory") double cumulativeSystemMemory, @JsonProperty("userMemoryReservation") DataSize userMemoryReservation, @@ -216,6 +224,9 @@ public TaskStats( checkArgument(completedDrivers >= 0, "completedDrivers is negative"); this.completedDrivers = completedDrivers; + this.skippedSplitsByIndex = skippedSplitsByIndex; + this.totalIndexReadTime = requireNonNull(totalIndexReadTime, "totalIndexReadTime is null"); + this.cumulativeUserMemory = cumulativeUserMemory; this.cumulativeSystemMemory = cumulativeSystemMemory; this.userMemoryReservation = requireNonNull(userMemoryReservation, "userMemoryReservation is null"); @@ -334,6 +345,18 @@ public int getCompletedDrivers() return completedDrivers; } + @JsonProperty + public long getSkippedSplitsByIndex() + { + return skippedSplitsByIndex; + } + + @JsonProperty + public Duration getTotalIndexReadTime() + { + return totalIndexReadTime; + } + @JsonProperty public double getCumulativeUserMemory() { @@ -527,6 +550,8 @@ public TaskStats summarize() runningPartitionedSplitsWeight, blockedDrivers, completedDrivers, + skippedSplitsByIndex, + totalIndexReadTime, cumulativeUserMemory, cumulativeSystemMemory, userMemoryReservation, @@ -573,6 +598,8 @@ public TaskStats summarizeFinal() runningPartitionedSplitsWeight, blockedDrivers, completedDrivers, + skippedSplitsByIndex, + totalIndexReadTime, cumulativeUserMemory, cumulativeSystemMemory, userMemoryReservation, diff --git a/core/trino-main/src/main/java/io/trino/operator/WorkProcessorPipelineSourceOperator.java b/core/trino-main/src/main/java/io/trino/operator/WorkProcessorPipelineSourceOperator.java index 50c829703b39..b4b180f98e4f 100644 --- a/core/trino-main/src/main/java/io/trino/operator/WorkProcessorPipelineSourceOperator.java +++ b/core/trino-main/src/main/java/io/trino/operator/WorkProcessorPipelineSourceOperator.java @@ -56,6 +56,7 @@ import static io.trino.operator.WorkProcessor.ProcessState.Type.FINISHED; import static io.trino.operator.project.MergePages.mergePages; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; public class WorkProcessorPipelineSourceOperator @@ -226,6 +227,9 @@ private void workProcessorOperatorStateMonitor(WorkProcessor.ProcessState // update input stats for source operator WorkProcessorSourceOperator sourceOperator = (WorkProcessorSourceOperator) context.operator; + long deltaSkippedSplitsByIndex = deltaAndSet(context.skippedSplitsByIndex, sourceOperator.getSkippedSplitsByIndex()); + long deltaIndexReadTime = deltaAndSet(context.indexReadTime, sourceOperator.getIndexReadTime()); + long deltaPhysicalInputDataSize = deltaAndSet(context.physicalInputDataSize, sourceOperator.getPhysicalInputDataSize().toBytes()); long deltaPhysicalInputPositions = deltaAndSet(context.physicalInputPositions, sourceOperator.getPhysicalInputPositions()); @@ -240,6 +244,7 @@ private void workProcessorOperatorStateMonitor(WorkProcessor.ProcessState context.dynamicFilterSplitsProcessed.set(sourceOperator.getDynamicFilterSplitsProcessed()); context.connectorMetrics.set(sourceOperator.getConnectorMetrics()); + operatorContext.recordIndexStats(deltaSkippedSplitsByIndex, deltaIndexReadTime); operatorContext.recordPhysicalInputWithTiming(deltaPhysicalInputDataSize, deltaPhysicalInputPositions, deltaReadTimeNanos); operatorContext.recordNetworkInput(deltaInternalNetworkInputDataSize, deltaInternalNetworkInputPositions); operatorContext.recordProcessedInput(deltaInputDataSize, deltaInputPositions); @@ -317,6 +322,9 @@ private List getNestedOperatorStats() context.operatorType, 1, + context.skippedSplitsByIndex.get(), + new Duration(context.indexReadTime.get(), MILLISECONDS), + // WorkProcessorOperator doesn't have addInput call 0, // source operators report read time though @@ -672,6 +680,9 @@ private static class WorkProcessorOperatorContext final String operatorType; final MemoryTrackingContext memoryTrackingContext; + final AtomicLong skippedSplitsByIndex = new AtomicLong(); + final AtomicLong indexReadTime = new AtomicLong(); + final OperationTiming operatorTiming = new OperationTiming(); final AtomicLong blockedWallNanos = new AtomicLong(); diff --git a/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperator.java b/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperator.java index 42860ee9422a..5733ddbb442e 100644 --- a/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperator.java +++ b/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperator.java @@ -29,6 +29,16 @@ public interface WorkProcessorSourceOperator { Supplier> getUpdatablePageSourceSupplier(); + default long getSkippedSplitsByIndex() + { + return 0; + } + + default long getIndexReadTime() + { + return 0; + } + default DataSize getPhysicalInputDataSize() { return DataSize.ofBytes(0); diff --git a/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperatorAdapter.java b/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperatorAdapter.java index b70cc6d27562..c58a9622b31a 100644 --- a/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperatorAdapter.java +++ b/core/trino-main/src/main/java/io/trino/operator/WorkProcessorSourceOperatorAdapter.java @@ -46,6 +46,9 @@ public class WorkProcessorSourceOperatorAdapter private boolean operatorFinishing; + private long previousSkippedSplitsByIndex; + private long previousIndexReadTime; + private long previousPhysicalInputBytes; private long previousPhysicalInputPositions; private long previousInternalNetworkInputBytes; @@ -183,6 +186,9 @@ public void close() private void updateOperatorStats() { + long currentSkippedSplitsByIndex = sourceOperator.getSkippedSplitsByIndex(); + long currentIndexReadTime = sourceOperator.getIndexReadTime(); + long currentPhysicalInputBytes = sourceOperator.getPhysicalInputDataSize().toBytes(); long currentPhysicalInputPositions = sourceOperator.getPhysicalInputPositions(); long currentReadTimeNanos = sourceOperator.getReadTime().roundTo(NANOSECONDS); @@ -197,6 +203,16 @@ private void updateOperatorStats() Metrics currentMetrics = sourceOperator.getMetrics(); Metrics currentConnectorMetrics = sourceOperator.getConnectorMetrics(); + if (currentSkippedSplitsByIndex != previousSkippedSplitsByIndex + || currentIndexReadTime != previousIndexReadTime) { + operatorContext.recordIndexStats( + currentSkippedSplitsByIndex - previousSkippedSplitsByIndex, + currentIndexReadTime - previousIndexReadTime); + + previousSkippedSplitsByIndex = currentSkippedSplitsByIndex; + previousIndexReadTime = currentIndexReadTime; + } + if (currentPhysicalInputBytes != previousPhysicalInputBytes || currentPhysicalInputPositions != previousPhysicalInputPositions || currentReadTimeNanos != previousReadTimeNanos) { diff --git a/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js b/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js index 2d1ac00386b8..67817185e5e7 100644 --- a/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js +++ b/core/trino-main/src/main/resources/webapp/dist/embedded_plan.js @@ -20652,7 +20652,7 @@ eval("module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/index.js b/core/trino-main/src/main/resources/webapp/dist/index.js index cd9cae9908fa..e2b521377135 100644 --- a/core/trino-main/src/main/resources/webapp/dist/index.js +++ b/core/trino-main/src/main/resources/webapp/dist/index.js @@ -20628,7 +20628,7 @@ eval("module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/plan.js b/core/trino-main/src/main/resources/webapp/dist/plan.js index d9cbc95db330..ae989353ab29 100644 --- a/core/trino-main/src/main/resources/webapp/dist/plan.js +++ b/core/trino-main/src/main/resources/webapp/dist/plan.js @@ -20664,7 +20664,7 @@ eval("\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/i /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/query.js b/core/trino-main/src/main/resources/webapp/dist/query.js index e2c6b801fb4e..7150a8ccf6da 100644 --- a/core/trino-main/src/main/resources/webapp/dist/query.js +++ b/core/trino-main/src/main/resources/webapp/dist/query.js @@ -106,7 +106,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n}); /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.QueryDetail = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactable = __webpack_require__(/*! reactable */ \"./node_modules/reactable/lib/reactable.js\");\n\nvar _reactable2 = _interopRequireDefault(_reactable);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nvar _QueryHeader = __webpack_require__(/*! ./QueryHeader */ \"./components/QueryHeader.jsx\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar Table = _reactable2.default.Table,\n Thead = _reactable2.default.Thead,\n Th = _reactable2.default.Th,\n Tr = _reactable2.default.Tr,\n Td = _reactable2.default.Td;\n\nvar TaskList = function (_React$Component) {\n _inherits(TaskList, _React$Component);\n\n function TaskList() {\n _classCallCheck(this, TaskList);\n\n return _possibleConstructorReturn(this, (TaskList.__proto__ || Object.getPrototypeOf(TaskList)).apply(this, arguments));\n }\n\n _createClass(TaskList, [{\n key: \"render\",\n value: function render() {\n var tasks = this.props.tasks;\n\n if (tasks === undefined || tasks.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"No threads in the selected group\"\n )\n )\n );\n }\n\n var showPortNumbers = TaskList.showPortNumbers(tasks);\n\n var renderedTasks = tasks.map(function (task) {\n var elapsedTime = (0, _utils.parseDuration)(task.stats.elapsedTime);\n if (elapsedTime === 0) {\n elapsedTime = Date.now() - Date.parse(task.stats.createTime);\n }\n\n return _react2.default.createElement(\n Tr,\n { key: task.taskStatus.taskId },\n _react2.default.createElement(\n Td,\n { column: \"id\", value: task.taskStatus.taskId },\n _react2.default.createElement(\n \"a\",\n { href: \"/ui/api/worker/\" + task.taskStatus.nodeId + \"/task/\" + task.taskStatus.taskId + \"?pretty\" },\n (0, _utils.getTaskIdSuffix)(task.taskStatus.taskId)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"host\", value: (0, _utils.getHostname)(task.taskStatus.self) },\n _react2.default.createElement(\n \"a\",\n { href: \"worker.html?\" + task.taskStatus.nodeId, className: \"font-light\", target: \"_blank\" },\n showPortNumbers ? (0, _utils.getHostAndPort)(task.taskStatus.self) : (0, _utils.getHostname)(task.taskStatus.self)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"state\", value: TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked) },\n TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rows\", value: task.stats.rawInputPositions },\n (0, _utils.formatCount)(task.stats.rawInputPositions)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rowsSec\", value: (0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime) },\n (0, _utils.formatCount)((0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytes\", value: (0, _utils.parseDataSize)(task.stats.rawInputDataSize) },\n (0, _utils.formatDataSizeBytes)((0, _utils.parseDataSize)(task.stats.rawInputDataSize))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytesSec\", value: (0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime) },\n (0, _utils.formatDataSizeBytes)((0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsPending\", value: task.stats.queuedDrivers },\n task.stats.queuedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsRunning\", value: task.stats.runningDrivers },\n task.stats.runningDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsBlocked\", value: task.stats.blockedDrivers },\n task.stats.blockedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsDone\", value: task.stats.completedDrivers },\n task.stats.completedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"elapsedTime\", value: (0, _utils.parseDuration)(task.stats.elapsedTime) },\n task.stats.elapsedTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"cpuTime\", value: (0, _utils.parseDuration)(task.stats.totalCpuTime) },\n task.stats.totalCpuTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"bufferedBytes\", value: task.outputBuffers.totalBufferedBytes },\n (0, _utils.formatDataSizeBytes)(task.outputBuffers.totalBufferedBytes)\n )\n );\n });\n\n return _react2.default.createElement(\n Table,\n { id: \"tasks\", className: \"table table-striped sortable\", sortable: [{\n column: 'id',\n sortFunction: TaskList.compareTaskId\n }, 'host', 'state', 'splitsPending', 'splitsRunning', 'splitsBlocked', 'splitsDone', 'rows', 'rowsSec', 'bytes', 'bytesSec', 'elapsedTime', 'cpuTime', 'bufferedBytes'],\n defaultSort: { column: 'id', direction: 'asc' } },\n _react2.default.createElement(\n Thead,\n null,\n _react2.default.createElement(\n Th,\n { column: \"id\" },\n \"ID\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"host\" },\n \"Host\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"state\" },\n \"State\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsPending\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-pause\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Pending splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsRunning\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-play\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Running splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsBlocked\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-bookmark\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Blocked splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsDone\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-ok\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Completed splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"rows\" },\n \"Rows\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"rowsSec\" },\n \"Rows/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytes\" },\n \"Bytes\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytesSec\" },\n \"Bytes/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"elapsedTime\" },\n \"Elapsed\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"cpuTime\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bufferedBytes\" },\n \"Buffered\"\n )\n ),\n renderedTasks\n );\n }\n }], [{\n key: \"removeQueryId\",\n value: function removeQueryId(id) {\n var pos = id.indexOf('.');\n if (pos !== -1) {\n return id.substring(pos + 1);\n }\n return id;\n }\n }, {\n key: \"compareTaskId\",\n value: function compareTaskId(taskA, taskB) {\n var taskIdArrA = TaskList.removeQueryId(taskA).split(\".\");\n var taskIdArrB = TaskList.removeQueryId(taskB).split(\".\");\n\n if (taskIdArrA.length > taskIdArrB.length) {\n return 1;\n }\n for (var i = 0; i < taskIdArrA.length; i++) {\n var anum = Number.parseInt(taskIdArrA[i]);\n var bnum = Number.parseInt(taskIdArrB[i]);\n if (anum !== bnum) {\n return anum > bnum ? 1 : -1;\n }\n }\n\n return 0;\n }\n }, {\n key: \"showPortNumbers\",\n value: function showPortNumbers(tasks) {\n // check if any host has multiple port numbers\n var hostToPortNumber = {};\n for (var i = 0; i < tasks.length; i++) {\n var taskUri = tasks[i].taskStatus.self;\n var hostname = (0, _utils.getHostname)(taskUri);\n var port = (0, _utils.getPort)(taskUri);\n if (hostname in hostToPortNumber && hostToPortNumber[hostname] !== port) {\n return true;\n }\n hostToPortNumber[hostname] = port;\n }\n\n return false;\n }\n }, {\n key: \"formatState\",\n value: function formatState(state, fullyBlocked) {\n if (fullyBlocked && state === \"RUNNING\") {\n return \"BLOCKED\";\n } else {\n return state;\n }\n }\n }]);\n\n return TaskList;\n}(_react2.default.Component);\n\nvar BAR_CHART_WIDTH = 800;\n\nvar BAR_CHART_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#8997B3',\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: 'Task {{offset:offset}} - {{value}}',\n disableHiddenCheck: true\n};\n\nvar HISTOGRAM_WIDTH = 175;\n\nvar HISTOGRAM_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#747F96',\n zeroAxis: true,\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: '{{offset:offset}} -- {{value}} tasks',\n disableHiddenCheck: true\n};\n\nvar StageSummary = function (_React$Component2) {\n _inherits(StageSummary, _React$Component2);\n\n function StageSummary(props) {\n _classCallCheck(this, StageSummary);\n\n var _this2 = _possibleConstructorReturn(this, (StageSummary.__proto__ || Object.getPrototypeOf(StageSummary)).call(this, props));\n\n _this2.state = {\n expanded: false,\n lastRender: null\n };\n return _this2;\n }\n\n _createClass(StageSummary, [{\n key: \"getExpandedIcon\",\n value: function getExpandedIcon() {\n return this.state.expanded ? \"glyphicon-chevron-up\" : \"glyphicon-chevron-down\";\n }\n }, {\n key: \"getExpandedStyle\",\n value: function getExpandedStyle() {\n return this.state.expanded ? {} : { display: \"none\" };\n }\n }, {\n key: \"toggleExpanded\",\n value: function toggleExpanded() {\n this.setState({\n expanded: !this.state.expanded\n });\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n var stage = this.props.stage;\n var numTasks = stage.tasks.length;\n\n // sort the x-axis\n stage.tasks.sort(function (taskA, taskB) {\n return (0, _utils.getTaskNumber)(taskA.taskStatus.taskId) - (0, _utils.getTaskNumber)(taskB.taskStatus.taskId);\n });\n\n var scheduledTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalScheduledTime);\n });\n var cpuTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalCpuTime);\n });\n\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000) {\n var renderTimestamp = Date.now();\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n StageSummary.renderHistogram('#scheduled-time-histogram-' + stageId, scheduledTimes, _utils.formatDuration);\n StageSummary.renderHistogram('#cpu-time-histogram-' + stageId, cpuTimes, _utils.formatDuration);\n\n if (this.state.expanded) {\n // this needs to be a string otherwise it will also be passed to numberFormatter\n var tooltipValueLookups = { 'offset': {} };\n for (var i = 0; i < numTasks; i++) {\n tooltipValueLookups['offset'][i] = (0, _utils.getStageNumber)(stage.stageId) + \".\" + i;\n }\n\n var stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, { barWidth: BAR_CHART_WIDTH / numTasks, tooltipValueLookups: tooltipValueLookups });\n\n $('#scheduled-time-bar-chart-' + stageId).sparkline(scheduledTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n $('#cpu-time-bar-chart-' + stageId).sparkline(cpuTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n }\n\n this.setState({\n lastRender: renderTimestamp\n });\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var stage = this.props.stage;\n if (stage === undefined || !stage.hasOwnProperty('plan')) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Information about this stage is unavailable.\"\n )\n );\n }\n\n var totalBufferedBytes = stage.tasks.map(function (task) {\n return task.outputBuffers.totalBufferedBytes;\n }).reduce(function (a, b) {\n return a + b;\n }, 0);\n\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-id\" },\n _react2.default.createElement(\n \"div\",\n { className: \"stage-state-color\", style: { borderLeftColor: (0, _utils.getStageStateColor)(stage) } },\n stageId\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"table single-stage-table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-time\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Time\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Scheduled\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalBlockedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"CPU\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalCpuTime\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-memory\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Memory\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Cumulative\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSizeBytes)(stage.stageStats.cumulativeUserMemory / 1000)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Current\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.userMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Buffers\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSize)(totalBufferedBytes)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Peak\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.peakUserMemoryReservation)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-tasks\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Tasks\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Pending\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"PLANNED\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Running\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"RUNNING\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.stats.fullyBlocked;\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Total\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.length\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"Scheduled Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"scheduled-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"CPU Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"cpu-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"expand-charts-container\" },\n _react2.default.createElement(\n \"a\",\n { onClick: this.toggleExpanded.bind(this), className: \"expand-charts-button\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon \" + this.getExpandedIcon(), style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"More\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"scheduled-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"cpu-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n )\n )\n )\n )\n );\n }\n }], [{\n key: \"renderHistogram\",\n value: function renderHistogram(histogramId, inputData, numberFormatter) {\n var numBuckets = Math.min(HISTOGRAM_WIDTH, Math.sqrt(inputData.length));\n var dataMin = Math.min.apply(null, inputData);\n var dataMax = Math.max.apply(null, inputData);\n var bucketSize = (dataMax - dataMin) / numBuckets;\n\n var histogramData = [];\n if (bucketSize === 0) {\n histogramData = [inputData.length];\n } else {\n for (var i = 0; i < numBuckets + 1; i++) {\n histogramData.push(0);\n }\n\n for (var _i in inputData) {\n var dataPoint = inputData[_i];\n var bucket = Math.floor((dataPoint - dataMin) / bucketSize);\n histogramData[bucket] = histogramData[bucket] + 1;\n }\n }\n\n var tooltipValueLookups = { 'offset': {} };\n for (var _i2 = 0; _i2 < histogramData.length; _i2++) {\n tooltipValueLookups['offset'][_i2] = numberFormatter(dataMin + _i2 * bucketSize) + \"-\" + numberFormatter(dataMin + (_i2 + 1) * bucketSize);\n }\n\n var stageHistogramProperties = $.extend({}, HISTOGRAM_PROPERTIES, { barWidth: HISTOGRAM_WIDTH / histogramData.length, tooltipValueLookups: tooltipValueLookups });\n $(histogramId).sparkline(histogramData, stageHistogramProperties);\n }\n }]);\n\n return StageSummary;\n}(_react2.default.Component);\n\nvar StageList = function (_React$Component3) {\n _inherits(StageList, _React$Component3);\n\n function StageList() {\n _classCallCheck(this, StageList);\n\n return _possibleConstructorReturn(this, (StageList.__proto__ || Object.getPrototypeOf(StageList)).apply(this, arguments));\n }\n\n _createClass(StageList, [{\n key: \"getStages\",\n value: function getStages(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages')) {\n return [];\n }\n\n return [].concat.apply(stage, stage.subStages.map(this.getStages, this));\n }\n }, {\n key: \"render\",\n value: function render() {\n var stages = this.getStages(this.props.outputStage);\n\n if (stages === undefined || stages.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n \"No stage information available.\"\n )\n );\n }\n\n var renderedStages = stages.map(function (stage) {\n return _react2.default.createElement(StageSummary, { key: stage.stageId, stage: stage });\n });\n\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"stage-list\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n renderedStages\n )\n )\n )\n );\n }\n }]);\n\n return StageList;\n}(_react2.default.Component);\n\nvar SMALL_SPARKLINE_PROPERTIES = {\n width: '100%',\n height: '57px',\n fillColor: '#3F4552',\n lineColor: '#747F96',\n spotColor: '#1EDCFF',\n tooltipClassname: 'sparkline-tooltip',\n disableHiddenCheck: true\n};\n\nvar TASK_FILTER = {\n ALL: function ALL() {\n return true;\n },\n PLANNED: function PLANNED(state) {\n return state === 'PLANNED';\n },\n RUNNING: function RUNNING(state) {\n return state === 'RUNNING';\n },\n FINISHED: function FINISHED(state) {\n return state === 'FINISHED';\n },\n FAILED: function FAILED(state) {\n return state === 'FAILED' || state === 'ABORTED' || state === 'CANCELED';\n }\n};\n\nvar QueryDetail = exports.QueryDetail = function (_React$Component4) {\n _inherits(QueryDetail, _React$Component4);\n\n function QueryDetail(props) {\n _classCallCheck(this, QueryDetail);\n\n var _this4 = _possibleConstructorReturn(this, (QueryDetail.__proto__ || Object.getPrototypeOf(QueryDetail)).call(this, props));\n\n _this4.state = {\n query: null,\n lastSnapshotStages: null,\n lastSnapshotTasks: null,\n\n lastScheduledTime: 0,\n lastCpuTime: 0,\n lastRowInput: 0,\n lastByteInput: 0,\n lastPhysicalInput: 0,\n lastPhysicalTime: 0,\n\n scheduledTimeRate: [],\n cpuTimeRate: [],\n rowInputRate: [],\n byteInputRate: [],\n physicalInputRate: [],\n\n reservedMemory: [],\n\n initialized: false,\n queryEnded: false,\n renderingEnded: false,\n\n lastRefresh: null,\n lastRender: null,\n\n stageRefresh: true,\n taskRefresh: true,\n\n taskFilter: TASK_FILTER.ALL\n };\n\n _this4.refreshLoop = _this4.refreshLoop.bind(_this4);\n return _this4;\n }\n\n _createClass(QueryDetail, [{\n key: \"resetTimer\",\n value: function resetTimer() {\n clearTimeout(this.timeoutId);\n // stop refreshing when query finishes or fails\n if (this.state.query === null || !this.state.queryEnded) {\n // task.info-update-interval is set to 3 seconds by default\n this.timeoutId = setTimeout(this.refreshLoop, 3000);\n }\n }\n }, {\n key: \"refreshLoop\",\n value: function refreshLoop() {\n var _this5 = this;\n\n clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously\n var queryId = (0, _utils.getFirstParameter)(window.location.search);\n $.get('/ui/api/query/' + queryId, function (query) {\n var lastSnapshotStages = this.state.lastSnapshotStage;\n if (this.state.stageRefresh) {\n lastSnapshotStages = query.outputStage;\n }\n var lastSnapshotTasks = this.state.lastSnapshotTasks;\n if (this.state.taskRefresh) {\n lastSnapshotTasks = query.outputStage;\n }\n\n var lastRefresh = this.state.lastRefresh;\n var lastScheduledTime = this.state.lastScheduledTime;\n var lastCpuTime = this.state.lastCpuTime;\n var lastRowInput = this.state.lastRowInput;\n var lastByteInput = this.state.lastByteInput;\n var lastPhysicalInput = this.state.lastPhysicalInput;\n var lastPhysicalTime = this.state.lastPhysicalTime;\n var alreadyEnded = this.state.queryEnded;\n var nowMillis = Date.now();\n\n this.setState({\n query: query,\n lastSnapshotStage: lastSnapshotStages,\n lastSnapshotTasks: lastSnapshotTasks,\n\n lastPhysicalTime: (0, _utils.parseDuration)(query.queryStats.physicalInputReadTime),\n lastScheduledTime: (0, _utils.parseDuration)(query.queryStats.totalScheduledTime),\n lastCpuTime: (0, _utils.parseDuration)(query.queryStats.totalCpuTime),\n lastRowInput: query.queryStats.processedInputPositions,\n lastByteInput: (0, _utils.parseDataSize)(query.queryStats.processedInputDataSize),\n lastPhysicalInput: (0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize),\n\n initialized: true,\n queryEnded: !!query.finalQueryInfo,\n\n lastRefresh: nowMillis\n });\n\n // i.e. don't show sparklines if we've already decided not to update or if we don't have one previous measurement\n if (alreadyEnded || lastRefresh === null && query.state === \"RUNNING\") {\n this.resetTimer();\n return;\n }\n\n if (lastRefresh === null) {\n lastRefresh = nowMillis - (0, _utils.parseDuration)(query.queryStats.elapsedTime);\n }\n\n var elapsedSecsSinceLastRefresh = (nowMillis - lastRefresh) / 1000.0;\n if (elapsedSecsSinceLastRefresh >= 0) {\n var currentScheduledTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalScheduledTime) - lastScheduledTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentCpuTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalCpuTime) - lastCpuTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentPhysicalReadTime = ((0, _utils.parseDuration)(query.queryStats.physicalInputReadTime) - lastPhysicalTime) / 1000;\n var currentRowInputRate = (query.queryStats.processedInputPositions - lastRowInput) / elapsedSecsSinceLastRefresh;\n var currentByteInputRate = ((0, _utils.parseDataSize)(query.queryStats.processedInputDataSize) - lastByteInput) / elapsedSecsSinceLastRefresh;\n var currentPhysicalInputRate = currentPhysicalReadTime > 0 ? ((0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize) - lastPhysicalInput) / currentPhysicalReadTime : 0;\n\n this.setState({\n scheduledTimeRate: (0, _utils.addToHistory)(currentScheduledTimeRate, this.state.scheduledTimeRate),\n cpuTimeRate: (0, _utils.addToHistory)(currentCpuTimeRate, this.state.cpuTimeRate),\n rowInputRate: (0, _utils.addToHistory)(currentRowInputRate, this.state.rowInputRate),\n byteInputRate: (0, _utils.addToHistory)(currentByteInputRate, this.state.byteInputRate),\n reservedMemory: (0, _utils.addToHistory)((0, _utils.parseDataSize)(query.queryStats.totalMemoryReservation), this.state.reservedMemory),\n physicalInputRate: (0, _utils.addToHistory)(currentPhysicalInputRate, this.state.physicalInputRate)\n });\n }\n this.resetTimer();\n }.bind(this)).fail(function () {\n _this5.setState({\n initialized: true\n });\n _this5.resetTimer();\n });\n }\n }, {\n key: \"handleTaskRefreshClick\",\n value: function handleTaskRefreshClick() {\n if (this.state.taskRefresh) {\n this.setState({\n taskRefresh: false,\n lastSnapshotTasks: this.state.query.outputStage\n });\n } else {\n this.setState({\n taskRefresh: true\n });\n }\n }\n }, {\n key: \"renderTaskRefreshButton\",\n value: function renderTaskRefreshButton() {\n if (this.state.taskRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"handleStageRefreshClick\",\n value: function handleStageRefreshClick() {\n if (this.state.stageRefresh) {\n this.setState({\n stageRefresh: false,\n lastSnapshotStages: this.state.query.outputStage\n });\n } else {\n this.setState({\n stageRefresh: true\n });\n }\n }\n }, {\n key: \"renderStageRefreshButton\",\n value: function renderStageRefreshButton() {\n if (this.state.stageRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"renderTaskFilterListItem\",\n value: function renderTaskFilterListItem(taskFilter, taskFilterText) {\n return _react2.default.createElement(\n \"li\",\n null,\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: this.state.taskFilter === taskFilter ? \"selected\" : \"\", onClick: this.handleTaskFilterClick.bind(this, taskFilter) },\n taskFilterText\n )\n );\n }\n }, {\n key: \"handleTaskFilterClick\",\n value: function handleTaskFilterClick(filter, event) {\n this.setState({\n taskFilter: filter\n });\n event.preventDefault();\n }\n }, {\n key: \"getTasksFromStage\",\n value: function getTasksFromStage(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages') || !stage.hasOwnProperty('tasks')) {\n return [];\n }\n\n return [].concat.apply(stage.tasks, stage.subStages.map(this.getTasksFromStage, this));\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.refreshLoop();\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000 || this.state.ended && !this.state.renderingEnded) {\n var renderTimestamp = Date.now();\n $('#scheduled-time-rate-sparkline').sparkline(this.state.scheduledTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {\n chartRangeMin: 0,\n numberFormatter: _utils.precisionRound\n }));\n $('#cpu-time-rate-sparkline').sparkline(this.state.cpuTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { chartRangeMin: 0, numberFormatter: _utils.precisionRound }));\n $('#row-input-rate-sparkline').sparkline(this.state.rowInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatCount }));\n $('#byte-input-rate-sparkline').sparkline(this.state.byteInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#reserved-memory-sparkline').sparkline(this.state.reservedMemory, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#physical-input-rate-sparkline').sparkline(this.state.physicalInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n\n if (this.state.lastRender === null) {\n $('#query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n\n $('#prepared-query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n }\n\n this.setState({\n renderingEnded: this.state.ended,\n lastRender: renderTimestamp\n });\n }\n\n $('[data-toggle=\"tooltip\"]').tooltip();\n new window.ClipboardJS('.copy-button');\n }\n }, {\n key: \"renderTasks\",\n value: function renderTasks() {\n var _this6 = this;\n\n if (this.state.lastSnapshotTasks === null) {\n return;\n }\n\n var tasks = this.getTasksFromStage(this.state.lastSnapshotTasks).filter(function (task) {\n return _this6.state.taskFilter(task.taskStatus.state);\n }, this);\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Tasks\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"input-group-btn text-right\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"btn btn-default dropdown-toggle pull-right text-right\", \"data-toggle\": \"dropdown\", \"aria-haspopup\": \"true\",\n \"aria-expanded\": \"false\" },\n \"Show \",\n _react2.default.createElement(\"span\", { className: \"caret\" })\n ),\n _react2.default.createElement(\n \"ul\",\n { className: \"dropdown-menu\" },\n this.renderTaskFilterListItem(TASK_FILTER.ALL, \"All\"),\n this.renderTaskFilterListItem(TASK_FILTER.PLANNED, \"Planned\"),\n this.renderTaskFilterListItem(TASK_FILTER.RUNNING, \"Running\"),\n this.renderTaskFilterListItem(TASK_FILTER.FINISHED, \"Finished\"),\n this.renderTaskFilterListItem(TASK_FILTER.FAILED, \"Aborted/Canceled/Failed\")\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n \"\\xA0\\xA0\",\n this.renderTaskRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(TaskList, { key: this.state.query.queryId, tasks: tasks })\n )\n )\n );\n }\n }, {\n key: \"renderStages\",\n value: function renderStages() {\n if (this.state.lastSnapshotStage === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Stages\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n this.renderStageRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(StageList, { key: this.state.query.queryId, outputStage: this.state.lastSnapshotStage })\n )\n )\n );\n }\n }, {\n key: \"renderPreparedQuery\",\n value: function renderPreparedQuery() {\n var query = this.state.query;\n if (!query.hasOwnProperty('preparedQuery') || query.preparedQuery === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Prepared Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#prepared-query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"prepared-query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"prepared-query-text\" },\n query.preparedQuery\n )\n )\n );\n }\n }, {\n key: \"renderSessionProperties\",\n value: function renderSessionProperties() {\n var query = this.state.query;\n\n var properties = [];\n for (var property in query.session.systemProperties) {\n if (query.session.systemProperties.hasOwnProperty(property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n property + \"=\" + query.session.systemProperties[property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n for (var catalog in query.session.catalogProperties) {\n if (query.session.catalogProperties.hasOwnProperty(catalog)) {\n for (var _property in query.session.catalogProperties[catalog]) {\n if (query.session.catalogProperties[catalog].hasOwnProperty(_property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n catalog + \".\" + _property + \"=\" + query.session.catalogProperties[catalog][_property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n }\n }\n\n return properties;\n }\n }, {\n key: \"renderResourceEstimates\",\n value: function renderResourceEstimates() {\n var query = this.state.query;\n var estimates = query.session.resourceEstimates;\n var renderedEstimates = [];\n\n for (var resource in estimates) {\n if (estimates.hasOwnProperty(resource)) {\n var upperChars = resource.match(/([A-Z])/g) || [];\n var snakeCased = resource;\n for (var i = 0, n = upperChars.length; i < n; i++) {\n snakeCased = snakeCased.replace(new RegExp(upperChars[i]), '_' + upperChars[i].toLowerCase());\n }\n\n renderedEstimates.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n snakeCased + \"=\" + query.session.resourceEstimates[resource],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n return renderedEstimates;\n }\n }, {\n key: \"renderWarningInfo\",\n value: function renderWarningInfo() {\n var query = this.state.query;\n if (query.warnings.length > 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Warnings\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"warnings-table\" },\n query.warnings.map(function (warning) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n warning.warningCode.name\n ),\n _react2.default.createElement(\n \"td\",\n null,\n warning.message\n )\n );\n })\n )\n )\n );\n } else {\n return null;\n }\n }\n }, {\n key: \"renderFailureInfo\",\n value: function renderFailureInfo() {\n var query = this.state.query;\n if (query.failureInfo) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Error Information\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Type\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorType\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Code\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorCode.name + \" (\" + this.state.query.errorCode.code + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Stack Trace\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#stack-trace\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n _react2.default.createElement(\n \"pre\",\n { id: \"stack-trace\" },\n QueryDetail.formatStackTrace(query.failureInfo)\n )\n )\n )\n )\n )\n )\n );\n } else {\n return \"\";\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var query = this.state.query;\n\n if (query === null || this.state.initialized === false) {\n var label = _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n );\n if (this.state.initialized) {\n label = \"Query not found\";\n }\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n label\n )\n )\n );\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(_QueryHeader.QueryHeader, { query: query }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Session\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"User\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n _react2.default.createElement(\n \"span\",\n { id: \"query-user\" },\n query.session.user\n ),\n \"\\xA0\\xA0\",\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: \"copy-button\", \"data-clipboard-target\": \"#query-user\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Principal\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.principal\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Source\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.source\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Catalog\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.catalog\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Schema\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.schema\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Time zone\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.timeZone\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Address\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.remoteUserAddress\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Tags\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.clientTags.join(\", \")\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Session Properties\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderSessionProperties()\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Estimates\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderResourceEstimates()\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Execution\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Group\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.resourceGroupId ? query.resourceGroupId.join(\".\") : \"n/a\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Submission Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatShortDateTime)(new Date(query.queryStats.createTime))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Completion Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.endTime ? (0, _utils.formatShortDateTime)(new Date(query.queryStats.endTime)) : \"\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Elapsed Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.elapsedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Queued Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.queuedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Analysis Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.analysisTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Planning Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.planningTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Execution Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.executionTime\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Resource Utilization Summary\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalCpuTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.processedInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.processedInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.physicalInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Read Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.physicalInputReadTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.internalNetworkInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.internalNetworkInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakUserMemoryReservation)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.peakRevocableMemoryReservation) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Revocable Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakRevocableMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Total Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakTotalMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Pool\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.memoryPool\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeUserMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative System Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeSystemMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.outputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.outputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Written Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.writtenPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Logical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.logicalWrittenDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalWrittenDataSize)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.spilledDataSize) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Spilled Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.spilledDataSize)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Timeline\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Parallelism\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"cpu-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.cpuTimeRate[this.state.cpuTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"scheduled-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.scheduledTimeRate[this.state.scheduledTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"row-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.rowInputRate[this.state.rowInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"byte-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.byteInputRate[this.state.byteInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"physical-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.physicalInputRate[this.state.physicalInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Utilization\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"reserved-memory-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.reservedMemory[this.state.reservedMemory.length - 1])\n )\n )\n )\n )\n )\n )\n )\n ),\n this.renderWarningInfo(),\n this.renderFailureInfo(),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"query-text\" },\n query.query\n )\n )\n ),\n this.renderPreparedQuery()\n ),\n this.renderStages(),\n this.renderTasks()\n );\n }\n }], [{\n key: \"formatStackTrace\",\n value: function formatStackTrace(info) {\n return QueryDetail.formatStackTraceHelper(info, [], \"\", \"\");\n }\n }, {\n key: \"formatStackTraceHelper\",\n value: function formatStackTraceHelper(info, parentStack, prefix, linePrefix) {\n var s = linePrefix + prefix + QueryDetail.failureInfoToString(info) + \"\\n\";\n\n if (info.stack) {\n var sharedStackFrames = 0;\n if (parentStack !== null) {\n sharedStackFrames = QueryDetail.countSharedStackFrames(info.stack, parentStack);\n }\n\n for (var i = 0; i < info.stack.length - sharedStackFrames; i++) {\n s += linePrefix + \"\\tat \" + info.stack[i] + \"\\n\";\n }\n if (sharedStackFrames !== 0) {\n s += linePrefix + \"\\t... \" + sharedStackFrames + \" more\" + \"\\n\";\n }\n }\n\n if (info.suppressed) {\n for (var _i3 = 0; _i3 < info.suppressed.length; _i3++) {\n s += QueryDetail.formatStackTraceHelper(info.suppressed[_i3], info.stack, \"Suppressed: \", linePrefix + \"\\t\");\n }\n }\n\n if (info.cause) {\n s += QueryDetail.formatStackTraceHelper(info.cause, info.stack, \"Caused by: \", linePrefix);\n }\n\n return s;\n }\n }, {\n key: \"countSharedStackFrames\",\n value: function countSharedStackFrames(stack, parentStack) {\n var n = 0;\n var minStackLength = Math.min(stack.length, parentStack.length);\n while (n < minStackLength && stack[stack.length - 1 - n] === parentStack[parentStack.length - 1 - n]) {\n n++;\n }\n return n;\n }\n }, {\n key: \"failureInfoToString\",\n value: function failureInfoToString(t) {\n return t.message !== null ? t.type + \": \" + t.message : t.type;\n }\n }]);\n\n return QueryDetail;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/QueryDetail.jsx?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.QueryDetail = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactable = __webpack_require__(/*! reactable */ \"./node_modules/reactable/lib/reactable.js\");\n\nvar _reactable2 = _interopRequireDefault(_reactable);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nvar _QueryHeader = __webpack_require__(/*! ./QueryHeader */ \"./components/QueryHeader.jsx\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar Table = _reactable2.default.Table,\n Thead = _reactable2.default.Thead,\n Th = _reactable2.default.Th,\n Tr = _reactable2.default.Tr,\n Td = _reactable2.default.Td;\n\nvar TaskList = function (_React$Component) {\n _inherits(TaskList, _React$Component);\n\n function TaskList() {\n _classCallCheck(this, TaskList);\n\n return _possibleConstructorReturn(this, (TaskList.__proto__ || Object.getPrototypeOf(TaskList)).apply(this, arguments));\n }\n\n _createClass(TaskList, [{\n key: \"render\",\n value: function render() {\n var tasks = this.props.tasks;\n\n if (tasks === undefined || tasks.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"No threads in the selected group\"\n )\n )\n );\n }\n\n var showPortNumbers = TaskList.showPortNumbers(tasks);\n\n var renderedTasks = tasks.map(function (task) {\n var elapsedTime = (0, _utils.parseDuration)(task.stats.elapsedTime);\n if (elapsedTime === 0) {\n elapsedTime = Date.now() - Date.parse(task.stats.createTime);\n }\n\n return _react2.default.createElement(\n Tr,\n { key: task.taskStatus.taskId },\n _react2.default.createElement(\n Td,\n { column: \"id\", value: task.taskStatus.taskId },\n _react2.default.createElement(\n \"a\",\n { href: \"/ui/api/worker/\" + task.taskStatus.nodeId + \"/task/\" + task.taskStatus.taskId + \"?pretty\" },\n (0, _utils.getTaskIdSuffix)(task.taskStatus.taskId)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"host\", value: (0, _utils.getHostname)(task.taskStatus.self) },\n _react2.default.createElement(\n \"a\",\n { href: \"worker.html?\" + task.taskStatus.nodeId, className: \"font-light\", target: \"_blank\" },\n showPortNumbers ? (0, _utils.getHostAndPort)(task.taskStatus.self) : (0, _utils.getHostname)(task.taskStatus.self)\n )\n ),\n _react2.default.createElement(\n Td,\n { column: \"state\", value: TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked) },\n TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rows\", value: task.stats.rawInputPositions },\n (0, _utils.formatCount)(task.stats.rawInputPositions)\n ),\n _react2.default.createElement(\n Td,\n { column: \"rowsSec\", value: (0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime) },\n (0, _utils.formatCount)((0, _utils.computeRate)(task.stats.rawInputPositions, elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytes\", value: (0, _utils.parseDataSize)(task.stats.rawInputDataSize) },\n (0, _utils.formatDataSizeBytes)((0, _utils.parseDataSize)(task.stats.rawInputDataSize))\n ),\n _react2.default.createElement(\n Td,\n { column: \"bytesSec\", value: (0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime) },\n (0, _utils.formatDataSizeBytes)((0, _utils.computeRate)((0, _utils.parseDataSize)(task.stats.rawInputDataSize), elapsedTime))\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsPending\", value: task.stats.queuedDrivers },\n task.stats.queuedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsRunning\", value: task.stats.runningDrivers },\n task.stats.runningDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsBlocked\", value: task.stats.blockedDrivers },\n task.stats.blockedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsDone\", value: task.stats.completedDrivers },\n task.stats.completedDrivers\n ),\n _react2.default.createElement(\n Td,\n { column: \"splitsSkippedByIndex\", value: task.stats.skippedSplitsByIndex },\n task.stats.skippedSplitsByIndex\n ),\n _react2.default.createElement(\n Td,\n { column: \"avgIndexReadTime\", value: (0, _utils.parseDuration)((0, _utils.computeAvgDuration)(task.stats.totalIndexReadTime, task.stats.completedDrivers)) },\n (0, _utils.computeAvgDuration)(task.stats.totalIndexReadTime, task.stats.completedDrivers)\n ),\n _react2.default.createElement(\n Td,\n { column: \"elapsedTime\", value: (0, _utils.parseDuration)(task.stats.elapsedTime) },\n task.stats.elapsedTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"cpuTime\", value: (0, _utils.parseDuration)(task.stats.totalCpuTime) },\n task.stats.totalCpuTime\n ),\n _react2.default.createElement(\n Td,\n { column: \"bufferedBytes\", value: task.outputBuffers.totalBufferedBytes },\n (0, _utils.formatDataSizeBytes)(task.outputBuffers.totalBufferedBytes)\n )\n );\n });\n\n return _react2.default.createElement(\n Table,\n { id: \"tasks\", className: \"table table-striped sortable\", sortable: [{\n column: 'id',\n sortFunction: TaskList.compareTaskId\n }, 'host', 'state', 'splitsPending', 'splitsRunning', 'splitsBlocked', 'splitsDone', 'splitsSkippedByIndex', 'avgIndexReadTime', 'rows', 'rowsSec', 'bytes', 'bytesSec', 'elapsedTime', 'cpuTime', 'bufferedBytes'],\n defaultSort: { column: 'id', direction: 'asc' } },\n _react2.default.createElement(\n Thead,\n null,\n _react2.default.createElement(\n Th,\n { column: \"id\" },\n \"ID\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"host\" },\n \"Host\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"state\" },\n \"State\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsPending\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-pause\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Pending splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsRunning\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-play\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Running splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsBlocked\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-bookmark\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Blocked splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsDone\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-ok\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Completed splits\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"splitsSkippedByIndex\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-share-alt\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Skipped splits by index\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"avgIndexReadTime\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-align-justify\", style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\",\n title: \"Average index read time per driver/split\" })\n ),\n _react2.default.createElement(\n Th,\n { column: \"rows\" },\n \"Rows\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"rowsSec\" },\n \"Rows/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytes\" },\n \"Bytes\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bytesSec\" },\n \"Bytes/s\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"elapsedTime\" },\n \"Elapsed\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"cpuTime\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n Th,\n { column: \"bufferedBytes\" },\n \"Buffered\"\n )\n ),\n renderedTasks\n );\n }\n }], [{\n key: \"removeQueryId\",\n value: function removeQueryId(id) {\n var pos = id.indexOf('.');\n if (pos !== -1) {\n return id.substring(pos + 1);\n }\n return id;\n }\n }, {\n key: \"compareTaskId\",\n value: function compareTaskId(taskA, taskB) {\n var taskIdArrA = TaskList.removeQueryId(taskA).split(\".\");\n var taskIdArrB = TaskList.removeQueryId(taskB).split(\".\");\n\n if (taskIdArrA.length > taskIdArrB.length) {\n return 1;\n }\n for (var i = 0; i < taskIdArrA.length; i++) {\n var anum = Number.parseInt(taskIdArrA[i]);\n var bnum = Number.parseInt(taskIdArrB[i]);\n if (anum !== bnum) {\n return anum > bnum ? 1 : -1;\n }\n }\n\n return 0;\n }\n }, {\n key: \"showPortNumbers\",\n value: function showPortNumbers(tasks) {\n // check if any host has multiple port numbers\n var hostToPortNumber = {};\n for (var i = 0; i < tasks.length; i++) {\n var taskUri = tasks[i].taskStatus.self;\n var hostname = (0, _utils.getHostname)(taskUri);\n var port = (0, _utils.getPort)(taskUri);\n if (hostname in hostToPortNumber && hostToPortNumber[hostname] !== port) {\n return true;\n }\n hostToPortNumber[hostname] = port;\n }\n\n return false;\n }\n }, {\n key: \"formatState\",\n value: function formatState(state, fullyBlocked) {\n if (fullyBlocked && state === \"RUNNING\") {\n return \"BLOCKED\";\n } else {\n return state;\n }\n }\n }]);\n\n return TaskList;\n}(_react2.default.Component);\n\nvar BAR_CHART_WIDTH = 800;\n\nvar BAR_CHART_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#8997B3',\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: 'Task {{offset:offset}} - {{value}}',\n disableHiddenCheck: true\n};\n\nvar HISTOGRAM_WIDTH = 175;\n\nvar HISTOGRAM_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#747F96',\n zeroAxis: true,\n chartRangeMin: 0,\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: '{{offset:offset}} -- {{value}} tasks',\n disableHiddenCheck: true\n};\n\nvar StageSummary = function (_React$Component2) {\n _inherits(StageSummary, _React$Component2);\n\n function StageSummary(props) {\n _classCallCheck(this, StageSummary);\n\n var _this2 = _possibleConstructorReturn(this, (StageSummary.__proto__ || Object.getPrototypeOf(StageSummary)).call(this, props));\n\n _this2.state = {\n expanded: false,\n lastRender: null\n };\n return _this2;\n }\n\n _createClass(StageSummary, [{\n key: \"getExpandedIcon\",\n value: function getExpandedIcon() {\n return this.state.expanded ? \"glyphicon-chevron-up\" : \"glyphicon-chevron-down\";\n }\n }, {\n key: \"getExpandedStyle\",\n value: function getExpandedStyle() {\n return this.state.expanded ? {} : { display: \"none\" };\n }\n }, {\n key: \"toggleExpanded\",\n value: function toggleExpanded() {\n this.setState({\n expanded: !this.state.expanded\n });\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n var stage = this.props.stage;\n var numTasks = stage.tasks.length;\n\n // sort the x-axis\n stage.tasks.sort(function (taskA, taskB) {\n return (0, _utils.getTaskNumber)(taskA.taskStatus.taskId) - (0, _utils.getTaskNumber)(taskB.taskStatus.taskId);\n });\n\n var scheduledTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalScheduledTime);\n });\n var cpuTimes = stage.tasks.map(function (task) {\n return (0, _utils.parseDuration)(task.stats.totalCpuTime);\n });\n\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000) {\n var renderTimestamp = Date.now();\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n StageSummary.renderHistogram('#scheduled-time-histogram-' + stageId, scheduledTimes, _utils.formatDuration);\n StageSummary.renderHistogram('#cpu-time-histogram-' + stageId, cpuTimes, _utils.formatDuration);\n\n if (this.state.expanded) {\n // this needs to be a string otherwise it will also be passed to numberFormatter\n var tooltipValueLookups = { 'offset': {} };\n for (var i = 0; i < numTasks; i++) {\n tooltipValueLookups['offset'][i] = (0, _utils.getStageNumber)(stage.stageId) + \".\" + i;\n }\n\n var stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, { barWidth: BAR_CHART_WIDTH / numTasks, tooltipValueLookups: tooltipValueLookups });\n\n $('#scheduled-time-bar-chart-' + stageId).sparkline(scheduledTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n $('#cpu-time-bar-chart-' + stageId).sparkline(cpuTimes, $.extend({}, stageBarChartProperties, { numberFormatter: _utils.formatDuration }));\n }\n\n this.setState({\n lastRender: renderTimestamp\n });\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var stage = this.props.stage;\n if (stage === undefined || !stage.hasOwnProperty('plan')) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Information about this stage is unavailable.\"\n )\n );\n }\n\n var totalBufferedBytes = stage.tasks.map(function (task) {\n return task.outputBuffers.totalBufferedBytes;\n }).reduce(function (a, b) {\n return a + b;\n }, 0);\n\n var stageId = (0, _utils.getStageNumber)(stage.stageId);\n\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-id\" },\n _react2.default.createElement(\n \"div\",\n { className: \"stage-state-color\", style: { borderLeftColor: (0, _utils.getStageStateColor)(stage) } },\n stageId\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"table single-stage-table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-time\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Time\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Scheduled\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalBlockedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"CPU\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.stageStats.totalCpuTime\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-memory\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Memory\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Cumulative\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSizeBytes)(stage.stageStats.cumulativeUserMemory / 1000)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Current\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.userMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Buffers\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.formatDataSize)(totalBufferedBytes)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Peak\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n (0, _utils.parseAndFormatDataSize)(stage.stageStats.peakUserMemoryReservation)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table stage-table-tasks\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-stat-header\" },\n \"Tasks\"\n ),\n _react2.default.createElement(\"th\", null)\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Pending\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"PLANNED\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Running\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.taskStatus.state === \"RUNNING\";\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.filter(function (task) {\n return task.stats.fullyBlocked;\n }).length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title\" },\n \"Total\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-text\" },\n stage.tasks.length\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"Scheduled Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"scheduled-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"table\",\n { className: \"stage-table histogram-table\" },\n _react2.default.createElement(\n \"thead\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"th\",\n { className: \"stage-table-stat-title stage-table-chart-header\" },\n \"CPU Time Skew\"\n )\n )\n ),\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"histogram-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"histogram\", id: \"cpu-time-histogram-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"expand-charts-container\" },\n _react2.default.createElement(\n \"a\",\n { onClick: this.toggleExpanded.bind(this), className: \"expand-charts-button\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon \" + this.getExpandedIcon(), style: _utils.GLYPHICON_HIGHLIGHT, \"data-toggle\": \"tooltip\", \"data-placement\": \"top\", title: \"More\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"scheduled-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { style: this.getExpandedStyle() },\n _react2.default.createElement(\n \"td\",\n { colSpan: \"6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"expanded-chart\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"stage-table-stat-title expanded-chart-title\" },\n \"Task CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"bar-chart-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"bar-chart\", id: \"cpu-time-bar-chart-\" + stageId },\n _react2.default.createElement(\"div\", { className: \"loader\" })\n )\n )\n )\n )\n )\n )\n )\n )\n )\n )\n );\n }\n }], [{\n key: \"renderHistogram\",\n value: function renderHistogram(histogramId, inputData, numberFormatter) {\n var numBuckets = Math.min(HISTOGRAM_WIDTH, Math.sqrt(inputData.length));\n var dataMin = Math.min.apply(null, inputData);\n var dataMax = Math.max.apply(null, inputData);\n var bucketSize = (dataMax - dataMin) / numBuckets;\n\n var histogramData = [];\n if (bucketSize === 0) {\n histogramData = [inputData.length];\n } else {\n for (var i = 0; i < numBuckets + 1; i++) {\n histogramData.push(0);\n }\n\n for (var _i in inputData) {\n var dataPoint = inputData[_i];\n var bucket = Math.floor((dataPoint - dataMin) / bucketSize);\n histogramData[bucket] = histogramData[bucket] + 1;\n }\n }\n\n var tooltipValueLookups = { 'offset': {} };\n for (var _i2 = 0; _i2 < histogramData.length; _i2++) {\n tooltipValueLookups['offset'][_i2] = numberFormatter(dataMin + _i2 * bucketSize) + \"-\" + numberFormatter(dataMin + (_i2 + 1) * bucketSize);\n }\n\n var stageHistogramProperties = $.extend({}, HISTOGRAM_PROPERTIES, { barWidth: HISTOGRAM_WIDTH / histogramData.length, tooltipValueLookups: tooltipValueLookups });\n $(histogramId).sparkline(histogramData, stageHistogramProperties);\n }\n }]);\n\n return StageSummary;\n}(_react2.default.Component);\n\nvar StageList = function (_React$Component3) {\n _inherits(StageList, _React$Component3);\n\n function StageList() {\n _classCallCheck(this, StageList);\n\n return _possibleConstructorReturn(this, (StageList.__proto__ || Object.getPrototypeOf(StageList)).apply(this, arguments));\n }\n\n _createClass(StageList, [{\n key: \"getStages\",\n value: function getStages(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages')) {\n return [];\n }\n\n return [].concat.apply(stage, stage.subStages.map(this.getStages, this));\n }\n }, {\n key: \"render\",\n value: function render() {\n var stages = this.getStages(this.props.outputStage);\n\n if (stages === undefined || stages.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n \"No stage information available.\"\n )\n );\n }\n\n var renderedStages = stages.map(function (stage) {\n return _react2.default.createElement(StageSummary, { key: stage.stageId, stage: stage });\n });\n\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"stage-list\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n renderedStages\n )\n )\n )\n );\n }\n }]);\n\n return StageList;\n}(_react2.default.Component);\n\nvar SMALL_SPARKLINE_PROPERTIES = {\n width: '100%',\n height: '57px',\n fillColor: '#3F4552',\n lineColor: '#747F96',\n spotColor: '#1EDCFF',\n tooltipClassname: 'sparkline-tooltip',\n disableHiddenCheck: true\n};\n\nvar TASK_FILTER = {\n ALL: function ALL() {\n return true;\n },\n PLANNED: function PLANNED(state) {\n return state === 'PLANNED';\n },\n RUNNING: function RUNNING(state) {\n return state === 'RUNNING';\n },\n FINISHED: function FINISHED(state) {\n return state === 'FINISHED';\n },\n FAILED: function FAILED(state) {\n return state === 'FAILED' || state === 'ABORTED' || state === 'CANCELED';\n }\n};\n\nvar QueryDetail = exports.QueryDetail = function (_React$Component4) {\n _inherits(QueryDetail, _React$Component4);\n\n function QueryDetail(props) {\n _classCallCheck(this, QueryDetail);\n\n var _this4 = _possibleConstructorReturn(this, (QueryDetail.__proto__ || Object.getPrototypeOf(QueryDetail)).call(this, props));\n\n _this4.state = {\n query: null,\n lastSnapshotStages: null,\n lastSnapshotTasks: null,\n\n lastScheduledTime: 0,\n lastCpuTime: 0,\n lastRowInput: 0,\n lastByteInput: 0,\n lastPhysicalInput: 0,\n lastPhysicalTime: 0,\n\n scheduledTimeRate: [],\n cpuTimeRate: [],\n rowInputRate: [],\n byteInputRate: [],\n physicalInputRate: [],\n\n reservedMemory: [],\n\n initialized: false,\n queryEnded: false,\n renderingEnded: false,\n\n lastRefresh: null,\n lastRender: null,\n\n stageRefresh: true,\n taskRefresh: true,\n\n taskFilter: TASK_FILTER.ALL\n };\n\n _this4.refreshLoop = _this4.refreshLoop.bind(_this4);\n return _this4;\n }\n\n _createClass(QueryDetail, [{\n key: \"resetTimer\",\n value: function resetTimer() {\n clearTimeout(this.timeoutId);\n // stop refreshing when query finishes or fails\n if (this.state.query === null || !this.state.queryEnded) {\n // task.info-update-interval is set to 3 seconds by default\n this.timeoutId = setTimeout(this.refreshLoop, 3000);\n }\n }\n }, {\n key: \"refreshLoop\",\n value: function refreshLoop() {\n var _this5 = this;\n\n clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously\n var queryId = (0, _utils.getFirstParameter)(window.location.search);\n $.get('/ui/api/query/' + queryId, function (query) {\n var lastSnapshotStages = this.state.lastSnapshotStage;\n if (this.state.stageRefresh) {\n lastSnapshotStages = query.outputStage;\n }\n var lastSnapshotTasks = this.state.lastSnapshotTasks;\n if (this.state.taskRefresh) {\n lastSnapshotTasks = query.outputStage;\n }\n\n var lastRefresh = this.state.lastRefresh;\n var lastScheduledTime = this.state.lastScheduledTime;\n var lastCpuTime = this.state.lastCpuTime;\n var lastRowInput = this.state.lastRowInput;\n var lastByteInput = this.state.lastByteInput;\n var lastPhysicalInput = this.state.lastPhysicalInput;\n var lastPhysicalTime = this.state.lastPhysicalTime;\n var alreadyEnded = this.state.queryEnded;\n var nowMillis = Date.now();\n\n this.setState({\n query: query,\n lastSnapshotStage: lastSnapshotStages,\n lastSnapshotTasks: lastSnapshotTasks,\n\n lastPhysicalTime: (0, _utils.parseDuration)(query.queryStats.physicalInputReadTime),\n lastScheduledTime: (0, _utils.parseDuration)(query.queryStats.totalScheduledTime),\n lastCpuTime: (0, _utils.parseDuration)(query.queryStats.totalCpuTime),\n lastRowInput: query.queryStats.processedInputPositions,\n lastByteInput: (0, _utils.parseDataSize)(query.queryStats.processedInputDataSize),\n lastPhysicalInput: (0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize),\n\n initialized: true,\n queryEnded: !!query.finalQueryInfo,\n\n lastRefresh: nowMillis\n });\n\n // i.e. don't show sparklines if we've already decided not to update or if we don't have one previous measurement\n if (alreadyEnded || lastRefresh === null && query.state === \"RUNNING\") {\n this.resetTimer();\n return;\n }\n\n if (lastRefresh === null) {\n lastRefresh = nowMillis - (0, _utils.parseDuration)(query.queryStats.elapsedTime);\n }\n\n var elapsedSecsSinceLastRefresh = (nowMillis - lastRefresh) / 1000.0;\n if (elapsedSecsSinceLastRefresh >= 0) {\n var currentScheduledTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalScheduledTime) - lastScheduledTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentCpuTimeRate = ((0, _utils.parseDuration)(query.queryStats.totalCpuTime) - lastCpuTime) / (elapsedSecsSinceLastRefresh * 1000);\n var currentPhysicalReadTime = ((0, _utils.parseDuration)(query.queryStats.physicalInputReadTime) - lastPhysicalTime) / 1000;\n var currentRowInputRate = (query.queryStats.processedInputPositions - lastRowInput) / elapsedSecsSinceLastRefresh;\n var currentByteInputRate = ((0, _utils.parseDataSize)(query.queryStats.processedInputDataSize) - lastByteInput) / elapsedSecsSinceLastRefresh;\n var currentPhysicalInputRate = currentPhysicalReadTime > 0 ? ((0, _utils.parseDataSize)(query.queryStats.physicalInputDataSize) - lastPhysicalInput) / currentPhysicalReadTime : 0;\n\n this.setState({\n scheduledTimeRate: (0, _utils.addToHistory)(currentScheduledTimeRate, this.state.scheduledTimeRate),\n cpuTimeRate: (0, _utils.addToHistory)(currentCpuTimeRate, this.state.cpuTimeRate),\n rowInputRate: (0, _utils.addToHistory)(currentRowInputRate, this.state.rowInputRate),\n byteInputRate: (0, _utils.addToHistory)(currentByteInputRate, this.state.byteInputRate),\n reservedMemory: (0, _utils.addToHistory)((0, _utils.parseDataSize)(query.queryStats.totalMemoryReservation), this.state.reservedMemory),\n physicalInputRate: (0, _utils.addToHistory)(currentPhysicalInputRate, this.state.physicalInputRate)\n });\n }\n this.resetTimer();\n }.bind(this)).fail(function () {\n _this5.setState({\n initialized: true\n });\n _this5.resetTimer();\n });\n }\n }, {\n key: \"handleTaskRefreshClick\",\n value: function handleTaskRefreshClick() {\n if (this.state.taskRefresh) {\n this.setState({\n taskRefresh: false,\n lastSnapshotTasks: this.state.query.outputStage\n });\n } else {\n this.setState({\n taskRefresh: true\n });\n }\n }\n }, {\n key: \"renderTaskRefreshButton\",\n value: function renderTaskRefreshButton() {\n if (this.state.taskRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleTaskRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"handleStageRefreshClick\",\n value: function handleStageRefreshClick() {\n if (this.state.stageRefresh) {\n this.setState({\n stageRefresh: false,\n lastSnapshotStages: this.state.query.outputStage\n });\n } else {\n this.setState({\n stageRefresh: true\n });\n }\n }\n }, {\n key: \"renderStageRefreshButton\",\n value: function renderStageRefreshButton() {\n if (this.state.stageRefresh) {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: On\"\n );\n } else {\n return _react2.default.createElement(\n \"button\",\n { className: \"btn btn-info live-button\", onClick: this.handleStageRefreshClick.bind(this) },\n \"Auto-Refresh: Off\"\n );\n }\n }\n }, {\n key: \"renderTaskFilterListItem\",\n value: function renderTaskFilterListItem(taskFilter, taskFilterText) {\n return _react2.default.createElement(\n \"li\",\n null,\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: this.state.taskFilter === taskFilter ? \"selected\" : \"\", onClick: this.handleTaskFilterClick.bind(this, taskFilter) },\n taskFilterText\n )\n );\n }\n }, {\n key: \"handleTaskFilterClick\",\n value: function handleTaskFilterClick(filter, event) {\n this.setState({\n taskFilter: filter\n });\n event.preventDefault();\n }\n }, {\n key: \"getTasksFromStage\",\n value: function getTasksFromStage(stage) {\n if (stage === undefined || !stage.hasOwnProperty('subStages') || !stage.hasOwnProperty('tasks')) {\n return [];\n }\n\n return [].concat.apply(stage.tasks, stage.subStages.map(this.getTasksFromStage, this));\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.refreshLoop();\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts\n if (this.state.lastRender === null || Date.now() - this.state.lastRender >= 1000 || this.state.ended && !this.state.renderingEnded) {\n var renderTimestamp = Date.now();\n $('#scheduled-time-rate-sparkline').sparkline(this.state.scheduledTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {\n chartRangeMin: 0,\n numberFormatter: _utils.precisionRound\n }));\n $('#cpu-time-rate-sparkline').sparkline(this.state.cpuTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { chartRangeMin: 0, numberFormatter: _utils.precisionRound }));\n $('#row-input-rate-sparkline').sparkline(this.state.rowInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatCount }));\n $('#byte-input-rate-sparkline').sparkline(this.state.byteInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#reserved-memory-sparkline').sparkline(this.state.reservedMemory, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n $('#physical-input-rate-sparkline').sparkline(this.state.physicalInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { numberFormatter: _utils.formatDataSize }));\n\n if (this.state.lastRender === null) {\n $('#query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n\n $('#prepared-query').each(function (i, block) {\n hljs.highlightBlock(block);\n });\n }\n\n this.setState({\n renderingEnded: this.state.ended,\n lastRender: renderTimestamp\n });\n }\n\n $('[data-toggle=\"tooltip\"]').tooltip();\n new window.ClipboardJS('.copy-button');\n }\n }, {\n key: \"renderTasks\",\n value: function renderTasks() {\n var _this6 = this;\n\n if (this.state.lastSnapshotTasks === null) {\n return;\n }\n\n var tasks = this.getTasksFromStage(this.state.lastSnapshotTasks).filter(function (task) {\n return _this6.state.taskFilter(task.taskStatus.state);\n }, this);\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Tasks\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"input-group-btn text-right\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"btn btn-default dropdown-toggle pull-right text-right\", \"data-toggle\": \"dropdown\", \"aria-haspopup\": \"true\",\n \"aria-expanded\": \"false\" },\n \"Show \",\n _react2.default.createElement(\"span\", { className: \"caret\" })\n ),\n _react2.default.createElement(\n \"ul\",\n { className: \"dropdown-menu\" },\n this.renderTaskFilterListItem(TASK_FILTER.ALL, \"All\"),\n this.renderTaskFilterListItem(TASK_FILTER.PLANNED, \"Planned\"),\n this.renderTaskFilterListItem(TASK_FILTER.RUNNING, \"Running\"),\n this.renderTaskFilterListItem(TASK_FILTER.FINISHED, \"Finished\"),\n this.renderTaskFilterListItem(TASK_FILTER.FAILED, \"Aborted/Canceled/Failed\")\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n \"\\xA0\\xA0\",\n this.renderTaskRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(TaskList, { key: this.state.query.queryId, tasks: tasks })\n )\n )\n );\n }\n }, {\n key: \"renderStages\",\n value: function renderStages() {\n if (this.state.lastSnapshotStage === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-9\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Stages\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-3\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n this.renderStageRefreshButton()\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(StageList, { key: this.state.query.queryId, outputStage: this.state.lastSnapshotStage })\n )\n )\n );\n }\n }, {\n key: \"renderPreparedQuery\",\n value: function renderPreparedQuery() {\n var query = this.state.query;\n if (!query.hasOwnProperty('preparedQuery') || query.preparedQuery === null) {\n return;\n }\n\n return _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Prepared Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#prepared-query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"prepared-query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"prepared-query-text\" },\n query.preparedQuery\n )\n )\n );\n }\n }, {\n key: \"renderSessionProperties\",\n value: function renderSessionProperties() {\n var query = this.state.query;\n\n var properties = [];\n for (var property in query.session.systemProperties) {\n if (query.session.systemProperties.hasOwnProperty(property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n property + \"=\" + query.session.systemProperties[property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n for (var catalog in query.session.catalogProperties) {\n if (query.session.catalogProperties.hasOwnProperty(catalog)) {\n for (var _property in query.session.catalogProperties[catalog]) {\n if (query.session.catalogProperties[catalog].hasOwnProperty(_property)) {\n properties.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n catalog + \".\" + _property + \"=\" + query.session.catalogProperties[catalog][_property],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n }\n }\n\n return properties;\n }\n }, {\n key: \"renderResourceEstimates\",\n value: function renderResourceEstimates() {\n var query = this.state.query;\n var estimates = query.session.resourceEstimates;\n var renderedEstimates = [];\n\n for (var resource in estimates) {\n if (estimates.hasOwnProperty(resource)) {\n var upperChars = resource.match(/([A-Z])/g) || [];\n var snakeCased = resource;\n for (var i = 0, n = upperChars.length; i < n; i++) {\n snakeCased = snakeCased.replace(new RegExp(upperChars[i]), '_' + upperChars[i].toLowerCase());\n }\n\n renderedEstimates.push(_react2.default.createElement(\n \"span\",\n null,\n \"- \",\n snakeCased + \"=\" + query.session.resourceEstimates[resource],\n \" \",\n _react2.default.createElement(\"br\", null)\n ));\n }\n }\n\n return renderedEstimates;\n }\n }, {\n key: \"renderWarningInfo\",\n value: function renderWarningInfo() {\n var query = this.state.query;\n if (query.warnings.length > 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Warnings\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\", id: \"warnings-table\" },\n query.warnings.map(function (warning) {\n return _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n warning.warningCode.name\n ),\n _react2.default.createElement(\n \"td\",\n null,\n warning.message\n )\n );\n })\n )\n )\n );\n } else {\n return null;\n }\n }\n }, {\n key: \"renderFailureInfo\",\n value: function renderFailureInfo() {\n var query = this.state.query;\n if (query.failureInfo) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Error Information\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Type\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorType\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Error Code\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.errorCode.name + \" (\" + this.state.query.errorCode.code + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Stack Trace\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#stack-trace\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n _react2.default.createElement(\n \"pre\",\n { id: \"stack-trace\" },\n QueryDetail.formatStackTrace(query.failureInfo)\n )\n )\n )\n )\n )\n )\n );\n } else {\n return \"\";\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var query = this.state.query;\n\n if (query === null || this.state.initialized === false) {\n var label = _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n );\n if (this.state.initialized) {\n label = \"Query not found\";\n }\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n label\n )\n )\n );\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(_QueryHeader.QueryHeader, { query: query }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Session\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"User\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n _react2.default.createElement(\n \"span\",\n { id: \"query-user\" },\n query.session.user\n ),\n \"\\xA0\\xA0\",\n _react2.default.createElement(\n \"a\",\n { href: \"#\", className: \"copy-button\", \"data-clipboard-target\": \"#query-user\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Principal\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.principal\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Source\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.session.source\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Catalog\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.catalog\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Schema\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.schema\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Time zone\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.timeZone\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Address\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.remoteUserAddress\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Client Tags\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.session.clientTags.join(\", \")\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Session Properties\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderSessionProperties()\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Estimates\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n this.renderResourceEstimates()\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Execution\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Resource Group\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text wrap-text\" },\n query.resourceGroupId ? query.resourceGroupId.join(\".\") : \"n/a\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Submission Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatShortDateTime)(new Date(query.queryStats.createTime))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Completion Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.endTime ? (0, _utils.formatShortDateTime)(new Date(query.queryStats.endTime)) : \"\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Elapsed Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.elapsedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Queued Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.queuedTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Analysis Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.analysisTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Planning Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.planningTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Execution Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.executionTime\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Resource Utilization Summary\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"CPU Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalCpuTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.totalScheduledTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.processedInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.processedInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.physicalInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Read Time\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.queryStats.physicalInputReadTime\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.internalNetworkInputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Internal Network Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.internalNetworkInputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakUserMemoryReservation)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.peakRevocableMemoryReservation) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Revocable Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakRevocableMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Peak Total Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.peakTotalMemoryReservation)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Pool\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n query.memoryPool\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative User Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeUserMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Cumulative System Memory\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatDataSizeBytes)(query.queryStats.cumulativeSystemMemory / 1000.0) + \" seconds\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.outputPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Output Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.outputDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Written Rows\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.formatCount)(query.queryStats.writtenPositions)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Logical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.logicalWrittenDataSize)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Written Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.physicalWrittenDataSize)\n )\n ),\n (0, _utils.parseDataSize)(query.queryStats.spilledDataSize) > 0 && _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Spilled Data\"\n ),\n _react2.default.createElement(\n \"td\",\n { className: \"info-text\" },\n (0, _utils.parseAndFormatDataSize)(query.queryStats.spilledDataSize)\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Timeline\"\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Parallelism\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"cpu-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.cpuTimeRate[this.state.cpuTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Scheduled Time/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"scheduled-time-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.scheduledTimeRate[this.state.scheduledTimeRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Rows/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"row-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatCount)(this.state.rowInputRate[this.state.rowInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"byte-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.byteInputRate[this.state.byteInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Physical Input Bytes/s\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"physical-input-rate-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.physicalInputRate[this.state.physicalInputRate.length - 1])\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { className: \"info-title\" },\n \"Memory Utilization\"\n ),\n _react2.default.createElement(\n \"td\",\n { rowSpan: \"2\" },\n _react2.default.createElement(\n \"div\",\n { className: \"query-stats-sparkline-container\" },\n _react2.default.createElement(\n \"span\",\n { className: \"sparkline\", id: \"reserved-memory-sparkline\" },\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading ...\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"tr\",\n { className: \"tr-noborder\" },\n _react2.default.createElement(\n \"td\",\n { className: \"info-sparkline-text\" },\n (0, _utils.formatDataSize)(this.state.reservedMemory[this.state.reservedMemory.length - 1])\n )\n )\n )\n )\n )\n )\n )\n ),\n this.renderWarningInfo(),\n this.renderFailureInfo(),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Query\",\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#query-text\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n ),\n _react2.default.createElement(\n \"pre\",\n { id: \"query\" },\n _react2.default.createElement(\n \"code\",\n { className: \"lang-sql\", id: \"query-text\" },\n query.query\n )\n )\n ),\n this.renderPreparedQuery()\n ),\n this.renderStages(),\n this.renderTasks()\n );\n }\n }], [{\n key: \"formatStackTrace\",\n value: function formatStackTrace(info) {\n return QueryDetail.formatStackTraceHelper(info, [], \"\", \"\");\n }\n }, {\n key: \"formatStackTraceHelper\",\n value: function formatStackTraceHelper(info, parentStack, prefix, linePrefix) {\n var s = linePrefix + prefix + QueryDetail.failureInfoToString(info) + \"\\n\";\n\n if (info.stack) {\n var sharedStackFrames = 0;\n if (parentStack !== null) {\n sharedStackFrames = QueryDetail.countSharedStackFrames(info.stack, parentStack);\n }\n\n for (var i = 0; i < info.stack.length - sharedStackFrames; i++) {\n s += linePrefix + \"\\tat \" + info.stack[i] + \"\\n\";\n }\n if (sharedStackFrames !== 0) {\n s += linePrefix + \"\\t... \" + sharedStackFrames + \" more\" + \"\\n\";\n }\n }\n\n if (info.suppressed) {\n for (var _i3 = 0; _i3 < info.suppressed.length; _i3++) {\n s += QueryDetail.formatStackTraceHelper(info.suppressed[_i3], info.stack, \"Suppressed: \", linePrefix + \"\\t\");\n }\n }\n\n if (info.cause) {\n s += QueryDetail.formatStackTraceHelper(info.cause, info.stack, \"Caused by: \", linePrefix);\n }\n\n return s;\n }\n }, {\n key: \"countSharedStackFrames\",\n value: function countSharedStackFrames(stack, parentStack) {\n var n = 0;\n var minStackLength = Math.min(stack.length, parentStack.length);\n while (n < minStackLength && stack[stack.length - 1 - n] === parentStack[parentStack.length - 1 - n]) {\n n++;\n }\n return n;\n }\n }, {\n key: \"failureInfoToString\",\n value: function failureInfoToString(t) {\n return t.message !== null ? t.type + \": \" + t.message : t.type;\n }\n }]);\n\n return QueryDetail;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/QueryDetail.jsx?"); /***/ }), @@ -20820,7 +20820,7 @@ eval("\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/i /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/stage.js b/core/trino-main/src/main/resources/webapp/dist/stage.js index 9a18d4aed358..97b7fa3a5272 100644 --- a/core/trino-main/src/main/resources/webapp/dist/stage.js +++ b/core/trino-main/src/main/resources/webapp/dist/stage.js @@ -118,7 +118,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n}); /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.StageDetail = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactDom = __webpack_require__(/*! react-dom */ \"./node_modules/react-dom/index.js\");\n\nvar _reactDom2 = _interopRequireDefault(_reactDom);\n\nvar _server = __webpack_require__(/*! react-dom/server */ \"./node_modules/react-dom/server.browser.js\");\n\nvar _server2 = _interopRequireDefault(_server);\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nvar _QueryHeader = __webpack_require__(/*! ./QueryHeader */ \"./components/QueryHeader.jsx\");\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nfunction getTotalWallTime(operator) {\n return (0, _utils.parseDuration)(operator.addInputWall) + (0, _utils.parseDuration)(operator.getOutputWall) + (0, _utils.parseDuration)(operator.finishWall) + (0, _utils.parseDuration)(operator.blockedWall);\n}\n\nvar OperatorSummary = function (_React$Component) {\n _inherits(OperatorSummary, _React$Component);\n\n function OperatorSummary() {\n _classCallCheck(this, OperatorSummary);\n\n return _possibleConstructorReturn(this, (OperatorSummary.__proto__ || Object.getPrototypeOf(OperatorSummary)).apply(this, arguments));\n }\n\n _createClass(OperatorSummary, [{\n key: \"render\",\n value: function render() {\n var operator = this.props.operator;\n\n var totalWallTime = (0, _utils.parseDuration)(operator.addInputWall) + (0, _utils.parseDuration)(operator.getOutputWall) + (0, _utils.parseDuration)(operator.finishWall) + (0, _utils.parseDuration)(operator.blockedWall);\n\n var rowInputRate = totalWallTime === 0 ? 0 : 1.0 * operator.inputPositions / (totalWallTime / 1000.0);\n var byteInputRate = totalWallTime === 0 ? 0 : 1.0 * (0, _utils.parseDataSize)(operator.inputDataSize) / (totalWallTime / 1000.0);\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"highlight-row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"header-row\" },\n operator.operatorType\n ),\n _react2.default.createElement(\n \"div\",\n null,\n (0, _utils.formatCount)(rowInputRate) + \" rows/s (\" + (0, _utils.formatDataSize)(byteInputRate) + \"/s)\"\n )\n ),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Output\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.outputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.outputDataSize) + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Drivers\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n operator.totalDrivers\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Wall Time\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)(totalWallTime)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)((0, _utils.parseDuration)(operator.blockedWall))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Input\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.inputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.inputDataSize) + \")\"\n )\n )\n )\n )\n );\n }\n }]);\n\n return OperatorSummary;\n}(_react2.default.Component);\n\nvar BAR_CHART_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#8997B3',\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: 'Task {{offset:offset}} - {{value}}',\n disableHiddenCheck: true\n};\n\nvar OperatorStatistic = function (_React$Component2) {\n _inherits(OperatorStatistic, _React$Component2);\n\n function OperatorStatistic() {\n _classCallCheck(this, OperatorStatistic);\n\n return _possibleConstructorReturn(this, (OperatorStatistic.__proto__ || Object.getPrototypeOf(OperatorStatistic)).apply(this, arguments));\n }\n\n _createClass(OperatorStatistic, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n var operators = this.props.operators;\n var statistic = operators.map(this.props.supplier);\n var numTasks = operators.length;\n\n var tooltipValueLookups = { 'offset': {} };\n for (var i = 0; i < numTasks; i++) {\n tooltipValueLookups['offset'][i] = \"\" + i;\n }\n\n var stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, { barWidth: 800 / numTasks, tooltipValueLookups: tooltipValueLookups });\n $('#' + this.props.id).sparkline(statistic, $.extend({}, stageBarChartProperties, { numberFormatter: this.props.renderer }));\n }\n }, {\n key: \"render\",\n value: function render() {\n return _react2.default.createElement(\n \"div\",\n { className: \"row operator-statistic\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2 italic-uppercase operator-statistic-title\" },\n this.props.name\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-10\" },\n _react2.default.createElement(\"span\", { className: \"bar-chart\", id: this.props.id })\n )\n );\n }\n }]);\n\n return OperatorStatistic;\n}(_react2.default.Component);\n\nvar OperatorDetail = function (_React$Component3) {\n _inherits(OperatorDetail, _React$Component3);\n\n function OperatorDetail(props) {\n _classCallCheck(this, OperatorDetail);\n\n var _this3 = _possibleConstructorReturn(this, (OperatorDetail.__proto__ || Object.getPrototypeOf(OperatorDetail)).call(this, props));\n\n _this3.state = {\n selectedStatistics: _this3.getInitialStatistics()\n };\n return _this3;\n }\n\n _createClass(OperatorDetail, [{\n key: \"getInitialStatistics\",\n value: function getInitialStatistics() {\n return [{\n name: \"Total Wall Time\",\n id: \"totalWallTime\",\n supplier: getTotalWallTime,\n renderer: _utils.formatDuration\n }, {\n name: \"Input Rows\",\n id: \"inputPositions\",\n supplier: function supplier(operator) {\n return operator.inputPositions;\n },\n renderer: _utils.formatCount\n }, {\n name: \"Input Data Size\",\n id: \"inputDataSize\",\n supplier: function supplier(operator) {\n return (0, _utils.parseDataSize)(operator.inputDataSize);\n },\n renderer: _utils.formatDataSize\n }, {\n name: \"Output Rows\",\n id: \"outputPositions\",\n supplier: function supplier(operator) {\n return operator.outputPositions;\n },\n renderer: _utils.formatCount\n }, {\n name: \"Output Data Size\",\n id: \"outputDataSize\",\n supplier: function supplier(operator) {\n return (0, _utils.parseDataSize)(operator.outputDataSize);\n },\n renderer: _utils.formatDataSize\n }];\n }\n }, {\n key: \"getOperatorTasks\",\n value: function getOperatorTasks() {\n // sort the x-axis\n var tasks = this.props.tasks.sort(function (taskA, taskB) {\n return (0, _utils.getTaskNumber)(taskA.taskStatus.taskId) - (0, _utils.getTaskNumber)(taskB.taskStatus.taskId);\n });\n\n var operatorSummary = this.props.operator;\n\n var operatorTasks = [];\n tasks.forEach(function (task) {\n task.stats.pipelines.forEach(function (pipeline) {\n if (pipeline.pipelineId === operatorSummary.pipelineId) {\n pipeline.operatorSummaries.forEach(function (operator) {\n if (operatorSummary.operatorId === operator.operatorId) {\n operatorTasks.push(operator);\n }\n });\n }\n });\n });\n\n return operatorTasks;\n }\n }, {\n key: \"render\",\n value: function render() {\n var operator = this.props.operator;\n var operatorTasks = this.getOperatorTasks();\n var totalWallTime = getTotalWallTime(operator);\n\n var rowInputRate = totalWallTime === 0 ? 0 : 1.0 * operator.inputPositions / totalWallTime;\n var byteInputRate = totalWallTime === 0 ? 0 : 1.0 * (0, _utils.parseDataSize)(operator.inputDataSize) / (totalWallTime / 1000.0);\n\n var rowOutputRate = totalWallTime === 0 ? 0 : 1.0 * operator.outputPositions / totalWallTime;\n var byteOutputRate = totalWallTime === 0 ? 0 : 1.0 * (0, _utils.parseDataSize)(operator.outputDataSize) / (totalWallTime / 1000.0);\n\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"modal-header\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"close\", \"data-dismiss\": \"modal\", \"aria-label\": \"Close\" },\n _react2.default.createElement(\n \"span\",\n { \"aria-hidden\": \"true\" },\n \"\\xD7\"\n )\n ),\n _react2.default.createElement(\n \"h3\",\n null,\n _react2.default.createElement(\n \"small\",\n null,\n \"Pipeline \",\n operator.pipelineId\n ),\n _react2.default.createElement(\"br\", null),\n operator.operatorType\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Input\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.inputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.inputDataSize) + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Input Rate\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(rowInputRate) + \" rows/s (\" + (0, _utils.formatDataSize)(byteInputRate) + \"/s)\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Output\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.outputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.outputDataSize) + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Output Rate\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(rowOutputRate) + \" rows/s (\" + (0, _utils.formatDataSize)(byteOutputRate) + \"/s)\"\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Wall Time\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)(totalWallTime)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)((0, _utils.parseDuration)(operator.blockedWall))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Drivers\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n operator.totalDrivers\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Tasks\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n operatorTasks.length\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row font-white\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2 italic-uppercase\" },\n _react2.default.createElement(\n \"strong\",\n null,\n \"Statistic\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-10 italic-uppercase\" },\n _react2.default.createElement(\n \"strong\",\n null,\n \"Tasks\"\n )\n )\n ),\n this.state.selectedStatistics.map(function (statistic) {\n return _react2.default.createElement(OperatorStatistic, {\n key: statistic.id,\n id: statistic.id,\n name: statistic.name,\n supplier: statistic.supplier,\n renderer: statistic.renderer,\n operators: operatorTasks });\n }.bind(this)),\n _react2.default.createElement(\"p\", null),\n _react2.default.createElement(\"p\", null)\n )\n );\n }\n }]);\n\n return OperatorDetail;\n}(_react2.default.Component);\n\nvar StageOperatorGraph = function (_React$Component4) {\n _inherits(StageOperatorGraph, _React$Component4);\n\n function StageOperatorGraph() {\n _classCallCheck(this, StageOperatorGraph);\n\n return _possibleConstructorReturn(this, (StageOperatorGraph.__proto__ || Object.getPrototypeOf(StageOperatorGraph)).apply(this, arguments));\n }\n\n _createClass(StageOperatorGraph, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.updateD3Graph();\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n this.updateD3Graph();\n }\n }, {\n key: \"handleOperatorClick\",\n value: function handleOperatorClick(operatorCssId) {\n $('#operator-detail-modal').modal();\n\n var pipelineId = parseInt(operatorCssId.split('-')[1]);\n var operatorId = parseInt(operatorCssId.split('-')[2]);\n var stage = this.props.stage;\n\n var operatorStageSummary = null;\n var operatorSummaries = stage.stageStats.operatorSummaries;\n for (var i = 0; i < operatorSummaries.length; i++) {\n if (operatorSummaries[i].pipelineId === pipelineId && operatorSummaries[i].operatorId === operatorId) {\n operatorStageSummary = operatorSummaries[i];\n }\n }\n\n _reactDom2.default.render(_react2.default.createElement(OperatorDetail, { key: operatorCssId, operator: operatorStageSummary, tasks: stage.tasks }), document.getElementById('operator-detail'));\n }\n }, {\n key: \"computeOperatorGraphs\",\n value: function computeOperatorGraphs(planNode, operatorMap) {\n var _this5 = this;\n\n var sources = (0, _utils.getChildren)(planNode);\n\n var sourceResults = new Map();\n sources.forEach(function (source) {\n var sourceResult = _this5.computeOperatorGraphs(source, operatorMap);\n sourceResult.forEach(function (operator, pipelineId) {\n if (sourceResults.has(pipelineId)) {\n console.error(\"Multiple sources for \", planNode['@type'], \" had the same pipeline ID\");\n return sourceResults;\n }\n sourceResults.set(pipelineId, operator);\n });\n });\n\n var nodeOperators = operatorMap.get(planNode.id);\n if (!nodeOperators || nodeOperators.length === 0) {\n return sourceResults;\n }\n\n var pipelineOperators = new Map();\n nodeOperators.forEach(function (operator) {\n if (!pipelineOperators.has(operator.pipelineId)) {\n pipelineOperators.set(operator.pipelineId, []);\n }\n pipelineOperators.get(operator.pipelineId).push(operator);\n });\n\n var result = new Map();\n pipelineOperators.forEach(function (pipelineOperators, pipelineId) {\n // sort deep-copied operators in this pipeline from source to sink\n var linkedOperators = pipelineOperators.map(function (a) {\n return Object.assign({}, a);\n }).sort(function (a, b) {\n return a.operatorId - b.operatorId;\n });\n var sinkOperator = linkedOperators[linkedOperators.length - 1];\n var sourceOperator = linkedOperators[0];\n\n if (sourceResults.has(pipelineId)) {\n var pipelineChildResult = sourceResults.get(pipelineId);\n if (pipelineChildResult) {\n sourceOperator.child = pipelineChildResult;\n }\n }\n\n // chain operators at this level\n var currentOperator = sourceOperator;\n linkedOperators.slice(1).forEach(function (source) {\n source.child = currentOperator;\n currentOperator = source;\n });\n\n result.set(pipelineId, sinkOperator);\n });\n\n sourceResults.forEach(function (operator, pipelineId) {\n if (!result.has(pipelineId)) {\n result.set(pipelineId, operator);\n }\n });\n\n return result;\n }\n }, {\n key: \"computeOperatorMap\",\n value: function computeOperatorMap() {\n var operatorMap = new Map();\n this.props.stage.stageStats.operatorSummaries.forEach(function (operator) {\n if (!operatorMap.has(operator.planNodeId)) {\n operatorMap.set(operator.planNodeId, []);\n }\n\n operatorMap.get(operator.planNodeId).push(operator);\n });\n\n return operatorMap;\n }\n }, {\n key: \"computeD3StageOperatorGraph\",\n value: function computeD3StageOperatorGraph(graph, operator, sink, pipelineNode) {\n var operatorNodeId = \"operator-\" + operator.pipelineId + \"-\" + operator.operatorId;\n\n // this is a non-standard use of ReactDOMServer, but it's the cleanest way to unify DagreD3 with React\n var html = _server2.default.renderToString(_react2.default.createElement(OperatorSummary, { key: operator.pipelineId + \"-\" + operator.operatorId, operator: operator }));\n graph.setNode(operatorNodeId, { class: \"operator-stats\", label: html, labelType: \"html\" });\n\n if (operator.hasOwnProperty(\"child\")) {\n this.computeD3StageOperatorGraph(graph, operator.child, operatorNodeId, pipelineNode);\n }\n\n if (sink !== null) {\n graph.setEdge(operatorNodeId, sink, { class: \"plan-edge\", arrowheadClass: \"plan-arrowhead\" });\n }\n\n graph.setParent(operatorNodeId, pipelineNode);\n }\n }, {\n key: \"updateD3Graph\",\n value: function updateD3Graph() {\n var _this6 = this;\n\n if (!this.props.stage) {\n return;\n }\n\n var stage = this.props.stage;\n var operatorMap = this.computeOperatorMap();\n var operatorGraphs = this.computeOperatorGraphs(stage.plan.root, operatorMap);\n\n var graph = (0, _utils.initializeGraph)();\n operatorGraphs.forEach(function (operator, pipelineId) {\n var pipelineNodeId = \"pipeline-\" + pipelineId;\n graph.setNode(pipelineNodeId, { label: \"Pipeline \" + pipelineId + \" \", clusterLabelPos: 'top', style: 'fill: #2b2b2b', labelStyle: 'fill: #fff' });\n _this6.computeD3StageOperatorGraph(graph, operator, null, pipelineNodeId);\n });\n\n $(\"#operator-canvas\").html(\"\");\n\n if (operatorGraphs.size > 0) {\n $(\".graph-container\").css(\"display\", \"block\");\n var svg = (0, _utils.initializeSvg)(\"#operator-canvas\");\n var render = new dagreD3.render();\n render(d3.select(\"#operator-canvas g\"), graph);\n\n svg.selectAll(\"g.operator-stats\").on(\"click\", this.handleOperatorClick.bind(this));\n svg.attr(\"height\", graph.graph().height);\n svg.attr(\"width\", graph.graph().width);\n } else {\n $(\".graph-container\").css(\"display\", \"none\");\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var stage = this.props.stage;\n\n if (!stage.hasOwnProperty('plan')) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Stage does not have a plan\"\n )\n )\n );\n }\n\n if (!stage.hasOwnProperty('stageStats') || !stage.stageStats.hasOwnProperty(\"operatorSummaries\") || stage.stageStats.operatorSummaries.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Operator data not available for \",\n stage.stageId\n )\n )\n );\n }\n\n return null;\n }\n }]);\n\n return StageOperatorGraph;\n}(_react2.default.Component);\n\nvar StageDetail = exports.StageDetail = function (_React$Component5) {\n _inherits(StageDetail, _React$Component5);\n\n function StageDetail(props) {\n _classCallCheck(this, StageDetail);\n\n var _this7 = _possibleConstructorReturn(this, (StageDetail.__proto__ || Object.getPrototypeOf(StageDetail)).call(this, props));\n\n _this7.state = {\n initialized: false,\n ended: false,\n\n selectedStageId: null,\n query: null,\n\n lastRefresh: null,\n lastRender: null\n };\n\n _this7.refreshLoop = _this7.refreshLoop.bind(_this7);\n return _this7;\n }\n\n _createClass(StageDetail, [{\n key: \"resetTimer\",\n value: function resetTimer() {\n clearTimeout(this.timeoutId);\n // stop refreshing when query finishes or fails\n if (this.state.query === null || !this.state.ended) {\n this.timeoutId = setTimeout(this.refreshLoop, 1000);\n }\n }\n }, {\n key: \"refreshLoop\",\n value: function refreshLoop() {\n var _this8 = this;\n\n clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously\n var queryString = (0, _utils.getFirstParameter)(window.location.search).split('.');\n var queryId = queryString[0];\n\n var selectedStageId = this.state.selectedStageId;\n if (selectedStageId === null) {\n selectedStageId = 0;\n if (queryString.length > 1) {\n selectedStageId = parseInt(queryString[1]);\n }\n }\n\n $.get('/ui/api/query/' + queryId, function (query) {\n _this8.setState({\n initialized: true,\n ended: query.finalQueryInfo,\n\n selectedStageId: selectedStageId,\n query: query\n });\n _this8.resetTimer();\n }).fail(function () {\n _this8.setState({\n initialized: true\n });\n _this8.resetTimer();\n });\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.refreshLoop();\n new window.ClipboardJS('.copy-button');\n }\n }, {\n key: \"findStage\",\n value: function findStage(stageId, currentStage) {\n if (stageId === null) {\n return null;\n }\n\n if (currentStage.stageId === stageId) {\n return currentStage;\n }\n\n for (var i = 0; i < currentStage.subStages.length; i++) {\n var stage = this.findStage(stageId, currentStage.subStages[i]);\n if (stage !== null) {\n return stage;\n }\n }\n\n return null;\n }\n }, {\n key: \"getAllStageIds\",\n value: function getAllStageIds(result, currentStage) {\n var _this9 = this;\n\n result.push(currentStage.plan.id);\n currentStage.subStages.forEach(function (stage) {\n _this9.getAllStageIds(result, stage);\n });\n }\n }, {\n key: \"render\",\n value: function render() {\n var _this10 = this;\n\n if (!this.state.query) {\n var label = _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n );\n if (this.state.initialized) {\n label = \"Query not found\";\n }\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n label\n )\n )\n );\n }\n\n if (!this.state.query.outputStage) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Query does not have an output stage\"\n )\n )\n );\n }\n\n var query = this.state.query;\n var allStages = [];\n this.getAllStageIds(allStages, query.outputStage);\n\n var stage = this.findStage(query.queryId + \".\" + this.state.selectedStageId, query.outputStage);\n if (stage === null) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Stage not found\"\n )\n )\n );\n }\n\n var stageOperatorGraph = null;\n if (!(0, _utils.isQueryEnded)(query)) {\n stageOperatorGraph = _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Operator graph will appear automatically when query completes.\"\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n )\n )\n );\n } else {\n stageOperatorGraph = _react2.default.createElement(StageOperatorGraph, { id: stage.stageId, stage: stage });\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(_QueryHeader.QueryHeader, { query: query }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Stage \",\n stage.plan.id\n )\n ),\n _react2.default.createElement(\"div\", { className: \"col-xs-8\" }),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2 stage-dropdown\" },\n _react2.default.createElement(\n \"div\",\n { className: \"input-group-btn\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"btn btn-default dropdown-toggle\", \"data-toggle\": \"dropdown\", \"aria-haspopup\": \"true\", \"aria-expanded\": \"false\" },\n \"Select Stage \",\n _react2.default.createElement(\"span\", { className: \"caret\" })\n ),\n _react2.default.createElement(\n \"ul\",\n { className: \"dropdown-menu\" },\n allStages.map(function (stageId) {\n return _react2.default.createElement(\n \"li\",\n { key: stageId },\n _react2.default.createElement(\n \"a\",\n { onClick: function onClick() {\n return _this10.setState({ selectedStageId: stageId });\n } },\n stageId\n )\n );\n })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n stageOperatorGraph\n )\n )\n );\n }\n }]);\n\n return StageDetail;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/StageDetail.jsx?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.StageDetail = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactDom = __webpack_require__(/*! react-dom */ \"./node_modules/react-dom/index.js\");\n\nvar _reactDom2 = _interopRequireDefault(_reactDom);\n\nvar _server = __webpack_require__(/*! react-dom/server */ \"./node_modules/react-dom/server.browser.js\");\n\nvar _server2 = _interopRequireDefault(_server);\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nvar _QueryHeader = __webpack_require__(/*! ./QueryHeader */ \"./components/QueryHeader.jsx\");\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nfunction getTotalWallTime(operator) {\n return (0, _utils.parseDuration)(operator.addInputWall) + (0, _utils.parseDuration)(operator.getOutputWall) + (0, _utils.parseDuration)(operator.finishWall) + (0, _utils.parseDuration)(operator.blockedWall);\n}\n\nvar OperatorSummary = function (_React$Component) {\n _inherits(OperatorSummary, _React$Component);\n\n function OperatorSummary() {\n _classCallCheck(this, OperatorSummary);\n\n return _possibleConstructorReturn(this, (OperatorSummary.__proto__ || Object.getPrototypeOf(OperatorSummary)).apply(this, arguments));\n }\n\n _createClass(OperatorSummary, [{\n key: \"render\",\n value: function render() {\n var operator = this.props.operator;\n\n var totalWallTime = (0, _utils.parseDuration)(operator.addInputWall) + (0, _utils.parseDuration)(operator.getOutputWall) + (0, _utils.parseDuration)(operator.finishWall) + (0, _utils.parseDuration)(operator.blockedWall);\n\n var rowInputRate = totalWallTime === 0 ? 0 : 1.0 * operator.inputPositions / (totalWallTime / 1000.0);\n var byteInputRate = totalWallTime === 0 ? 0 : 1.0 * (0, _utils.parseDataSize)(operator.inputDataSize) / (totalWallTime / 1000.0);\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"highlight-row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"header-row\" },\n operator.operatorType\n ),\n _react2.default.createElement(\n \"div\",\n null,\n (0, _utils.formatCount)(rowInputRate) + \" rows/s (\" + (0, _utils.formatDataSize)(byteInputRate) + \"/s)\"\n )\n ),\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Output\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.outputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.outputDataSize) + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Drivers\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n operator.totalDrivers\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Wall Time\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)(totalWallTime)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)((0, _utils.parseDuration)(operator.blockedWall))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Input\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.inputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.inputDataSize) + \")\"\n )\n )\n )\n )\n );\n }\n }]);\n\n return OperatorSummary;\n}(_react2.default.Component);\n\nvar BAR_CHART_PROPERTIES = {\n type: 'bar',\n barSpacing: '0',\n height: '80px',\n barColor: '#747F96',\n zeroColor: '#8997B3',\n tooltipClassname: 'sparkline-tooltip',\n tooltipFormat: 'Task {{offset:offset}} - {{value}}',\n disableHiddenCheck: true\n};\n\nvar OperatorStatistic = function (_React$Component2) {\n _inherits(OperatorStatistic, _React$Component2);\n\n function OperatorStatistic() {\n _classCallCheck(this, OperatorStatistic);\n\n return _possibleConstructorReturn(this, (OperatorStatistic.__proto__ || Object.getPrototypeOf(OperatorStatistic)).apply(this, arguments));\n }\n\n _createClass(OperatorStatistic, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n var operators = this.props.operators;\n var statistic = operators.map(this.props.supplier);\n var numTasks = operators.length;\n\n var tooltipValueLookups = { 'offset': {} };\n for (var i = 0; i < numTasks; i++) {\n tooltipValueLookups['offset'][i] = \"\" + i;\n }\n\n var stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, { barWidth: 800 / numTasks, tooltipValueLookups: tooltipValueLookups });\n $('#' + this.props.id).sparkline(statistic, $.extend({}, stageBarChartProperties, { numberFormatter: this.props.renderer }));\n }\n }, {\n key: \"render\",\n value: function render() {\n return _react2.default.createElement(\n \"div\",\n { className: \"row operator-statistic\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2 italic-uppercase operator-statistic-title\" },\n this.props.name\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-10\" },\n _react2.default.createElement(\"span\", { className: \"bar-chart\", id: this.props.id })\n )\n );\n }\n }]);\n\n return OperatorStatistic;\n}(_react2.default.Component);\n\nvar OperatorDetail = function (_React$Component3) {\n _inherits(OperatorDetail, _React$Component3);\n\n function OperatorDetail(props) {\n _classCallCheck(this, OperatorDetail);\n\n var _this3 = _possibleConstructorReturn(this, (OperatorDetail.__proto__ || Object.getPrototypeOf(OperatorDetail)).call(this, props));\n\n _this3.state = {\n selectedStatistics: _this3.getInitialStatistics()\n };\n return _this3;\n }\n\n _createClass(OperatorDetail, [{\n key: \"getInitialStatistics\",\n value: function getInitialStatistics() {\n return [{\n name: \"Total Wall Time\",\n id: \"totalWallTime\",\n supplier: getTotalWallTime,\n renderer: _utils.formatDuration\n }, {\n name: \"Input Rows\",\n id: \"inputPositions\",\n supplier: function supplier(operator) {\n return operator.inputPositions;\n },\n renderer: _utils.formatCount\n }, {\n name: \"Input Data Size\",\n id: \"inputDataSize\",\n supplier: function supplier(operator) {\n return (0, _utils.parseDataSize)(operator.inputDataSize);\n },\n renderer: _utils.formatDataSize\n }, {\n name: \"Output Rows\",\n id: \"outputPositions\",\n supplier: function supplier(operator) {\n return operator.outputPositions;\n },\n renderer: _utils.formatCount\n }, {\n name: \"Output Data Size\",\n id: \"outputDataSize\",\n supplier: function supplier(operator) {\n return (0, _utils.parseDataSize)(operator.outputDataSize);\n },\n renderer: _utils.formatDataSize\n }];\n }\n }, {\n key: \"getOperatorTasks\",\n value: function getOperatorTasks() {\n // sort the x-axis\n var tasks = this.props.tasks.sort(function (taskA, taskB) {\n return (0, _utils.getTaskNumber)(taskA.taskStatus.taskId) - (0, _utils.getTaskNumber)(taskB.taskStatus.taskId);\n });\n\n var operatorSummary = this.props.operator;\n\n var operatorTasks = [];\n tasks.forEach(function (task) {\n task.stats.pipelines.forEach(function (pipeline) {\n if (pipeline.pipelineId === operatorSummary.pipelineId) {\n pipeline.operatorSummaries.forEach(function (operator) {\n if (operatorSummary.operatorId === operator.operatorId) {\n operatorTasks.push(operator);\n }\n });\n }\n });\n });\n\n return operatorTasks;\n }\n }, {\n key: \"render\",\n value: function render() {\n var operator = this.props.operator;\n var operatorTasks = this.getOperatorTasks();\n var totalWallTime = getTotalWallTime(operator);\n\n var rowInputRate = totalWallTime === 0 ? 0 : 1.0 * operator.inputPositions / totalWallTime;\n var byteInputRate = totalWallTime === 0 ? 0 : 1.0 * (0, _utils.parseDataSize)(operator.inputDataSize) / (totalWallTime / 1000.0);\n\n var rowOutputRate = totalWallTime === 0 ? 0 : 1.0 * operator.outputPositions / totalWallTime;\n var byteOutputRate = totalWallTime === 0 ? 0 : 1.0 * (0, _utils.parseDataSize)(operator.outputDataSize) / (totalWallTime / 1000.0);\n\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"modal-header\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"close\", \"data-dismiss\": \"modal\", \"aria-label\": \"Close\" },\n _react2.default.createElement(\n \"span\",\n { \"aria-hidden\": \"true\" },\n \"\\xD7\"\n )\n ),\n _react2.default.createElement(\n \"h3\",\n null,\n _react2.default.createElement(\n \"small\",\n null,\n \"Pipeline \",\n operator.pipelineId\n ),\n _react2.default.createElement(\"br\", null),\n operator.operatorType\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Input\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.inputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.inputDataSize) + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Input Rate\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(rowInputRate) + \" rows/s (\" + (0, _utils.formatDataSize)(byteInputRate) + \"/s)\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Output\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(operator.outputPositions) + \" rows (\" + (0, _utils.parseAndFormatDataSize)(operator.outputDataSize) + \")\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Output Rate\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatCount)(rowOutputRate) + \" rows/s (\" + (0, _utils.formatDataSize)(byteOutputRate) + \"/s)\"\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Skipped Splits by Index\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n operator.skippedSplitsByIndex\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"table\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Wall Time\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)(totalWallTime)\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Blocked\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)((0, _utils.parseDuration)(operator.blockedWall))\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Drivers\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n operator.totalDrivers\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Tasks\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n operatorTasks.length\n )\n ),\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n \"Total Index Read Time\"\n ),\n _react2.default.createElement(\n \"td\",\n null,\n (0, _utils.formatDuration)((0, _utils.parseDuration)(operator.indexReadTime))\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"row font-white\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2 italic-uppercase\" },\n _react2.default.createElement(\n \"strong\",\n null,\n \"Statistic\"\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-10 italic-uppercase\" },\n _react2.default.createElement(\n \"strong\",\n null,\n \"Tasks\"\n )\n )\n ),\n this.state.selectedStatistics.map(function (statistic) {\n return _react2.default.createElement(OperatorStatistic, {\n key: statistic.id,\n id: statistic.id,\n name: statistic.name,\n supplier: statistic.supplier,\n renderer: statistic.renderer,\n operators: operatorTasks });\n }.bind(this)),\n _react2.default.createElement(\"p\", null),\n _react2.default.createElement(\"p\", null)\n )\n );\n }\n }]);\n\n return OperatorDetail;\n}(_react2.default.Component);\n\nvar StageOperatorGraph = function (_React$Component4) {\n _inherits(StageOperatorGraph, _React$Component4);\n\n function StageOperatorGraph() {\n _classCallCheck(this, StageOperatorGraph);\n\n return _possibleConstructorReturn(this, (StageOperatorGraph.__proto__ || Object.getPrototypeOf(StageOperatorGraph)).apply(this, arguments));\n }\n\n _createClass(StageOperatorGraph, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.updateD3Graph();\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate() {\n this.updateD3Graph();\n }\n }, {\n key: \"handleOperatorClick\",\n value: function handleOperatorClick(operatorCssId) {\n $('#operator-detail-modal').modal();\n\n var pipelineId = parseInt(operatorCssId.split('-')[1]);\n var operatorId = parseInt(operatorCssId.split('-')[2]);\n var stage = this.props.stage;\n\n var operatorStageSummary = null;\n var operatorSummaries = stage.stageStats.operatorSummaries;\n for (var i = 0; i < operatorSummaries.length; i++) {\n if (operatorSummaries[i].pipelineId === pipelineId && operatorSummaries[i].operatorId === operatorId) {\n operatorStageSummary = operatorSummaries[i];\n }\n }\n\n _reactDom2.default.render(_react2.default.createElement(OperatorDetail, { key: operatorCssId, operator: operatorStageSummary, tasks: stage.tasks }), document.getElementById('operator-detail'));\n }\n }, {\n key: \"computeOperatorGraphs\",\n value: function computeOperatorGraphs(planNode, operatorMap) {\n var _this5 = this;\n\n var sources = (0, _utils.getChildren)(planNode);\n\n var sourceResults = new Map();\n sources.forEach(function (source) {\n var sourceResult = _this5.computeOperatorGraphs(source, operatorMap);\n sourceResult.forEach(function (operator, pipelineId) {\n if (sourceResults.has(pipelineId)) {\n console.error(\"Multiple sources for \", planNode['@type'], \" had the same pipeline ID\");\n return sourceResults;\n }\n sourceResults.set(pipelineId, operator);\n });\n });\n\n var nodeOperators = operatorMap.get(planNode.id);\n if (!nodeOperators || nodeOperators.length === 0) {\n return sourceResults;\n }\n\n var pipelineOperators = new Map();\n nodeOperators.forEach(function (operator) {\n if (!pipelineOperators.has(operator.pipelineId)) {\n pipelineOperators.set(operator.pipelineId, []);\n }\n pipelineOperators.get(operator.pipelineId).push(operator);\n });\n\n var result = new Map();\n pipelineOperators.forEach(function (pipelineOperators, pipelineId) {\n // sort deep-copied operators in this pipeline from source to sink\n var linkedOperators = pipelineOperators.map(function (a) {\n return Object.assign({}, a);\n }).sort(function (a, b) {\n return a.operatorId - b.operatorId;\n });\n var sinkOperator = linkedOperators[linkedOperators.length - 1];\n var sourceOperator = linkedOperators[0];\n\n if (sourceResults.has(pipelineId)) {\n var pipelineChildResult = sourceResults.get(pipelineId);\n if (pipelineChildResult) {\n sourceOperator.child = pipelineChildResult;\n }\n }\n\n // chain operators at this level\n var currentOperator = sourceOperator;\n linkedOperators.slice(1).forEach(function (source) {\n source.child = currentOperator;\n currentOperator = source;\n });\n\n result.set(pipelineId, sinkOperator);\n });\n\n sourceResults.forEach(function (operator, pipelineId) {\n if (!result.has(pipelineId)) {\n result.set(pipelineId, operator);\n }\n });\n\n return result;\n }\n }, {\n key: \"computeOperatorMap\",\n value: function computeOperatorMap() {\n var operatorMap = new Map();\n this.props.stage.stageStats.operatorSummaries.forEach(function (operator) {\n if (!operatorMap.has(operator.planNodeId)) {\n operatorMap.set(operator.planNodeId, []);\n }\n\n operatorMap.get(operator.planNodeId).push(operator);\n });\n\n return operatorMap;\n }\n }, {\n key: \"computeD3StageOperatorGraph\",\n value: function computeD3StageOperatorGraph(graph, operator, sink, pipelineNode) {\n var operatorNodeId = \"operator-\" + operator.pipelineId + \"-\" + operator.operatorId;\n\n // this is a non-standard use of ReactDOMServer, but it's the cleanest way to unify DagreD3 with React\n var html = _server2.default.renderToString(_react2.default.createElement(OperatorSummary, { key: operator.pipelineId + \"-\" + operator.operatorId, operator: operator }));\n graph.setNode(operatorNodeId, { class: \"operator-stats\", label: html, labelType: \"html\" });\n\n if (operator.hasOwnProperty(\"child\")) {\n this.computeD3StageOperatorGraph(graph, operator.child, operatorNodeId, pipelineNode);\n }\n\n if (sink !== null) {\n graph.setEdge(operatorNodeId, sink, { class: \"plan-edge\", arrowheadClass: \"plan-arrowhead\" });\n }\n\n graph.setParent(operatorNodeId, pipelineNode);\n }\n }, {\n key: \"updateD3Graph\",\n value: function updateD3Graph() {\n var _this6 = this;\n\n if (!this.props.stage) {\n return;\n }\n\n var stage = this.props.stage;\n var operatorMap = this.computeOperatorMap();\n var operatorGraphs = this.computeOperatorGraphs(stage.plan.root, operatorMap);\n\n var graph = (0, _utils.initializeGraph)();\n operatorGraphs.forEach(function (operator, pipelineId) {\n var pipelineNodeId = \"pipeline-\" + pipelineId;\n graph.setNode(pipelineNodeId, { label: \"Pipeline \" + pipelineId + \" \", clusterLabelPos: 'top', style: 'fill: #2b2b2b', labelStyle: 'fill: #fff' });\n _this6.computeD3StageOperatorGraph(graph, operator, null, pipelineNodeId);\n });\n\n $(\"#operator-canvas\").html(\"\");\n\n if (operatorGraphs.size > 0) {\n $(\".graph-container\").css(\"display\", \"block\");\n var svg = (0, _utils.initializeSvg)(\"#operator-canvas\");\n var render = new dagreD3.render();\n render(d3.select(\"#operator-canvas g\"), graph);\n\n svg.selectAll(\"g.operator-stats\").on(\"click\", this.handleOperatorClick.bind(this));\n svg.attr(\"height\", graph.graph().height);\n svg.attr(\"width\", graph.graph().width);\n } else {\n $(\".graph-container\").css(\"display\", \"none\");\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n var stage = this.props.stage;\n\n if (!stage.hasOwnProperty('plan')) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Stage does not have a plan\"\n )\n )\n );\n }\n\n if (!stage.hasOwnProperty('stageStats') || !stage.stageStats.hasOwnProperty(\"operatorSummaries\") || stage.stageStats.operatorSummaries.length === 0) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Operator data not available for \",\n stage.stageId\n )\n )\n );\n }\n\n return null;\n }\n }]);\n\n return StageOperatorGraph;\n}(_react2.default.Component);\n\nvar StageDetail = exports.StageDetail = function (_React$Component5) {\n _inherits(StageDetail, _React$Component5);\n\n function StageDetail(props) {\n _classCallCheck(this, StageDetail);\n\n var _this7 = _possibleConstructorReturn(this, (StageDetail.__proto__ || Object.getPrototypeOf(StageDetail)).call(this, props));\n\n _this7.state = {\n initialized: false,\n ended: false,\n\n selectedStageId: null,\n query: null,\n\n lastRefresh: null,\n lastRender: null\n };\n\n _this7.refreshLoop = _this7.refreshLoop.bind(_this7);\n return _this7;\n }\n\n _createClass(StageDetail, [{\n key: \"resetTimer\",\n value: function resetTimer() {\n clearTimeout(this.timeoutId);\n // stop refreshing when query finishes or fails\n if (this.state.query === null || !this.state.ended) {\n this.timeoutId = setTimeout(this.refreshLoop, 1000);\n }\n }\n }, {\n key: \"refreshLoop\",\n value: function refreshLoop() {\n var _this8 = this;\n\n clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously\n var queryString = (0, _utils.getFirstParameter)(window.location.search).split('.');\n var queryId = queryString[0];\n\n var selectedStageId = this.state.selectedStageId;\n if (selectedStageId === null) {\n selectedStageId = 0;\n if (queryString.length > 1) {\n selectedStageId = parseInt(queryString[1]);\n }\n }\n\n $.get('/ui/api/query/' + queryId, function (query) {\n _this8.setState({\n initialized: true,\n ended: query.finalQueryInfo,\n\n selectedStageId: selectedStageId,\n query: query\n });\n _this8.resetTimer();\n }).fail(function () {\n _this8.setState({\n initialized: true\n });\n _this8.resetTimer();\n });\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.refreshLoop();\n new window.ClipboardJS('.copy-button');\n }\n }, {\n key: \"findStage\",\n value: function findStage(stageId, currentStage) {\n if (stageId === null) {\n return null;\n }\n\n if (currentStage.stageId === stageId) {\n return currentStage;\n }\n\n for (var i = 0; i < currentStage.subStages.length; i++) {\n var stage = this.findStage(stageId, currentStage.subStages[i]);\n if (stage !== null) {\n return stage;\n }\n }\n\n return null;\n }\n }, {\n key: \"getAllStageIds\",\n value: function getAllStageIds(result, currentStage) {\n var _this9 = this;\n\n result.push(currentStage.plan.id);\n currentStage.subStages.forEach(function (stage) {\n _this9.getAllStageIds(result, stage);\n });\n }\n }, {\n key: \"render\",\n value: function render() {\n var _this10 = this;\n\n if (!this.state.query) {\n var label = _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n );\n if (this.state.initialized) {\n label = \"Query not found\";\n }\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n label\n )\n )\n );\n }\n\n if (!this.state.query.outputStage) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Query does not have an output stage\"\n )\n )\n );\n }\n\n var query = this.state.query;\n var allStages = [];\n this.getAllStageIds(allStages, query.outputStage);\n\n var stage = this.findStage(query.queryId + \".\" + this.state.selectedStageId, query.outputStage);\n if (stage === null) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Stage not found\"\n )\n )\n );\n }\n\n var stageOperatorGraph = null;\n if (!(0, _utils.isQueryEnded)(query)) {\n stageOperatorGraph = _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Operator graph will appear automatically when query completes.\"\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n )\n )\n );\n } else {\n stageOperatorGraph = _react2.default.createElement(StageOperatorGraph, { id: stage.stageId, stage: stage });\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(_QueryHeader.QueryHeader, { query: query }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2\" },\n _react2.default.createElement(\n \"h3\",\n null,\n \"Stage \",\n stage.plan.id\n )\n ),\n _react2.default.createElement(\"div\", { className: \"col-xs-8\" }),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-2 stage-dropdown\" },\n _react2.default.createElement(\n \"div\",\n { className: \"input-group-btn\" },\n _react2.default.createElement(\n \"button\",\n { type: \"button\", className: \"btn btn-default dropdown-toggle\", \"data-toggle\": \"dropdown\", \"aria-haspopup\": \"true\", \"aria-expanded\": \"false\" },\n \"Select Stage \",\n _react2.default.createElement(\"span\", { className: \"caret\" })\n ),\n _react2.default.createElement(\n \"ul\",\n { className: \"dropdown-menu\" },\n allStages.map(function (stageId) {\n return _react2.default.createElement(\n \"li\",\n { key: stageId },\n _react2.default.createElement(\n \"a\",\n { onClick: function onClick() {\n return _this10.setState({ selectedStageId: stageId });\n } },\n stageId\n )\n );\n })\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\"hr\", { className: \"h3-hr\" }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n stageOperatorGraph\n )\n )\n );\n }\n }]);\n\n return StageDetail;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/StageDetail.jsx?"); /***/ }), @@ -20664,7 +20664,7 @@ eval("\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/i /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }) diff --git a/core/trino-main/src/main/resources/webapp/dist/worker.js b/core/trino-main/src/main/resources/webapp/dist/worker.js index 01234bc6d29c..222ba133119d 100644 --- a/core/trino-main/src/main/resources/webapp/dist/worker.js +++ b/core/trino-main/src/main/resources/webapp/dist/worker.js @@ -20616,7 +20616,7 @@ eval("module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\ /***/ (function(module, exports, __webpack_require__) { "use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.GLYPHICON_HIGHLIGHT = exports.GLYPHICON_DEFAULT = undefined;\nexports.getQueryStateColor = getQueryStateColor;\nexports.getStageStateColor = getStageStateColor;\nexports.getHumanReadableState = getHumanReadableState;\nexports.getProgressBarPercentage = getProgressBarPercentage;\nexports.getProgressBarTitle = getProgressBarTitle;\nexports.isQueryEnded = isQueryEnded;\nexports.addToHistory = addToHistory;\nexports.addExponentiallyWeightedToHistory = addExponentiallyWeightedToHistory;\nexports.initializeGraph = initializeGraph;\nexports.initializeSvg = initializeSvg;\nexports.getChildren = getChildren;\nexports.truncateString = truncateString;\nexports.getStageNumber = getStageNumber;\nexports.getTaskIdSuffix = getTaskIdSuffix;\nexports.getTaskNumber = getTaskNumber;\nexports.getFirstParameter = getFirstParameter;\nexports.getHostname = getHostname;\nexports.getPort = getPort;\nexports.getHostAndPort = getHostAndPort;\nexports.computeRate = computeRate;\nexports.computeAvgDuration = computeAvgDuration;\nexports.precisionRound = precisionRound;\nexports.formatDuration = formatDuration;\nexports.formatRows = formatRows;\nexports.formatCount = formatCount;\nexports.formatDataSizeBytes = formatDataSizeBytes;\nexports.formatDataSize = formatDataSize;\nexports.parseDataSize = parseDataSize;\nexports.parseAndFormatDataSize = parseAndFormatDataSize;\nexports.parseDuration = parseDuration;\nexports.formatShortTime = formatShortTime;\nexports.formatShortDateTime = formatShortDateTime;\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\n// Query display\n// =============\n\n/*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar GLYPHICON_DEFAULT = exports.GLYPHICON_DEFAULT = { color: '#1edcff' };\nvar GLYPHICON_HIGHLIGHT = exports.GLYPHICON_HIGHLIGHT = { color: '#999999' };\n\nvar EMDASH = \"\\u2014\";\n\nvar STATE_COLOR_MAP = {\n QUEUED: '#1b8f72',\n RUNNING: '#19874e',\n PLANNING: '#674f98',\n FINISHED: '#1a4629',\n BLOCKED: '#61003b',\n USER_ERROR: '#9a7d66',\n CANCELED: '#858959',\n INSUFFICIENT_RESOURCES: '#7f5b72',\n EXTERNAL_ERROR: '#ca7640',\n UNKNOWN_ERROR: '#943524'\n};\n\nfunction getQueryStateColor(query) {\n switch (query.state) {\n case \"QUEUED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"PLANNING\":\n return STATE_COLOR_MAP.PLANNING;\n case \"STARTING\":\n case \"FINISHING\":\n case \"RUNNING\":\n if (query.queryStats && query.queryStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FAILED\":\n switch (query.errorType) {\n case \"USER_ERROR\":\n if (query.errorCode.name === 'USER_CANCELED') {\n return STATE_COLOR_MAP.CANCELED;\n }\n return STATE_COLOR_MAP.USER_ERROR;\n case \"EXTERNAL\":\n return STATE_COLOR_MAP.EXTERNAL_ERROR;\n case \"INSUFFICIENT_RESOURCES\":\n return STATE_COLOR_MAP.INSUFFICIENT_RESOURCES;\n default:\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n }\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n default:\n return STATE_COLOR_MAP.QUEUED;\n }\n}\n\nfunction getStageStateColor(stage) {\n switch (stage.state) {\n case \"PLANNED\":\n return STATE_COLOR_MAP.QUEUED;\n case \"SCHEDULING\":\n case \"SCHEDULING_SPLITS\":\n case \"SCHEDULED\":\n return STATE_COLOR_MAP.PLANNING;\n case \"RUNNING\":\n if (stage.stageStats && stage.stageStats.fullyBlocked) {\n return STATE_COLOR_MAP.BLOCKED;\n }\n return STATE_COLOR_MAP.RUNNING;\n case \"FINISHED\":\n return STATE_COLOR_MAP.FINISHED;\n case \"CANCELED\":\n case \"ABORTED\":\n return STATE_COLOR_MAP.CANCELED;\n case \"FAILED\":\n return STATE_COLOR_MAP.UNKNOWN_ERROR;\n default:\n return \"#b5b5b5\";\n }\n}\n\n// This relies on the fact that BasicQueryInfo and QueryInfo have all the fields\n// necessary to compute this string, and that these fields are consistently named.\nfunction getHumanReadableState(query, forOverviewPage) {\n if (query.state === \"RUNNING\") {\n var title = \"RUNNING\";\n\n if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) {\n if (query.queryStats.fullyBlocked) {\n title = \"BLOCKED\";\n\n if (query.queryStats.blockedReasons && query.queryStats.blockedReasons.length > 0) {\n title += \" (\" + query.queryStats.blockedReasons.join(\", \") + \")\";\n }\n }\n\n if (query.memoryPool === \"reserved\") {\n title += \" (RESERVED)\";\n }\n\n return title;\n }\n }\n\n if (query.state === \"FAILED\") {\n var errorMsg = \"\";\n switch (query.errorType) {\n case \"USER_ERROR\":\n errorMsg = \"USER ERROR\";\n if (query.errorCode.name === \"USER_CANCELED\") {\n errorMsg = \"USER CANCELED\";\n }\n case \"INTERNAL_ERROR\":\n errorMsg = \"INTERNAL ERROR\";\n case \"INSUFFICIENT_RESOURCES\":\n errorMsg = \"INSUFFICIENT RESOURCES\";\n case \"EXTERNAL\":\n errorMsg = \"EXTERNAL ERROR\";\n }\n if (forOverviewPage && query.errorCode && query.errorCode.name) {\n errorMsg += \" \" + EMDASH + \" \" + query.errorCode.name;\n }\n return errorMsg;\n }\n\n return query.state;\n}\n\nfunction getProgressBarPercentage(query) {\n var progress = query.queryStats.progressPercentage;\n\n // progress bars should appear 'full' when query progress is not meaningful\n if (!progress || query.state !== \"RUNNING\") {\n return 100;\n }\n\n return Math.round(progress);\n}\n\nfunction getProgressBarTitle(query, forOverviewPage) {\n if (query.queryStats.progressPercentage && query.state === \"RUNNING\") {\n return getHumanReadableState(query, forOverviewPage) + \" (\" + getProgressBarPercentage(query) + \"%)\";\n }\n\n return getHumanReadableState(query, forOverviewPage);\n}\n\nfunction isQueryEnded(query) {\n return [\"FINISHED\", \"FAILED\", \"CANCELED\"].indexOf(query.state) > -1;\n}\n\n// Sparkline-related functions\n// ===========================\n\n// display at most 5 minutes worth of data on the sparklines\nvar MAX_HISTORY = 60 * 5;\n// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness\nvar MOVING_AVERAGE_ALPHA = 0.2;\n\nfunction addToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\nfunction addExponentiallyWeightedToHistory(value, valuesArray) {\n if (valuesArray.length === 0) {\n return valuesArray.concat([value]);\n }\n\n var movingAverage = value * MOVING_AVERAGE_ALPHA + valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA);\n if (value < 1) {\n movingAverage = 0;\n }\n\n return valuesArray.concat([movingAverage]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0));\n}\n\n// DagreD3 Graph-related functions\n// ===============================\n\nfunction initializeGraph() {\n return new dagreD3.graphlib.Graph({ compound: true }).setGraph({ rankdir: 'BT' }).setDefaultEdgeLabel(function () {\n return {};\n });\n}\n\nfunction initializeSvg(selector) {\n var svg = d3.select(selector);\n svg.append(\"g\");\n\n return svg;\n}\n\nfunction getChildren(nodeInfo) {\n // TODO: Remove this function by migrating StageDetail to use node JSON representation\n switch (nodeInfo['@type']) {\n case 'output':\n case 'explainAnalyze':\n case 'project':\n case 'filter':\n case 'aggregation':\n case 'sort':\n case 'markDistinct':\n case 'window':\n case 'rowNumber':\n case 'topnRanking':\n case 'limit':\n case 'distinctlimit':\n case 'topn':\n case 'sample':\n case 'tablewriter':\n case 'delete':\n case 'tableDelete':\n case 'tablecommit':\n case 'groupid':\n case 'unnest':\n case 'scalar':\n return [nodeInfo.source];\n case 'join':\n return [nodeInfo.left, nodeInfo.right];\n case 'semijoin':\n return [nodeInfo.source, nodeInfo.filteringSource];\n case 'spatialjoin':\n return [nodeInfo.left, nodeInfo.right];\n case 'indexjoin':\n return [nodeInfo.probeSource, nodeInfo.indexSource];\n case 'union':\n case 'exchange':\n return nodeInfo.sources;\n case 'remoteSource':\n case 'tablescan':\n case 'values':\n case 'indexsource':\n break;\n default:\n console.log(\"NOTE: Unhandled PlanNode: \" + nodeInfo['@type']);\n }\n\n return [];\n}\n\n// Utility functions\n// =================\n\nfunction truncateString(inputString, length) {\n if (inputString && inputString.length > length) {\n return inputString.substring(0, length) + \"...\";\n }\n\n return inputString;\n}\n\nfunction getStageNumber(stageId) {\n return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length));\n}\n\nfunction getTaskIdSuffix(taskId) {\n return taskId.slice(taskId.indexOf('.') + 1, taskId.length);\n}\n\nfunction getTaskNumber(taskId) {\n return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId)));\n}\n\nfunction getFirstParameter(searchString) {\n var searchText = searchString.substring(1);\n\n if (searchText.indexOf('&') !== -1) {\n return searchText.substring(0, searchText.indexOf('&'));\n }\n\n return searchText;\n}\n\nfunction getHostname(url) {\n var hostname = new URL(url).hostname;\n if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {\n hostname = hostname.substr(1, hostname.length - 2);\n }\n return hostname;\n}\n\nfunction getPort(url) {\n return new URL(url).port;\n}\n\nfunction getHostAndPort(urlStr) {\n var url = new URL(urlStr);\n return url.hostname + \":\" + url.port;\n}\n\nfunction computeRate(count, ms) {\n if (ms === 0) {\n return 0;\n }\n return count / ms * 1000.0;\n}\n\nfunction computeAvgDuration(duration, count) {\n if (count === 0) {\n return formatDuration(0);\n }\n var number = parseDuration(duration);\n if (number === null) {\n return null;\n }\n return formatDuration(number / count);\n}\n\nfunction precisionRound(n) {\n if (n < 10) {\n return n.toFixed(2);\n }\n if (n < 100) {\n return n.toFixed(1);\n }\n return Math.round(n).toString();\n}\n\nfunction formatDuration(duration) {\n var unit = \"ms\";\n if (duration > 1000) {\n duration /= 1000;\n unit = \"s\";\n }\n if (unit === \"s\" && duration > 60) {\n duration /= 60;\n unit = \"m\";\n }\n if (unit === \"m\" && duration > 60) {\n duration /= 60;\n unit = \"h\";\n }\n if (unit === \"h\" && duration > 24) {\n duration /= 24;\n unit = \"d\";\n }\n if (unit === \"d\" && duration > 7) {\n duration /= 7;\n unit = \"w\";\n }\n return precisionRound(duration) + unit;\n}\n\nfunction formatRows(count) {\n if (count === 1) {\n return \"1 row\";\n }\n\n return formatCount(count) + \" rows\";\n}\n\nfunction formatCount(count) {\n var unit = \"\";\n if (count > 1000) {\n count /= 1000;\n unit = \"K\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"M\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"B\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"T\";\n }\n if (count > 1000) {\n count /= 1000;\n unit = \"Q\";\n }\n return precisionRound(count) + unit;\n}\n\nfunction formatDataSizeBytes(size) {\n return formatDataSizeMinUnit(size, \"\");\n}\n\nfunction formatDataSize(size) {\n return formatDataSizeMinUnit(size, \"B\");\n}\n\nfunction formatDataSizeMinUnit(size, minUnit) {\n var unit = minUnit;\n if (size === 0) {\n return \"0\" + unit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"K\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"M\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"G\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"T\" + minUnit;\n }\n if (size >= 1024) {\n size /= 1024;\n unit = \"P\" + minUnit;\n }\n return precisionRound(size) + unit;\n}\n\nfunction parseDataSize(value) {\n var DATA_SIZE_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n var match = DATA_SIZE_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"B\":\n return number;\n case \"kB\":\n return number * Math.pow(2, 10);\n case \"MB\":\n return number * Math.pow(2, 20);\n case \"GB\":\n return number * Math.pow(2, 30);\n case \"TB\":\n return number * Math.pow(2, 40);\n case \"PB\":\n return number * Math.pow(2, 50);\n default:\n return null;\n }\n}\n\nfunction parseAndFormatDataSize(value) {\n var parsed = parseDataSize(value);\n\n if (parsed == null) {\n return \"\";\n }\n\n return formatDataSize(parsed);\n}\n\nfunction parseDuration(value) {\n var DURATION_PATTERN = /^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$/;\n\n var match = DURATION_PATTERN.exec(value);\n if (match === null) {\n return null;\n }\n var number = parseFloat(match[1]);\n switch (match[2]) {\n case \"ns\":\n return number / 1000000.0;\n case \"us\":\n return number / 1000.0;\n case \"ms\":\n return number;\n case \"s\":\n return number * 1000;\n case \"m\":\n return number * 1000 * 60;\n case \"h\":\n return number * 1000 * 60 * 60;\n case \"d\":\n return number * 1000 * 60 * 60 * 24;\n default:\n return null;\n }\n}\n\nfunction formatShortTime(date) {\n var hours = date.getHours() % 12 || 12;\n var minutes = (date.getMinutes() < 10 ? \"0\" : \"\") + date.getMinutes();\n return hours + \":\" + minutes + (date.getHours() >= 12 ? \"pm\" : \"am\");\n}\n\nfunction formatShortDateTime(date) {\n var year = date.getFullYear();\n var month = \"\" + (date.getMonth() + 1);\n var dayOfMonth = \"\" + date.getDate();\n return year + \"-\" + (month[1] ? month : \"0\" + month[0]) + \"-\" + (dayOfMonth[1] ? dayOfMonth : \"0\" + dayOfMonth[0]) + \" \" + formatShortTime(date);\n}\n\n//# sourceURL=webpack:///./utils.js?"); /***/ }), diff --git a/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx b/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx index 71f366aa5ce8..1518456fdc2e 100644 --- a/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx +++ b/core/trino-main/src/main/resources/webapp/src/components/QueryDetail.jsx @@ -16,7 +16,7 @@ import React from "react"; import Reactable from "reactable"; import { - addToHistory, + addToHistory, computeAvgDuration, computeRate, formatCount, formatDataSize, @@ -154,6 +154,12 @@ class TaskList extends React.Component { {task.stats.completedDrivers} + + {task.stats.skippedSplitsByIndex} + + + {computeAvgDuration(task.stats.totalIndexReadTime, task.stats.completedDrivers)} + {task.stats.elapsedTime} @@ -180,6 +186,8 @@ class TaskList extends React.Component { 'splitsRunning', 'splitsBlocked', 'splitsDone', + 'splitsSkippedByIndex', + 'avgIndexReadTime', 'rows', 'rowsSec', 'bytes', @@ -201,6 +209,10 @@ class TaskList extends React.Component { title="Blocked splits"/> + + Rows Rows/s Bytes diff --git a/core/trino-main/src/main/resources/webapp/src/components/StageDetail.jsx b/core/trino-main/src/main/resources/webapp/src/components/StageDetail.jsx index 543bbf8dd7ff..dbd942f775bf 100644 --- a/core/trino-main/src/main/resources/webapp/src/components/StageDetail.jsx +++ b/core/trino-main/src/main/resources/webapp/src/components/StageDetail.jsx @@ -271,6 +271,14 @@ class OperatorDetail extends React.Component { {formatCount(rowOutputRate) + " rows/s (" + formatDataSize(byteOutputRate) + "/s)"} + + + Skipped Splits by Index + + + {operator.skippedSplitsByIndex} + + @@ -309,6 +317,14 @@ class OperatorDetail extends React.Component { {operatorTasks.length} + + + Total Index Read Time + + + {formatDuration(parseDuration(operator.indexReadTime))} + + diff --git a/core/trino-main/src/main/resources/webapp/src/utils.js b/core/trino-main/src/main/resources/webapp/src/utils.js index 1229b68c60cc..97f0e50351cd 100644 --- a/core/trino-main/src/main/resources/webapp/src/utils.js +++ b/core/trino-main/src/main/resources/webapp/src/utils.js @@ -324,6 +324,17 @@ export function computeRate(count: number, ms: number): number { return (count / ms) * 1000.0; } +export function computeAvgDuration(duration: string, count: number): string { + if (count === 0) { + return formatDuration(0) + } + const number = parseDuration(duration) + if (number === null) { + return null + } + return formatDuration(number / count) +} + export function precisionRound(n: number): string { if (n < 10) { return n.toFixed(2); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestQueryStats.java b/core/trino-main/src/test/java/io/trino/execution/TestQueryStats.java index 688f4524a1f7..718017ebfeb8 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestQueryStats.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestQueryStats.java @@ -32,6 +32,7 @@ import static io.airlift.units.DataSize.succinctBytes; import static io.trino.server.DynamicFilterService.DynamicFiltersStats; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; @@ -46,6 +47,8 @@ public class TestQueryStats new PlanNodeId("13"), TableWriterOperator.class.getSimpleName(), 14L, + 183L, + new Duration(184, MILLISECONDS), 15L, new Duration(16, NANOSECONDS), new Duration(17, NANOSECONDS), @@ -87,6 +90,8 @@ public class TestQueryStats new PlanNodeId("23"), FilterAndProjectOperator.class.getSimpleName(), 24L, + 283L, + new Duration(284, MILLISECONDS), 25L, new Duration(26, NANOSECONDS), new Duration(27, NANOSECONDS), @@ -128,6 +133,8 @@ public class TestQueryStats new PlanNodeId("33"), TableWriterOperator.class.getSimpleName(), 34L, + 383L, + new Duration(384, MILLISECONDS), 35L, new Duration(36, NANOSECONDS), new Duration(37, NANOSECONDS), diff --git a/core/trino-main/src/test/java/io/trino/operator/TestDriverStats.java b/core/trino-main/src/test/java/io/trino/operator/TestDriverStats.java index 2800edc43606..a781c22e8fa3 100644 --- a/core/trino-main/src/test/java/io/trino/operator/TestDriverStats.java +++ b/core/trino-main/src/test/java/io/trino/operator/TestDriverStats.java @@ -23,6 +23,7 @@ import org.testng.annotations.Test; import static io.trino.operator.TestOperatorStats.assertExpectedOperatorStats; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; @@ -49,6 +50,9 @@ public class TestDriverStats false, ImmutableSet.of(), + 143, + new Duration(144, MILLISECONDS), + DataSize.ofBytes(131), 141, new Duration(151, NANOSECONDS), @@ -100,6 +104,9 @@ public static void assertExpectedDriverStats(DriverStats actual) assertEquals(actual.getTotalCpuTime(), new Duration(10, NANOSECONDS)); assertEquals(actual.getTotalBlockedTime(), new Duration(12, NANOSECONDS)); + assertEquals(actual.getSkippedSplitsByIndex(), 143); + assertEquals(actual.getIndexReadTime(), new Duration(144, MILLISECONDS)); + assertEquals(actual.getPhysicalInputDataSize(), DataSize.ofBytes(131)); assertEquals(actual.getPhysicalInputPositions(), 141); assertEquals(actual.getPhysicalInputReadTime(), new Duration(151, NANOSECONDS)); diff --git a/core/trino-main/src/test/java/io/trino/operator/TestOperatorStats.java b/core/trino-main/src/test/java/io/trino/operator/TestOperatorStats.java index bd48778a6802..2f22ff678b71 100644 --- a/core/trino-main/src/test/java/io/trino/operator/TestOperatorStats.java +++ b/core/trino-main/src/test/java/io/trino/operator/TestOperatorStats.java @@ -27,6 +27,7 @@ import java.util.Optional; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; @@ -44,6 +45,8 @@ public class TestOperatorStats "test", 1, + 53, + new Duration(54, MILLISECONDS), 2, new Duration(3, NANOSECONDS), @@ -93,6 +96,8 @@ public class TestOperatorStats "test", 1, + 53, + new Duration(54, MILLISECONDS), 2, new Duration(3, NANOSECONDS), @@ -152,6 +157,8 @@ public static void assertExpectedOperatorStats(OperatorStats actual) assertEquals(actual.getOperatorType(), "test"); assertEquals(actual.getTotalDrivers(), 1); + assertEquals(actual.getSkippedSplitsByIndex(), 53); + assertEquals(actual.getIndexReadTime(), new Duration(54, MILLISECONDS)); assertEquals(actual.getAddInputCalls(), 2); assertEquals(actual.getAddInputWall(), new Duration(3, NANOSECONDS)); assertEquals(actual.getAddInputCpu(), new Duration(4, NANOSECONDS)); @@ -204,6 +211,8 @@ public void testAdd() assertEquals(actual.getOperatorType(), "test"); assertEquals(actual.getTotalDrivers(), 3 * 1); + assertEquals(actual.getSkippedSplitsByIndex(), 3 * 53); + assertEquals(actual.getIndexReadTime(), new Duration(3 * 54, MILLISECONDS)); assertEquals(actual.getAddInputCalls(), 3 * 2); assertEquals(actual.getAddInputWall(), new Duration(3 * 3, NANOSECONDS)); assertEquals(actual.getAddInputCpu(), new Duration(3 * 4, NANOSECONDS)); @@ -254,6 +263,8 @@ public void testAddMergeable() assertEquals(actual.getOperatorType(), "test"); assertEquals(actual.getTotalDrivers(), 3 * 1); + assertEquals(actual.getSkippedSplitsByIndex(), 3 * 53); + assertEquals(actual.getIndexReadTime(), new Duration(3 * 54, MILLISECONDS)); assertEquals(actual.getAddInputCalls(), 3 * 2); assertEquals(actual.getAddInputWall(), new Duration(3 * 3, NANOSECONDS)); assertEquals(actual.getAddInputCpu(), new Duration(3 * 4, NANOSECONDS)); diff --git a/core/trino-main/src/test/java/io/trino/operator/TestPipelineStats.java b/core/trino-main/src/test/java/io/trino/operator/TestPipelineStats.java index d2e4c900bca7..34975427c82e 100644 --- a/core/trino-main/src/test/java/io/trino/operator/TestPipelineStats.java +++ b/core/trino-main/src/test/java/io/trino/operator/TestPipelineStats.java @@ -51,6 +51,9 @@ public class TestPipelineStats 19, 4, + 143, + getTestDistribution(144), + DataSize.ofBytes(5), DataSize.ofBytes(6), DataSize.ofBytes(7), @@ -114,6 +117,9 @@ public static void assertExpectedPipelineStats(PipelineStats actual) assertEquals(actual.getBlockedDrivers(), 19); assertEquals(actual.getCompletedDrivers(), 4); + assertEquals(actual.getSkippedSplitsByIndex(), 143); + assertEquals(actual.getIndexReadTime().getCount(), 144.0); + assertEquals(actual.getUserMemoryReservation(), DataSize.ofBytes(5)); assertEquals(actual.getRevocableMemoryReservation(), DataSize.ofBytes(6)); assertEquals(actual.getSystemMemoryReservation(), DataSize.ofBytes(7)); diff --git a/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java b/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java index 59184d013401..7c0b8763fad6 100644 --- a/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java +++ b/core/trino-main/src/test/java/io/trino/operator/TestTaskStats.java @@ -22,6 +22,7 @@ import org.testng.annotations.Test; import static io.trino.operator.TestPipelineStats.assertExpectedPipelineStats; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; @@ -47,6 +48,9 @@ public class TestTaskStats 24, 10, + 193, + new Duration(194, MILLISECONDS), + 11.0, 5.0, DataSize.ofBytes(12), @@ -112,6 +116,9 @@ public static void assertExpectedTaskStats(TaskStats actual) assertEquals(actual.getBlockedDrivers(), 24); assertEquals(actual.getCompletedDrivers(), 10); + assertEquals(actual.getSkippedSplitsByIndex(), 193); + assertEquals(actual.getTotalIndexReadTime(), new Duration(194, MILLISECONDS)); + assertEquals(actual.getCumulativeUserMemory(), 11.0); assertEquals(actual.getCumulativeSystemMemory(), 5.0); assertEquals(actual.getUserMemoryReservation(), DataSize.ofBytes(12)); diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorSplit.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorSplit.java index 0b5fa396eb99..28da8b73c9ef 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorSplit.java +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorSplit.java @@ -30,4 +30,14 @@ default SplitWeight getSplitWeight() { return SplitWeight.standard(); } + + default boolean isSkippedByIndex() + { + return false; + } + + default long getIndexReadTime() + { + return 0; + } } diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergConfig.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergConfig.java index aa92a94c7eea..30ab3aaac30e 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergConfig.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergConfig.java @@ -38,7 +38,7 @@ public class IcebergConfig private Duration dynamicFilteringWaitTimeout = new Duration(0, SECONDS); private boolean tableStatisticsEnabled = true; private boolean projectionPushdownEnabled = true; - private boolean readIndicesSwitchOn; + private boolean readIndicesSwitchOn = true; public CatalogType getCatalogType() { diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java index 17f4aba9e47b..7dfd2e2ff15b 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSourceProvider.java @@ -190,11 +190,13 @@ public ConnectorPageSource createPageSource( if (fileScanTask != null) { long start = System.currentTimeMillis(); HdfsFileIo hdfsFileIo = new HdfsFileIo(hdfsEnvironment, hdfsContext); - if (!fileScanTask.isRequired(hdfsFileIo, false)) { - log.debug("Indices hit for file : %s, split skipped, time spent : %s ms", fileScanTask.file().path(), System.currentTimeMillis() - start); + split.setIsSkippedByIndex(!fileScanTask.isRequired(hdfsFileIo, false)); + split.setIndexReadTime(System.currentTimeMillis() - start); + if (split.isSkippedByIndex()) { + log.info("Indices hit for file : %s, split skipped, time spent : %s ms", fileScanTask.file().path(), split.getIndexReadTime()); return new EmptyPageSource(); } - log.debug("Indices missed for file : %s, time spent : %s ms", fileScanTask.file().path(), System.currentTimeMillis() - start); + log.info("Indices missed for file : %s, time spent : %s ms", fileScanTask.file().path(), split.getIndexReadTime()); } List icebergColumns = columns.stream() diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplit.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplit.java index 9ed6a43830dd..e20f3f337c17 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplit.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplit.java @@ -47,6 +47,8 @@ public class IcebergSplit private final Map> partitionKeys; private final String fileScanTaskEncode; private FileScanTask fileScanTask; + private boolean isSkippedByIndex; + private long indexReadTime; @JsonCreator public IcebergSplit( @@ -142,6 +144,28 @@ public FileScanTask decodeFileScanTask() return fileScanTask; } + public void setIsSkippedByIndex(boolean isSkippedByIndex) + { + this.isSkippedByIndex = isSkippedByIndex; + } + + public void setIndexReadTime(long indexReadTime) + { + this.indexReadTime = indexReadTime; + } + + @Override + public boolean isSkippedByIndex() + { + return isSkippedByIndex; + } + + @Override + public long getIndexReadTime() + { + return indexReadTime; + } + @Override public Object getInfo() { diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergConfig.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergConfig.java index 38b6f52e26b7..b6abc0586f81 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergConfig.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergConfig.java @@ -45,7 +45,7 @@ public void testDefaults() .setDynamicFilteringWaitTimeout(new Duration(0, MINUTES)) .setTableStatisticsEnabled(true) .setProjectionPushdownEnabled(true) - .setReadIndicesSwitchOn(false)); + .setReadIndicesSwitchOn(true)); } @Test @@ -61,7 +61,7 @@ public void testExplicitPropertyMappings() .put("iceberg.dynamic-filtering.wait-timeout", "1h") .put("iceberg.table-statistics-enabled", "false") .put("iceberg.projection-pushdown-enabled", "false") - .put("iceberg.read-indices-switch-on", "true") + .put("iceberg.read-indices-switch-on", "false") .build(); IcebergConfig expected = new IcebergConfig() @@ -74,7 +74,7 @@ public void testExplicitPropertyMappings() .setDynamicFilteringWaitTimeout(Duration.valueOf("1h")) .setTableStatisticsEnabled(false) .setProjectionPushdownEnabled(false) - .setReadIndicesSwitchOn(true); + .setReadIndicesSwitchOn(false); assertFullMapping(properties, expected); }