From 4aa8a97ef59b6e46d56e0ff0667e0d584672b87f Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 5 Jan 2016 15:28:20 +0100 Subject: [PATCH] Add support for queryPlan to QueryStatistics --- .../google/gcloud/bigquery/JobStatistics.java | 33 +- .../google/gcloud/bigquery/QueryStage.java | 431 ++++++++++++++++++ .../gcloud/bigquery/ITBigQueryTest.java | 2 + .../gcloud/bigquery/JobStatisticsTest.java | 26 ++ .../gcloud/bigquery/QueryStageTest.java | 126 +++++ 5 files changed, 616 insertions(+), 2 deletions(-) create mode 100644 gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryStage.java create mode 100644 gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryStageTest.java diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java index 8322a887a4a0..cd6f3c8e71df 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java +++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/JobStatistics.java @@ -5,6 +5,7 @@ import com.google.api.services.bigquery.model.JobStatistics4; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.common.collect.Lists; import java.io.Serializable; import java.util.List; @@ -242,6 +243,7 @@ public static class QueryStatistics extends JobStatistics { private final Boolean cacheHit; private final Long totalBytesBilled; private final Long totalBytesProcessed; + private final List queryPlan; static final class Builder extends JobStatistics.Builder { @@ -249,6 +251,7 @@ static final class Builder extends JobStatistics.Builder queryPlan; private Builder() {} @@ -258,6 +261,10 @@ private Builder(com.google.api.services.bigquery.model.JobStatistics statisticsP this.cacheHit = statisticsPb.getQuery().getCacheHit(); this.totalBytesBilled = statisticsPb.getQuery().getTotalBytesBilled(); this.totalBytesProcessed = statisticsPb.getQuery().getTotalBytesProcessed(); + if (statisticsPb.getQuery().getQueryPlan() != null) { + this.queryPlan = + Lists.transform(statisticsPb.getQuery().getQueryPlan(), QueryStage.FROM_PB_FUNCTION); + } } Builder billingTier(Integer billingTier) { @@ -280,6 +287,11 @@ Builder totalBytesProcessed(Long totalBytesProcessed) { return self(); } + Builder queryPlan(List queryPlan) { + this.queryPlan = queryPlan; + return self(); + } + @Override QueryStatistics build() { return new QueryStatistics(this); @@ -292,6 +304,7 @@ private QueryStatistics(Builder builder) { this.cacheHit = builder.cacheHit; this.totalBytesBilled = builder.totalBytesBilled; this.totalBytesProcessed = builder.totalBytesProcessed; + this.queryPlan = builder.queryPlan; } /** @@ -325,13 +338,26 @@ public Long totalBytesProcessed() { return totalBytesProcessed; } + /** + * Returns the query plan as a list of stages. Each stage involves a number of steps that read + * from data sources, perform a series of transformations on the input, and emit an output to a + * future stage (or the final result). The query plan is available for a completed query job and + * is retained for 7 days. + * + * @see Query Plan + */ + public List queryPlan() { + return queryPlan; + } + @Override ToStringHelper toStringHelper() { return super.toStringHelper() .add("billingTier", billingTier) .add("cacheHit", cacheHit) .add("totalBytesBilled", totalBytesBilled) - .add("totalBytesProcessed", totalBytesProcessed); + .add("totalBytesProcessed", totalBytesProcessed) + .add("queryPlan", queryPlan); } @Override @@ -343,7 +369,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { return Objects.hash(super.hashCode(), billingTier, cacheHit, totalBytesBilled, - totalBytesProcessed); + totalBytesProcessed, queryPlan); } @Override @@ -353,6 +379,9 @@ com.google.api.services.bigquery.model.JobStatistics toPb() { queryStatisticsPb.setCacheHit(cacheHit); queryStatisticsPb.setTotalBytesBilled(totalBytesBilled); queryStatisticsPb.setTotalBytesProcessed(totalBytesProcessed); + if (queryPlan != null) { + queryStatisticsPb.setQueryPlan(Lists.transform(queryPlan, QueryStage.TO_PB_FUNCTION)); + } return super.toPb().setQuery(queryStatisticsPb); } diff --git a/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryStage.java b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryStage.java new file mode 100644 index 000000000000..85de9be96818 --- /dev/null +++ b/gcloud-java-bigquery/src/main/java/com/google/gcloud/bigquery/QueryStage.java @@ -0,0 +1,431 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.bigquery; + +import com.google.api.services.bigquery.model.ExplainQueryStage; +import com.google.api.services.bigquery.model.ExplainQueryStep; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * BigQuery provides diagnostic information about a completed query's execution plan (or query plan + * for short). The query plan describes a query as a series of stages, with each stage comprising a + * number of steps that read from data sources, perform a series of transformations on the input, + * and emit an output to a future stage (or the final result). This class contains information on a + * query stage. + * + * @see Query Plan + */ +public class QueryStage implements Serializable { + + static final Function FROM_PB_FUNCTION = + new Function() { + @Override + public QueryStage apply(ExplainQueryStage pb) { + return QueryStage.fromPb(pb); + } + }; + static final Function TO_PB_FUNCTION = + new Function() { + @Override + public ExplainQueryStage apply(QueryStage stage) { + return stage.toPb(); + } + }; + private static final long serialVersionUID = -472281297327952320L; + + /** + * Each query stage is made of a number of steps. This class contains information on a query step. + * + * @see Steps + * Metadata + */ + public static class QueryStep implements Serializable { + + static final Function FROM_PB_FUNCTION = + new Function() { + @Override + public QueryStep apply(ExplainQueryStep pb) { + return QueryStep.fromPb(pb); + } + }; + static final Function TO_PB_FUNCTION = + new Function() { + @Override + public ExplainQueryStep apply(QueryStep stage) { + return stage.toPb(); + } + }; + private static final long serialVersionUID = 8663444604771794411L; + + private final String kind; + private final List substeps; + + QueryStep(String kind, List substeps) { + this.kind = kind; + this.substeps = substeps; + } + + public String kind() { + return kind; + } + + public List substeps() { + return substeps; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("kind", kind) + .add("substeps", substeps) + .toString(); + } + + @Override + public int hashCode() { + return Objects.hash(kind, substeps); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof QueryStep)) { + return false; + } + QueryStep other = (QueryStep) obj; + return Objects.equals(kind, other.kind) + && Objects.equals(substeps, other.substeps); + } + + ExplainQueryStep toPb() { + return new ExplainQueryStep().setKind(kind).setSubsteps(substeps); + } + + static QueryStep fromPb(com.google.api.services.bigquery.model.ExplainQueryStep stepPb) { + List substeps = null; + if (stepPb.getSubsteps() != null) { + substeps = ImmutableList.copyOf(stepPb.getSubsteps()); + } + return new QueryStep(stepPb.getKind(), substeps); + } + } + + private final double computeRatioAvg; + private final double computeRatioMax; + private final long id; + private final String name; + private final double readRatioAvg; + private final double readRatioMax; + private final long recordsRead; + private final long recordsWritten; + private final List steps; + private final double waitRatioAvg; + private final double waitRatioMax; + private final double writeRatioAvg; + private final double writeRatioMax; + + static final class Builder { + + private double computeRatioAvg; + private double computeRatioMax; + private long id; + private String name; + private double readRatioAvg; + private double readRatioMax; + private long recordsRead; + private long recordsWritten; + private List steps; + private double waitRatioAvg; + private double waitRatioMax; + private double writeRatioAvg; + private double writeRatioMax; + + private Builder() {} + + Builder computeRatioAvg(double computeRatioAvg) { + this.computeRatioAvg = computeRatioAvg; + return this; + } + + Builder computeRatioMax(double computeRatioMax) { + this.computeRatioMax = computeRatioMax; + return this; + } + + Builder id(long id) { + this.id = id; + return this; + } + + Builder name(String name) { + this.name = name; + return this; + } + + Builder readRatioAvg(double readRatioAvg) { + this.readRatioAvg = readRatioAvg; + return this; + } + + Builder readRatioMax(double readRatioMax) { + this.readRatioMax = readRatioMax; + return this; + } + + Builder recordsRead(long recordsRead) { + this.recordsRead = recordsRead; + return this; + } + + Builder recordsWritten(long recordsWritten) { + this.recordsWritten = recordsWritten; + return this; + } + + Builder steps(List steps) { + this.steps = steps; + return this; + } + + Builder waitRatioAvg(double waitRatioAvg) { + this.waitRatioAvg = waitRatioAvg; + return this; + } + + Builder waitRatioMax(double waitRatioMax) { + this.waitRatioMax = waitRatioMax; + return this; + } + + Builder writeRatioAvg(double writeRatioAvg) { + this.writeRatioAvg = writeRatioAvg; + return this; + } + + Builder writeRatioMax(double writeRatioMax) { + this.writeRatioMax = writeRatioMax; + return this; + } + + QueryStage build() { + return new QueryStage(this); + } + } + + QueryStage(Builder builder) { + computeRatioAvg = builder.computeRatioAvg; + computeRatioMax = builder.computeRatioMax; + id = builder.id; + name = builder.name; + readRatioAvg = builder.readRatioAvg; + readRatioMax = builder.readRatioMax; + recordsRead = builder.recordsRead; + recordsWritten = builder.recordsWritten; + steps = builder.steps; + waitRatioAvg = builder.waitRatioAvg; + waitRatioMax = builder.waitRatioMax; + writeRatioAvg = builder.writeRatioAvg; + writeRatioMax = builder.writeRatioMax; + } + + /** + * Returns the relative amount of time the average shard spent on CPU-bound tasks. + */ + public double computeRatioAvg() { + return computeRatioAvg; + } + + /** + * Returns the relative amount of time the slowest shard spent on CPU-bound tasks. + */ + public double computeRatioMax() { + return computeRatioMax; + } + + /** + * Returns a unique ID for the stage within its plan. + */ + public long id() { + return id; + } + + /** + * Returns a human-readable name for the stage. + */ + public String name() { + return name; + } + + /** + * Returns the relative amount of time the average shard spent reading input. + */ + public double readRatioAvg() { + return readRatioAvg; + } + + /** + * Returns the relative amount of time the slowest shard spent reading input. + */ + public double readRatioMax() { + return readRatioMax; + } + + /** + * Returns the number of records read into the stage. + */ + public long recordsRead() { + return recordsRead; + } + + /** + * Returns the number of records written by the stage. + */ + public long recordsWritten() { + return recordsWritten; + } + + /** + * Returns the list of steps within the stage in dependency order (approximately chronological). + */ + public List steps() { + return steps; + } + + /** + * Returns the relative amount of time the average shard spent waiting to be scheduled. + */ + public double waitRatioAvg() { + return waitRatioAvg; + } + + /** + * Returns the relative amount of time the slowest shard spent waiting to be scheduled. + */ + public double waitRatioMax() { + return waitRatioMax; + } + + /** + * Returns the relative amount of time the average shard spent on writing output. + */ + public double writeRatioAvg() { + return writeRatioAvg; + } + + /** + * Returns the relative amount of time the slowest shard spent on writing output. + */ + public double writeRatioMax() { + return writeRatioMax; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("computeRatioAvg", computeRatioAvg) + .add("computeRatioMax", computeRatioMax) + .add("id", id) + .add("name", name) + .add("readRatioAvg", readRatioAvg) + .add("readRatioMax", readRatioMax) + .add("recordsRead", recordsRead) + .add("recordsWritten", recordsWritten) + .add("steps", steps) + .add("waitRatioAvg", waitRatioAvg) + .add("waitRatioMax", waitRatioMax) + .add("writeRatioAvg", writeRatioAvg) + .add("writeRatioMax", writeRatioMax) + .toString(); + } + + @Override + public int hashCode() { + return Objects.hash(computeRatioAvg, computeRatioMax, id, name, readRatioAvg, readRatioMax, + recordsRead, recordsWritten, steps, waitRatioAvg, waitRatioMax, writeRatioAvg); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof QueryStage)) { + return false; + } + QueryStage other = (QueryStage) obj; + return id == other.id + && computeRatioAvg == other.computeRatioAvg + && computeRatioMax == other.computeRatioMax + && readRatioAvg == other.readRatioAvg + && readRatioMax == other.readRatioMax + && recordsRead == other.recordsRead + && recordsWritten == other.recordsWritten + && waitRatioAvg == other.waitRatioAvg + && waitRatioMax == other.waitRatioMax + && writeRatioAvg == other.writeRatioAvg + && writeRatioMax == other.writeRatioMax + && Objects.equals(steps, other.steps) + && Objects.equals(name, other.name); + } + + static Builder builder() { + return new Builder(); + } + + ExplainQueryStage toPb() { + ExplainQueryStage stagePb = new ExplainQueryStage() + .setComputeRatioAvg(computeRatioAvg) + .setComputeRatioMax(computeRatioMax) + .setId(id) + .setName(name) + .setReadRatioAvg(readRatioAvg) + .setReadRatioMax(readRatioMax) + .setRecordsRead(recordsRead) + .setRecordsWritten(recordsWritten) + .setWaitRatioAvg(waitRatioAvg) + .setWaitRatioMax(waitRatioMax) + .setWriteRatioAvg(writeRatioAvg) + .setWriteRatioMax(writeRatioMax); + if (steps != null) { + stagePb.setSteps(Lists.transform(steps, QueryStep.TO_PB_FUNCTION)); + } + return stagePb; + } + + static QueryStage fromPb(com.google.api.services.bigquery.model.ExplainQueryStage stagePb) { + Builder builder = new QueryStage.Builder(); + builder.computeRatioAvg(stagePb.getComputeRatioAvg()); + builder.computeRatioMax(stagePb.getComputeRatioMax()); + builder.id(stagePb.getId()); + builder.name(stagePb.getName()); + builder.readRatioAvg(stagePb.getReadRatioAvg()); + builder.readRatioMax(stagePb.getReadRatioMax()); + builder.recordsRead(stagePb.getRecordsRead()); + builder.recordsWritten(stagePb.getRecordsWritten()); + if (stagePb.getSteps() != null) { + builder.steps(Lists.transform(stagePb.getSteps(), QueryStep.FROM_PB_FUNCTION)); + } + builder.waitRatioAvg(stagePb.getWaitRatioAvg()); + builder.waitRatioMax(stagePb.getWaitRatioMax()); + builder.writeRatioAvg(stagePb.getWriteRatioAvg()); + builder.writeRatioMax(stagePb.getWriteRatioMax()); + return builder.build(); + } +} diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ITBigQueryTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ITBigQueryTest.java index 34f4f6893187..e11dea1a9936 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ITBigQueryTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/ITBigQueryTest.java @@ -610,6 +610,8 @@ public void testQuery() throws InterruptedException { rowCount++; } assertEquals(2, rowCount); + QueryJobInfo queryJob = bigquery.getJob(response.jobId()); + assertNotNull(queryJob.statistics().queryPlan()); } @Test diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatisticsTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatisticsTest.java index 5b2123faa67d..1ec67d034754 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatisticsTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/JobStatisticsTest.java @@ -22,6 +22,7 @@ import com.google.gcloud.bigquery.JobStatistics.ExtractStatistics; import com.google.gcloud.bigquery.JobStatistics.LoadStatistics; import com.google.gcloud.bigquery.JobStatistics.QueryStatistics; +import com.google.gcloud.bigquery.QueryStage.QueryStep; import org.junit.Test; @@ -63,6 +64,26 @@ public class JobStatisticsTest { .inputBytes(INPUT_BYTES) .inputFiles(INPUT_FILES) .build(); + private static final List SUBSTEPS1 = ImmutableList.of("substep1", "substep2"); + private static final List SUBSTEPS2 = ImmutableList.of("substep3", "substep4"); + private static final QueryStep QUERY_STEP1 = new QueryStep("KIND", SUBSTEPS1); + private static final QueryStep QUERY_STEP2 = new QueryStep("KIND", SUBSTEPS2); + private static final QueryStage QUERY_STAGE = QueryStage.builder() + .computeRatioAvg(1.1) + .computeRatioMax(2.2) + .id(42L) + .name("stage") + .readRatioAvg(3.3) + .readRatioMax(4.4) + .recordsRead(5L) + .recordsWritten(6L) + .steps(ImmutableList.of(QUERY_STEP1, QUERY_STEP2)) + .waitRatioAvg(7.7) + .waitRatioMax(8.8) + .writeRatioAvg(9.9) + .writeRatioMax(10.10) + .build(); + private static final List QUERY_PLAN = ImmutableList.of(QUERY_STAGE); private static final QueryStatistics QUERY_STATISTICS = QueryStatistics.builder() .creationTime(CREATION_TIME) .endTime(END_TIME) @@ -71,6 +92,7 @@ public class JobStatisticsTest { .cacheHit(CACHE_HIT) .totalBytesBilled(TOTAL_BYTES_BILLED) .totalBytesProcessed(TOTAL_BYTES_PROCESSED) + .queryPlan(QUERY_PLAN) .build(); private static final QueryStatistics QUERY_STATISTICS_INCOMPLETE = QueryStatistics.builder() .creationTime(CREATION_TIME) @@ -111,6 +133,8 @@ public void testBuilder() { assertEquals(CACHE_HIT, QUERY_STATISTICS.cacheHit()); assertEquals(TOTAL_BYTES_BILLED, QUERY_STATISTICS.totalBytesBilled()); assertEquals(TOTAL_BYTES_PROCESSED, QUERY_STATISTICS.totalBytesProcessed()); + assertEquals(TOTAL_BYTES_PROCESSED, QUERY_STATISTICS.totalBytesProcessed()); + assertEquals(QUERY_PLAN, QUERY_STATISTICS.queryPlan()); assertEquals(CREATION_TIME, LOAD_STATISTICS_INCOMPLETE.creationTime()); assertEquals(START_TIME, LOAD_STATISTICS_INCOMPLETE.startTime()); @@ -127,6 +151,7 @@ public void testBuilder() { assertEquals(CACHE_HIT, QUERY_STATISTICS_INCOMPLETE.cacheHit()); assertEquals(null, QUERY_STATISTICS_INCOMPLETE.totalBytesBilled()); assertEquals(null, QUERY_STATISTICS_INCOMPLETE.totalBytesProcessed()); + assertEquals(null, QUERY_STATISTICS_INCOMPLETE.queryPlan()); } @Test @@ -165,6 +190,7 @@ private void compareQueryStatistics(QueryStatistics expected, QueryStatistics va assertEquals(expected.cacheHit(), value.cacheHit()); assertEquals(expected.totalBytesBilled(), value.totalBytesBilled()); assertEquals(expected.totalBytesProcessed(), value.totalBytesProcessed()); + assertEquals(expected.queryPlan(), value.queryPlan()); } private void compareStatistics(JobStatistics expected, JobStatistics value) { diff --git a/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryStageTest.java b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryStageTest.java new file mode 100644 index 000000000000..6ffc05ab50f2 --- /dev/null +++ b/gcloud-java-bigquery/src/test/java/com/google/gcloud/bigquery/QueryStageTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud.bigquery; + +import static org.junit.Assert.assertEquals; + +import com.google.common.collect.ImmutableList; +import com.google.gcloud.bigquery.QueryStage.QueryStep; + +import org.junit.Test; + +import java.util.List; + +public class QueryStageTest { + + private static final List SUBSTEPS1 = ImmutableList.of("substep1", "substep2"); + private static final List SUBSTEPS2 = ImmutableList.of("substep3", "substep4"); + private static final QueryStep QUERY_STEP1 = new QueryStep("KIND", SUBSTEPS1); + private static final QueryStep QUERY_STEP2 = new QueryStep("KIND", SUBSTEPS2); + private static final double COMPUTE_RATIO_AVG = 1.1; + private static final double COMPUTE_RATIO_MAX = 2.2; + private static final long ID = 42L; + private static final String NAME = "StageName"; + private static final double READ_RATIO_AVG = 3.3; + private static final double READ_RATIO_MAX = 4.4; + private static final long RECORDS_READ = 5L; + private static final long RECORDS_WRITTEN = 6L; + private static final List STEPS = ImmutableList.of(QUERY_STEP1, QUERY_STEP2); + private static final double WAIT_RATIO_AVG = 7.7; + private static final double WAIT_RATIO_MAX = 8.8; + private static final double WRITE_RATIO_AVG = 9.9; + private static final double WRITE_RATIO_MAX = 10.10; + private static final QueryStage QUERY_STAGE = QueryStage.builder() + .computeRatioAvg(COMPUTE_RATIO_AVG) + .computeRatioMax(COMPUTE_RATIO_MAX) + .id(ID) + .name(NAME) + .readRatioAvg(READ_RATIO_AVG) + .readRatioMax(READ_RATIO_MAX) + .recordsRead(RECORDS_READ) + .recordsWritten(RECORDS_WRITTEN) + .steps(STEPS) + .waitRatioAvg(WAIT_RATIO_AVG) + .waitRatioMax(WAIT_RATIO_MAX) + .writeRatioAvg(WRITE_RATIO_AVG) + .writeRatioMax(WRITE_RATIO_MAX) + .build(); + + @Test + public void testQueryStepConstructor() { + assertEquals("KIND", QUERY_STEP1.kind()); + assertEquals("KIND", QUERY_STEP2.kind()); + assertEquals(SUBSTEPS1, QUERY_STEP1.substeps()); + assertEquals(SUBSTEPS2, QUERY_STEP2.substeps()); + } + + @Test + public void testBuilder() { + assertEquals(COMPUTE_RATIO_AVG, QUERY_STAGE.computeRatioAvg(), 0); + assertEquals(COMPUTE_RATIO_MAX, QUERY_STAGE.computeRatioMax(), 0); + assertEquals(ID, QUERY_STAGE.id()); + assertEquals(NAME, QUERY_STAGE.name()); + assertEquals(READ_RATIO_AVG, QUERY_STAGE.readRatioAvg(), 0); + assertEquals(READ_RATIO_MAX, QUERY_STAGE.readRatioMax(), 0); + assertEquals(RECORDS_READ, QUERY_STAGE.recordsRead()); + assertEquals(RECORDS_WRITTEN, QUERY_STAGE.recordsWritten()); + assertEquals(STEPS, QUERY_STAGE.steps()); + assertEquals(WAIT_RATIO_AVG, QUERY_STAGE.waitRatioAvg(), 0); + assertEquals(WAIT_RATIO_MAX, QUERY_STAGE.waitRatioMax(), 0); + assertEquals(WRITE_RATIO_AVG, QUERY_STAGE.writeRatioAvg(), 0); + assertEquals(WRITE_RATIO_MAX, QUERY_STAGE.writeRatioMax(), 0); + } + + @Test + public void testToAndFromPb() { + compareQueryStep(QUERY_STEP1, QueryStep.fromPb(QUERY_STEP1.toPb())); + compareQueryStep(QUERY_STEP2, QueryStep.fromPb(QUERY_STEP2.toPb())); + compareQueryStage(QUERY_STAGE, QueryStage.fromPb(QUERY_STAGE.toPb())); + } + + @Test + public void testEquals() { + compareQueryStep(QUERY_STEP1, QUERY_STEP1); + compareQueryStep(QUERY_STEP2, QUERY_STEP2); + compareQueryStage(QUERY_STAGE, QUERY_STAGE); + } + + private void compareQueryStage(QueryStage expected, QueryStage value) { + assertEquals(expected, value); + assertEquals(expected.computeRatioAvg(), value.computeRatioAvg(), 0); + assertEquals(expected.computeRatioMax(), value.computeRatioMax(), 0); + assertEquals(expected.id(), value.id()); + assertEquals(expected.name(), value.name()); + assertEquals(expected.readRatioAvg(), value.readRatioAvg(), 0); + assertEquals(expected.readRatioMax(), value.readRatioMax(), 0); + assertEquals(expected.recordsRead(), value.recordsRead()); + assertEquals(expected.recordsWritten(), value.recordsWritten()); + assertEquals(expected.steps(), value.steps()); + assertEquals(expected.waitRatioAvg(), value.waitRatioAvg(), 0); + assertEquals(expected.waitRatioMax(), value.waitRatioMax(), 0); + assertEquals(expected.writeRatioAvg(), value.writeRatioAvg(), 0); + assertEquals(expected.writeRatioMax(), value.writeRatioMax(), 0); + assertEquals(expected.hashCode(), value.hashCode()); + } + + private void compareQueryStep(QueryStep expected, QueryStep value) { + assertEquals(expected, value); + assertEquals(expected.kind(), value.kind()); + assertEquals(expected.substeps(), value.substeps()); + assertEquals(expected.hashCode(), value.hashCode()); + } +}