Repository: elastic/apm-agent-python

Issue:
[META 505] Stop recording transaction.duration.sum.us,count metrics
Implement https://github.com/elastic/apm/issues/505

Also remove `transaction.breakdown.count` (see linked issue above)


Discussion:

Resolving patch:
diff --git a/elasticapm/metrics/sets/transactions.py b/elasticapm/metrics/sets/transactions.py
deleted file mode 100644
--- a/elasticapm/metrics/sets/transactions.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#  BSD 3-Clause License
-#
-#  Copyright (c) 2019, Elasticsearch BV
-#  All rights reserved.
-#
-#  Redistribution and use in source and binary forms, with or without
-#  modification, are permitted provided that the following conditions are met:
-#
-#  * Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-#  * Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-#  * Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import absolute_import
-
-from elasticapm.metrics.base_metrics import SpanBoundMetricSet
-
-
-class TransactionsMetricSet(SpanBoundMetricSet):
-    pass
diff --git a/elasticapm/traces.py b/elasticapm/traces.py
--- a/elasticapm/traces.py
+++ b/elasticapm/traces.py
@@ -214,23 +214,10 @@ def __init__(
             )
         except (LookupError, AttributeError):
             self._breakdown = None
-        try:
-            self._transaction_metrics = self.tracer._agent._metrics.get_metricset(
-                "elasticapm.metrics.sets.transactions.TransactionsMetricSet"
-            )
-        except (LookupError, AttributeError):
-            self._transaction_metrics = None
         super(Transaction, self).__init__(start=start)
 
     def end(self, skip_frames: int = 0, duration: Optional[float] = None):
         super().end(skip_frames, duration)
-        if self._transaction_metrics:
-            self._transaction_metrics.timer(
-                "transaction.duration",
-                reset_on_collect=True,
-                unit="us",
-                **{"transaction.name": self.name, "transaction.type": self.transaction_type},
-            ).update(int(self.duration * 1000000))
         if self._breakdown:
             for (span_type, span_subtype), timer in compat.iteritems(self._span_timers):
                 labels = {
@@ -246,7 +233,6 @@ def end(self, skip_frames: int = 0, duration: Optional[float] = None):
                 )
             labels = {"transaction.name": self.name, "transaction.type": self.transaction_type}
             if self.is_sampled:
