|
24 | 24 | import java.util.Arrays; |
25 | 25 | import java.util.Objects; |
26 | 26 | import javax.annotation.concurrent.GuardedBy; |
| 27 | +import org.apache.beam.model.pipeline.v1.MetricsApi.HistogramValue; |
| 28 | +import org.apache.beam.model.pipeline.v1.MetricsApi.HistogramValue.BucketOptions; |
| 29 | +import org.apache.beam.model.pipeline.v1.MetricsApi.HistogramValue.BucketOptions.Base2Exponent; |
| 30 | +import org.apache.beam.model.pipeline.v1.MetricsApi.HistogramValue.BucketOptions.Linear; |
| 31 | +import org.apache.beam.sdk.annotations.Internal; |
| 32 | +import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; |
27 | 33 | import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.math.DoubleMath; |
28 | 34 | import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.math.IntMath; |
29 | 35 | import org.checkerframework.checker.nullness.qual.Nullable; |
@@ -74,6 +80,43 @@ public HistogramData(BucketType bucketType) { |
74 | 80 | this.sumOfSquaredDeviations = 0; |
75 | 81 | } |
76 | 82 |
|
| 83 | + /** |
| 84 | + * Create a histogram from HistogramValue proto. |
| 85 | + * |
| 86 | + * @param histogramProto HistogramValue proto used to populate stats for the histogram. |
| 87 | + */ |
| 88 | + public HistogramData(HistogramValue histogramProto) { |
| 89 | + int numBuckets; |
| 90 | + if (histogramProto.getBucketOptions().hasLinear()) { |
| 91 | + System.out.println("xxx its linear"); |
| 92 | + double start = histogramProto.getBucketOptions().getLinear().getStart(); |
| 93 | + double width = histogramProto.getBucketOptions().getLinear().getWidth(); |
| 94 | + numBuckets = histogramProto.getBucketOptions().getLinear().getNumberOfBuckets(); |
| 95 | + this.bucketType = LinearBuckets.of(start, width, numBuckets); |
| 96 | + this.buckets = new long[bucketType.getNumBuckets()]; |
| 97 | + |
| 98 | + int idx = 0; |
| 99 | + for (long val : histogramProto.getBucketCountsList()) { |
| 100 | + this.buckets[idx] = val; |
| 101 | + this.numBoundedBucketRecords += val; |
| 102 | + idx++; |
| 103 | + } |
| 104 | + } else { |
| 105 | + System.out.println("xxx its exp"); |
| 106 | + // Assume it's a exponential histogram if its not linear |
| 107 | + int scale = histogramProto.getBucketOptions().getExponential().getScale(); |
| 108 | + numBuckets = histogramProto.getBucketOptions().getExponential().getNumberOfBuckets(); |
| 109 | + this.bucketType = ExponentialBuckets.of(scale, numBuckets); |
| 110 | + this.buckets = new long[bucketType.getNumBuckets()]; |
| 111 | + int idx = 0; |
| 112 | + for (long val : histogramProto.getBucketCountsList()) { |
| 113 | + this.buckets[idx] = val; |
| 114 | + this.numBoundedBucketRecords += val; |
| 115 | + idx++; |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | + |
77 | 120 | public BucketType getBucketType() { |
78 | 121 | return this.bucketType; |
79 | 122 | } |
@@ -207,6 +250,10 @@ public synchronized HistogramData getAndReset() { |
207 | 250 | return other; |
208 | 251 | } |
209 | 252 |
|
| 253 | + public synchronized long[] getBucketCount() { |
| 254 | + return buckets; |
| 255 | + } |
| 256 | + |
210 | 257 | public synchronized void record(double value) { |
211 | 258 | double rangeTo = bucketType.getRangeTo(); |
212 | 259 | double rangeFrom = bucketType.getRangeFrom(); |
@@ -240,6 +287,64 @@ private synchronized void updateStatistics(double value) { |
240 | 287 | sumOfSquaredDeviations += (value - mean) * (value - oldMean); |
241 | 288 | } |
242 | 289 |
|
| 290 | + public static class HistogramParsingException extends RuntimeException { |
| 291 | + public HistogramParsingException(String message) { |
| 292 | + super(message); |
| 293 | + } |
| 294 | + } |
| 295 | + |
| 296 | + /** Converts this {@link HistogramData} to its proto {@link HistogramValue}. */ |
| 297 | + public synchronized HistogramValue toProto() { |
| 298 | + HistogramValue.Builder builder = HistogramValue.newBuilder(); |
| 299 | + // try { |
| 300 | + int numberOfBuckets = this.getBucketType().getNumBuckets(); |
| 301 | + |
| 302 | + if (this.getBucketType() instanceof HistogramData.LinearBuckets) { |
| 303 | + System.out.println("xxx linear buckets"); |
| 304 | + HistogramData.LinearBuckets buckets = (HistogramData.LinearBuckets) this.getBucketType(); |
| 305 | + Linear.Builder linearBuilder = Linear.newBuilder(); |
| 306 | + linearBuilder.setNumberOfBuckets(numberOfBuckets); |
| 307 | + linearBuilder.setWidth(buckets.getWidth()); |
| 308 | + linearBuilder.setStart(buckets.getStart()); |
| 309 | + Linear linearOptions = linearBuilder.build(); |
| 310 | + |
| 311 | + BucketOptions.Builder bucketBuilder = BucketOptions.newBuilder(); |
| 312 | + bucketBuilder.setLinear(linearOptions); |
| 313 | + builder.setBucketOptions(bucketBuilder.build()); |
| 314 | + |
| 315 | + } else if (this.getBucketType() instanceof HistogramData.ExponentialBuckets) { |
| 316 | + System.out.println("xxx exp buckets"); |
| 317 | + HistogramData.ExponentialBuckets buckets = |
| 318 | + (HistogramData.ExponentialBuckets) this.getBucketType(); |
| 319 | + |
| 320 | + Base2Exponent.Builder base2ExpBuilder = Base2Exponent.newBuilder(); |
| 321 | + base2ExpBuilder.setNumberOfBuckets(numberOfBuckets); |
| 322 | + base2ExpBuilder.setScale(buckets.getScale()); |
| 323 | + Base2Exponent exponentialOptions = base2ExpBuilder.build(); |
| 324 | + |
| 325 | + BucketOptions.Builder bucketBuilder = BucketOptions.newBuilder(); |
| 326 | + bucketBuilder.setExponential(exponentialOptions); |
| 327 | + builder.setBucketOptions(bucketBuilder.build()); |
| 328 | + } else { |
| 329 | + throw new HistogramParsingException( |
| 330 | + "Unable to encode Int64 Histogram, bucket is not recognized"); |
| 331 | + } |
| 332 | + |
| 333 | + builder.setCount(this.getTotalCount()); |
| 334 | + |
| 335 | + for (long val : this.getBucketCount()) { |
| 336 | + builder.addBucketCounts(val); |
| 337 | + } |
| 338 | + System.out.println("xxxx " + builder.toString()); |
| 339 | + return builder.build(); |
| 340 | + } |
| 341 | + |
| 342 | + // /** Creates a {@link HistogramData} instance from its proto {@link HistogramValue}. */ |
| 343 | + // public static HistogramData fromProto(HistogramValue proto) { |
| 344 | + // HistgramValue value = new HistgramValue(); |
| 345 | + // return new HistogramValue(proto); |
| 346 | + // } |
| 347 | + |
243 | 348 | /** |
244 | 349 | * Increment the {@code numTopRecords} and update {@code topRecordsSum} when a new overflow value |
245 | 350 | * is recorded. This function should only be called when a Histogram is recording a value greater |
@@ -573,6 +678,42 @@ public double getRangeTo() { |
573 | 678 | // Note: equals() and hashCode() are implemented by the AutoValue. |
574 | 679 | } |
575 | 680 |
|
| 681 | + /** Used for testing unsupported Bucket formats. */ |
| 682 | + @AutoValue |
| 683 | + @Internal |
| 684 | + @VisibleForTesting |
| 685 | + public abstract static class UnsupportedBuckets implements BucketType { |
| 686 | + |
| 687 | + public static UnsupportedBuckets of() { |
| 688 | + return new AutoValue_HistogramData_UnsupportedBuckets(0); |
| 689 | + } |
| 690 | + |
| 691 | + @Override |
| 692 | + public int getBucketIndex(double value) { |
| 693 | + return 0; |
| 694 | + } |
| 695 | + |
| 696 | + @Override |
| 697 | + public double getBucketSize(int index) { |
| 698 | + return 0; |
| 699 | + } |
| 700 | + |
| 701 | + @Override |
| 702 | + public double getAccumulatedBucketSize(int index) { |
| 703 | + return 0; |
| 704 | + } |
| 705 | + |
| 706 | + @Override |
| 707 | + public double getRangeFrom() { |
| 708 | + return 0; |
| 709 | + } |
| 710 | + |
| 711 | + @Override |
| 712 | + public double getRangeTo() { |
| 713 | + return 0; |
| 714 | + } |
| 715 | + } |
| 716 | + |
576 | 717 | @Override |
577 | 718 | public synchronized boolean equals(@Nullable Object object) { |
578 | 719 | if (object instanceof HistogramData) { |
|
0 commit comments