Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ object MetricsSpec extends ZIOBaseSpec {
|test_frequency tags[x: a, y: b] Frequency[(strValue2 -> 1), (strValue1 -> 2)]
|test_gauge(description2) tags[x: a, y: b] Gauge[${3.0}]
|test_histogram tags[x: a, y: b] Histogram[buckets: [(${1.0} -> 1), (${2.0} -> 1), (${3.0} -> 2), (${1.7976931348623157e308} -> 2)], count: [2], min: [${1.0}], max: [${3.0}], sum: [${4.0}]]
|test_summary tags[x: a, y: b] Summary[quantiles: [(0.1 -> None), (0.5 -> Some(${1.0})), (0.9 -> Some(${1.0}))], count: [2], min: [${1.0}], max: [${3.0}], sum: [${4.0}]]""".stripMargin
|test_summary tags[x: a, y: b] Summary[quantiles: [(0.1 -> Some(${1.0})), (0.5 -> Some(${1.0})), (0.9 -> Some(${3.0}))], count: [2], min: [${1.0}], max: [${3.0}], sum: [${4.0}]]""".stripMargin
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private[zio] class ConcurrentMetricHooksPlatformSpecific extends ConcurrentMetri
}
}

zio.internal.metrics.calculateQuantiles(error, sortedQuantiles, builder.result().sorted(DoubleOrdering))
zio.internal.metrics.calculateQuantiles(sortedQuantiles, builder.result().sorted(DoubleOrdering))
}

// Assuming that the instant of observed values is continuously increasing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private[zio] class ConcurrentMetricHooksPlatformSpecific extends ConcurrentMetri
}
}

zio.internal.metrics.calculateQuantiles(error, sortedQuantiles, builder.result().sorted(DoubleOrdering))
zio.internal.metrics.calculateQuantiles(sortedQuantiles, builder.result().sorted(DoubleOrdering))
}

// Assuming that the instant of observed values is continuously increasing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private[zio] class ConcurrentMetricHooksPlatformSpecific extends ConcurrentMetri
}
}

zio.internal.metrics.calculateQuantiles(error, sortedQuantiles, builder.result().sorted(DoubleOrdering))
zio.internal.metrics.calculateQuantiles(sortedQuantiles, builder.result().sorted(DoubleOrdering))
}

// Assuming that the instant of observed values is continuously increasing
Expand Down
74 changes: 11 additions & 63 deletions core/shared/src/main/scala/zio/internal/metrics/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,73 +14,21 @@ package object metrics {
(l, r) => java.lang.Double.compare(l, r)

private[zio] def calculateQuantiles(
error: Double,
sortedQuantiles: Chunk[Double],
sortedSamples: Chunk[Double]
): Chunk[(Double, Option[Double])] = {

// The number of the samples examined
val sampleCnt = sortedSamples.size

@tailrec
def get(
current: Option[Double],
consumed: Int,
q: Double,
rest: Chunk[Double]
): ResolvedQuantile =
rest match {
// if the remaining list of samples is empty there is nothing more to resolve
case c if c.isEmpty => ResolvedQuantile(q, None, consumed, Chunk.empty)
// if the quantile is the 100% Quantile, we can take the max of all remaining values as the result
case c if q == 1.0d => ResolvedQuantile(q, Some(c.last), consumed + c.length, Chunk.empty)
case c =>
// Split in 2 chunks, the first chunk contains all elements of the same value as the chunk head
val sameHead = c.splitWhere(_ > c.head)
// How many elements do we want to accept for this quantile
val desired = q * sampleCnt
// The error margin
val allowedError = error / 2 * desired
// Taking into account the elements consumed from the samples so far and the number of
// same elements at the beginning of the chunk
// calculate the number of elements we would have if we selected the current head as result
val candConsumed = consumed + sameHead._1.length
val candError = Math.abs(candConsumed - desired)

// If we haven't got enough elements yet, recurse
if (candConsumed < desired - allowedError)
get(c.headOption, candConsumed, q, sameHead._2)
// If we have too many elements, select the previous value and hand back the the rest as leftover
else if (candConsumed > desired + allowedError) ResolvedQuantile(q, current, consumed, c)
// If we are in the target interval, select the current head and hand back the leftover after dropping all elements
// from the sample chunk that are equal to the current head
else {
current match {
case None => get(c.headOption, candConsumed, q, sameHead._2)
case Some(current) =>
val prevError = Math.abs(desired - current)
if (candError < prevError) get(c.headOption, candConsumed, q, sameHead._2)
else ResolvedQuantile(q, Some(current), consumed, rest)
}
}
val length = sortedSamples.length
if (length == 0) sortedQuantiles.map((_, None))
else {
sortedQuantiles.map { quantile =>
if (quantile <= 0.0) (quantile, Some(sortedSamples(0)))
else if (quantile >= 1.0) (quantile, Some(sortedSamples(length - 1)))
else {
val index = math.ceil(quantile * length).toInt - 1
val value = sortedSamples(index)
(quantile, Some(value))
}
}

val resolved = sortedQuantiles match {
case e if e.isEmpty => Chunk.empty
case c =>
sortedQuantiles.tail
.foldLeft(Chunk(get(None, 0, c.head, sortedSamples))) { case (cur, q) =>
cur ++ Chunk(get(cur.head.value, cur.head.consumed, q, cur.head.rest))
}
}

resolved.map(rq => (rq.quantile, rq.value))
}

private[metrics] case class ResolvedQuantile(
quantile: Double, // The Quantile that shall be resolved
value: Option[Double], // Some(d) if a value for the quantile could be found, None otherwise
consumed: Int, // How many samples have been consumed before this quantile
rest: Chunk[Double] // The rest of the samples after the quantile has been resolved
)
}