-                self._breakdown.counter("transaction.breakdown.count", reset_on_collect=True, **labels).inc()
                 self._breakdown.timer(
                     "span.self_time",
                     reset_on_collect=True,


Test patch:
diff --git a/tests/metrics/breakdown_tests.py b/tests/metrics/breakdown_tests.py
--- a/tests/metrics/breakdown_tests.py
+++ b/tests/metrics/breakdown_tests.py
@@ -41,27 +41,16 @@ def test_bare_transaction(elasticapm_client):
     elasticapm_client.end_transaction("test", "OK", duration=5)
     breakdown = elasticapm_client._metrics.get_metricset("elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
     data = list(breakdown.collect())
-    assert len(data) == 2
+    assert len(data) == 1
     asserts = 0
     for elem in data:
-        if "transaction.breakdown.count" in elem["samples"]:
-            assert elem["samples"]["transaction.breakdown.count"]["value"] == 1
-            assert elem["transaction"] == {"name": "test", "type": "request"}
-            asserts += 1
-        elif "span.self_time.sum.us" in elem["samples"]:
+        if "span.self_time.sum.us" in elem["samples"]:
             assert elem["samples"]["span.self_time.count"]["value"] == 1
             assert elem["samples"]["span.self_time.sum.us"]["value"] == 5000000
             assert elem["transaction"] == {"name": "test", "type": "request"}
             assert elem["span"] == {"subtype": "", "type": "app"}
             asserts += 1
-    assert asserts == 2
-
-    transaction_metrics = elasticapm_client._metrics.get_metricset(
-        "elasticapm.metrics.sets.transactions.TransactionsMetricSet"
-    )
-    transaction_data = list(transaction_metrics.collect())
-    assert len(transaction_data) == 1
-    assert transaction_data[0]["samples"]["transaction.duration.sum.us"]["value"] == 5000000
+    assert asserts == 1
 
 
 def test_single_span(elasticapm_client):
@@ -71,14 +60,10 @@ def test_single_span(elasticapm_client):
     elasticapm_client.end_transaction("test", "OK", duration=15)
     breakdown = elasticapm_client._metrics.get_metricset("elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
     data = list(breakdown.collect())
-    assert len(data) == 3
+    assert len(data) == 2
     asserts = 0
     for elem in data:
-        if "transaction.breakdown.count" in elem["samples"]:
-            assert elem["samples"]["transaction.breakdown.count"]["value"] == 1
-            assert elem["transaction"] == {"name": "test", "type": "request"}
-            asserts += 1
-        elif "span.self_time.sum.us" in elem["samples"]:
+        if "span.self_time.sum.us" in elem["samples"]:
             if elem["span"] == {"type": "app", "subtype": ""}:
                 assert elem["transaction"] == {"name": "test", "type": "request"}
                 assert elem["samples"]["span.self_time.sum.us"]["value"] == 10000000
@@ -89,14 +74,7 @@ def test_single_span(elasticapm_client):
                 assert elem["samples"]["span.self_time.sum.us"]["value"] == 5000000
                 assert elem["transaction"] == {"name": "test", "type": "request"}
                 asserts += 1
-    assert asserts == 3
-
-    transaction_metrics = elasticapm_client._metrics.get_metricset(
-        "elasticapm.metrics.sets.transactions.TransactionsMetricSet"
-    )
-    transaction_data = list(transaction_metrics.collect())
-    assert len(transaction_data) == 1
-    assert transaction_data[0]["samples"]["transaction.duration.sum.us"]["value"] == 15000000
+    assert asserts == 2
 
 
 def test_nested_spans(elasticapm_client):
@@ -109,14 +87,10 @@ def test_nested_spans(elasticapm_client):
     elasticapm_client.end_transaction("test", "OK", duration=25)
     breakdown = elasticapm_client._metrics.get_metricset("elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
     data = list(breakdown.collect())
-    assert len(data) == 4
+    assert len(data) == 3
     asserts = 0
     for elem in data:
-        if "transaction.breakdown.count" in elem["samples"]:
-            assert elem["samples"]["transaction.breakdown.count"]["value"] == 1
-            assert elem["transaction"] == {"name": "test", "type": "request"}
-            asserts += 1
-        elif "span.self_time.sum.us" in elem["samples"]:
+        if "span.self_time.sum.us" in elem["samples"]:
             if elem["span"] == {"type": "app", "subtype": ""}:
                 assert elem["transaction"] == {"name": "test", "type": "request"}
                 assert elem["samples"]["span.self_time.sum.us"]["value"] == 10000000
@@ -132,14 +106,7 @@ def test_nested_spans(elasticapm_client):
                 assert elem["samples"]["span.self_time.sum.us"]["value"] == 5000000
                 assert elem["transaction"] == {"name": "test", "type": "request"}
                 asserts += 1
-    assert asserts == 4
-
-    transaction_metrics = elasticapm_client._metrics.get_metricset(
-        "elasticapm.metrics.sets.transactions.TransactionsMetricSet"
-    )
-    transaction_data = list(transaction_metrics.collect())
-    assert len(transaction_data) == 1
-    assert transaction_data[0]["samples"]["transaction.duration.sum.us"]["value"] == 25000000
+    assert asserts == 3
 
 
 def test_explicit_app_span(elasticapm_client):
@@ -149,28 +116,21 @@ def test_explicit_app_span(elasticapm_client):
     elasticapm_client.end_transaction("test", "OK")
     breakdown = elasticapm_client._metrics.get_metricset("elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
     data = list(breakdown.collect())
-    assert len(data) == 2
+    assert len(data) == 1
     asserts = 0
     for elem in data:
-        if "transaction.breakdown.count" in elem["samples"]:
-            assert elem["samples"]["transaction.breakdown.count"]["value"] == 1
-            assert elem["transaction"] == {"name": "test", "type": "request"}
-            asserts += 1
-        elif "span.self_time.sum.us" in elem["samples"]:
+        if "span.self_time.sum.us" in elem["samples"]:
             if elem["span"] == {"type": "app", "subtype": ""}:
                 assert elem["transaction"] == {"name": "test", "type": "request"}
                 assert elem["samples"]["span.self_time.count"]["value"] == 2
                 asserts += 1
-    assert asserts == 2
+    assert asserts == 1
 
 
 @pytest.mark.parametrize("elasticapm_client", [{"breakdown_metrics": False}], indirect=True)
 def test_disable_breakdowns(elasticapm_client):
     with pytest.raises(LookupError):
         elasticapm_client._metrics.get_metricset("elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
-    transaction_metrics = elasticapm_client._metrics.get_metricset(
-        "elasticapm.metrics.sets.transactions.TransactionsMetricSet"
-    )
     with mock.patch("elasticapm.traces.BaseSpan.child_started") as mock_child_started, mock.patch(
         "elasticapm.traces.Transaction.track_span_duration"
     ) as mock_track_span_duration:
@@ -181,10 +141,6 @@ def test_disable_breakdowns(elasticapm_client):
         elasticapm_client.end_transaction("test", "OK", duration=5)
         assert mock_child_started.call_count == 0
         assert mock_track_span_duration.call_count == 0
-    # transaction duration should still be captured
-    data = list(transaction_metrics.collect())
-    assert len(data) == 1
-    assert data[0]["samples"]["transaction.duration.sum.us"]["value"] == 5000000
 
 
 def test_metrics_reset_after_collect(elasticapm_client):
@@ -193,19 +149,15 @@ def test_metrics_reset_after_collect(elasticapm_client):
         pass
     elasticapm_client.end_transaction("test", "OK", duration=15)
     breakdown = elasticapm_client._metrics.get_metricset("elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
-    transaction_metrics = elasticapm_client._metrics.get_metricset(
-        "elasticapm.metrics.sets.transactions.TransactionsMetricSet"
-    )
-    for metricset in (breakdown, transaction_metrics):
-        for labels, c in compat.iteritems(metricset._counters):
-            assert c.val != 0
-        for labels, t in compat.iteritems(metricset._timers):
-            assert t.val != (0, 0)
-        list(metricset.collect())
-        for labels, c in compat.iteritems(metricset._counters):
-            assert c.val == 0
-        for labels, t in compat.iteritems(metricset._timers):
-            assert t.val == (0, 0)
+    for labels, c in compat.iteritems(breakdown._counters):
+        assert c.val != 0
+    for labels, t in compat.iteritems(breakdown._timers):
+        assert t.val != (0, 0)
+    list(breakdown.collect())
+    for labels, c in compat.iteritems(breakdown._counters):
+        assert c.val == 0
+    for labels, t in compat.iteritems(breakdown._timers):
+        assert t.val == (0, 0)
 
 
 def test_multiple_transactions(elasticapm_client):
@@ -219,11 +171,7 @@ def test_multiple_transactions(elasticapm_client):
     data = list(breakdown.collect())
     asserts = 0
     for elem in data:
-        if "transaction.breakdown.count" in elem["samples"]:
-            assert elem["samples"]["transaction.breakdown.count"]["value"] == 2
-            assert elem["transaction"] == {"name": "test", "type": "request"}
-            asserts += 1
-        elif "span.self_time.sum.us" in elem["samples"]:
+        if "span.self_time.sum.us" in elem["samples"]:
             if elem["span"] == {"type": "app", "subtype": ""}:
                 assert elem["transaction"] == {"name": "test", "type": "request"}
                 # precision lost due to float arithmetic
@@ -235,12 +183,4 @@ def test_multiple_transactions(elasticapm_client):
                 assert 9999999 <= elem["samples"]["span.self_time.sum.us"]["value"] <= 10000000
                 assert elem["samples"]["span.self_time.count"]["value"] == 2
                 asserts += 1
-    assert asserts == 3
-
-    transaction_metrics = elasticapm_client._metrics.get_metricset(
-        "elasticapm.metrics.sets.transactions.TransactionsMetricSet"
-    )
-    transaction_data = list(transaction_metrics.collect())
-    assert len(transaction_data) == 1
-    assert 19999999 <= transaction_data[0]["samples"]["transaction.duration.sum.us"]["value"] <= 20000000
-    assert transaction_data[0]["samples"]["transaction.duration.count"]["value"] == 2
+    assert asserts == 2