From d9cd75db039b31eec8d7271b162ea822d1d4d5e3 Mon Sep 17 00:00:00 2001 From: Ivan Topolnjak Date: Wed, 13 Dec 2017 00:48:14 +0100 Subject: add PeriodSnapshotAccumulator, successor of 0.6.x TickMetricSnapshotBuffer --- .../metric/PeriodSnapshotAccumulatorSpec.scala | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 kamon-core-tests/src/test/scala/kamon/metric/PeriodSnapshotAccumulatorSpec.scala (limited to 'kamon-core-tests') diff --git a/kamon-core-tests/src/test/scala/kamon/metric/PeriodSnapshotAccumulatorSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/PeriodSnapshotAccumulatorSpec.scala new file mode 100644 index 00000000..202fe5b9 --- /dev/null +++ b/kamon-core-tests/src/test/scala/kamon/metric/PeriodSnapshotAccumulatorSpec.scala @@ -0,0 +1,129 @@ +package kamon.metric + +import java.time.temporal.ChronoUnit +import java.time.Duration + +import kamon.Kamon +import kamon.testkit.{MetricInspection, Reconfigure} +import org.scalatest.{BeforeAndAfterAll, Matchers, OptionValues, WordSpec} + +class PeriodSnapshotAccumulatorSpec extends WordSpec with Reconfigure with MetricInspection with Matchers + with BeforeAndAfterAll with OptionValues { + + "the PeriodSnapshotAccumulator" should { + "allow to peek on an empty accumulator" in { + val accumulator = newAccumulator(10, 1) + val periodSnapshot = accumulator.peek() + periodSnapshot.metrics.histograms shouldBe empty + periodSnapshot.metrics.rangeSamplers shouldBe empty + periodSnapshot.metrics.gauges shouldBe empty + periodSnapshot.metrics.counters shouldBe empty + } + + "bypass accumulation if the configured duration is equal to the metric tick-interval, regardless of the snapshot" in { + val accumulator = newAccumulator(10, 1) + accumulator.add(tenSeconds).value should be theSameInstanceAs(tenSeconds) + accumulator.add(fiveSecondsOne).value should be theSameInstanceAs(fiveSecondsOne) + } + + "bypass accumulation if snapshots have a period longer than duration minus margin" in { + val accumulator = newAccumulator(4, 1) + accumulator.add(almostThreeSeconds) shouldBe empty + accumulator.add(threeSeconds).value should be theSameInstanceAs(threeSeconds) + accumulator.add(fourSeconds).value should be theSameInstanceAs(fourSeconds) + accumulator.add(nineSeconds).value should be theSameInstanceAs(nineSeconds) + } + + "allow to peek into the data that has been accumulated" in { + val accumulator = newAccumulator(20, 1) + accumulator.add(fiveSecondsOne) shouldBe empty + accumulator.add(fiveSecondsTwo) shouldBe empty + + for(_ <- 1 to 10) { + val peekSnapshot = accumulator.peek() + val mergedHistogram = peekSnapshot.metrics.histograms.head + val mergedRangeSampler = peekSnapshot.metrics.rangeSamplers.head + peekSnapshot.metrics.counters.head.value shouldBe (55) + peekSnapshot.metrics.gauges.head.value shouldBe (33) + mergedHistogram.distribution.buckets.map(_.value) should contain allOf(22L, 33L) + mergedRangeSampler.distribution.buckets.map(_.value) should contain allOf(22L, 33L) + } + + accumulator.add(fiveSecondsThree) shouldBe empty + + for(_ <- 1 to 10) { + val peekSnapshot = accumulator.peek() + val mergedHistogram = peekSnapshot.metrics.histograms.head + val mergedRangeSampler = peekSnapshot.metrics.rangeSamplers.head + peekSnapshot.metrics.counters.head.value shouldBe (67) + peekSnapshot.metrics.gauges.head.value shouldBe (12) + mergedHistogram.distribution.buckets.map(_.value) should contain allOf(22L, 33L, 12L) + mergedRangeSampler.distribution.buckets.map(_.value) should contain allOf(22L, 33L, 12L) + } + } + + "produce a snapshot when enough data has been accumulated" in { + val accumulator = newAccumulator(15, 1) + accumulator.add(fiveSecondsOne) shouldBe empty + accumulator.add(fiveSecondsTwo) shouldBe empty + + val snapshotOne = accumulator.add(fiveSecondsThree).value + snapshotOne.from shouldBe fiveSecondsOne.from + snapshotOne.to shouldBe fiveSecondsThree.to + + val mergedHistogram = snapshotOne.metrics.histograms.head + val mergedRangeSampler = snapshotOne.metrics.rangeSamplers.head + snapshotOne.metrics.counters.head.value shouldBe(67) + snapshotOne.metrics.gauges.head.value shouldBe(12) + mergedHistogram.distribution.buckets.map(_.value) should contain allOf(22L, 33L, 12L) + mergedRangeSampler.distribution.buckets.map(_.value) should contain allOf(22L, 33L, 12L) + + val emptySnapshot = accumulator.peek() + emptySnapshot.metrics.histograms shouldBe empty + emptySnapshot.metrics.rangeSamplers shouldBe empty + emptySnapshot.metrics.gauges shouldBe empty + emptySnapshot.metrics.counters shouldBe empty + + accumulator.add(fiveSecondsFour) shouldBe empty + } + } + + val zeroTime = Kamon.clock().instant().truncatedTo(ChronoUnit.SECONDS) + + val fiveSecondsOne = PeriodSnapshot(zeroTime, zeroTime.plusSeconds(5), createMetricsSnapshot(22)) + val fiveSecondsTwo = PeriodSnapshot(zeroTime.plusSeconds(5), zeroTime.plusSeconds(10), createMetricsSnapshot(33)) + val fiveSecondsThree = PeriodSnapshot(zeroTime.plusSeconds(10), zeroTime.plusSeconds(15), createMetricsSnapshot(12)) + val fiveSecondsFour = PeriodSnapshot(zeroTime.plusSeconds(15), zeroTime.plusSeconds(20), createMetricsSnapshot(37)) + + val almostThreeSeconds = PeriodSnapshot(zeroTime, zeroTime.plusSeconds(3).minusMillis(1), createMetricsSnapshot(22)) + val threeSeconds = PeriodSnapshot(zeroTime, zeroTime.plusSeconds(3), createMetricsSnapshot(22)) + val fourSeconds = PeriodSnapshot(zeroTime, zeroTime.plusSeconds(4), createMetricsSnapshot(22)) + val nineSeconds = PeriodSnapshot(zeroTime, zeroTime.plusSeconds(9), createMetricsSnapshot(22)) + val tenSeconds = PeriodSnapshot(zeroTime, zeroTime.plusSeconds(10), createMetricsSnapshot(36)) + + + def newAccumulator(duration: Long, margin: Long) = + new PeriodSnapshotAccumulator(Duration.ofSeconds(duration), Duration.ofSeconds(margin)) + + def createMetricsSnapshot(value: Long) = MetricsSnapshot( + histograms = Seq(createDistributionSnapshot(s"histogram", Map("metric" -> "histogram"), MeasurementUnit.time.microseconds, DynamicRange.Fine)(value)), + rangeSamplers = Seq(createDistributionSnapshot(s"gauge", Map("metric" -> "gauge"), MeasurementUnit.time.microseconds, DynamicRange.Default)(value)), + gauges = Seq(createValueSnapshot(s"gauge", Map("metric" -> "gauge"), MeasurementUnit.information.bytes, value)), + counters = Seq(createValueSnapshot(s"counter", Map("metric" -> "counter"), MeasurementUnit.information.bytes, value)) + ) + + def createValueSnapshot(metric: String, tags: Map[String, String], unit: MeasurementUnit, value: Long): MetricValue = { + MetricValue(metric, tags, unit, value) + } + + def createDistributionSnapshot(metric: String, tags: Map[String, String], unit: MeasurementUnit, dynamicRange: DynamicRange)(values: Long*): MetricDistribution = { + val histogram = Kamon.histogram(metric, unit, dynamicRange).refine(tags) + values.foreach(histogram.record) + val distribution = histogram.distribution(resetState = true) + MetricDistribution(metric, tags, unit, dynamicRange, distribution) + } + + override protected def beforeAll(): Unit = { + applyConfig("kamon.metric.tick-interval = 10 seconds") + } +} -- cgit v1.2.3