Skip to content

Commit 9624775

Browse files
beniwohlibasepi
andauthored
Implemented span compression algorithm (#1321)
* Implemented span compression algorithm See elastic/apm#432 * disable span compression by default in tests * make _try_to_compress_composite and _try_to_compress_regular side effect free * fix issue with compression buffer not being reported * fix some type hinting issues/errors * move traceparent initialization into Transaction constructor This cleans up a wart where `traceparent` is initialized to None, and needs to be set by the callee right after, as there is code that assumes `traceparent` to be set. * fix breakdown metrics calculation * fix breakdown metrics test we no longer only call `child_ended` for breakdown metrics * update duration of the composite span based on compressed spans * add docs for compressed span config options * clean up merge Co-authored-by: Colton Myers <colton.myers@gmail.com>
1 parent 4ccd10c commit 9624775

File tree

13 files changed

+536
-85
lines changed

13 files changed

+536
-85
lines changed

docs/configuration.asciidoc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,49 @@ To disable stack trace collection for spans completely, set the value to `0`.
691691
Except for the special values `-1` and `0`,
692692
this setting has to be provided in *<<config-format-duration, duration format>>*.
693693

694+
[float]
695+
[[config-span-compression-exact-match-max_duration]]
696+
==== `span_compression_exact_match_max_duration`
697+
698+
<<dynamic-configuration, image:./images/dynamic-config.svg[] >>
699+
700+
[options="header"]
701+
|============
702+
| Environment | Django/Flask | Default
703+
| `ELASTIC_APM_SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION` | `SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION` | `"50ms"`
704+
|============
705+
706+
Consecutive spans that are exact match and that are under this threshold will be compressed into a single composite span.
707+
This reduces the collection, processing, and storage overhead, and removes clutter from the UI.
708+
The tradeoff is that the DB statements of all the compressed spans will not be collected.
709+
710+
Two spans are considered exact matches if the following attributes are identical:
711+
* span name
712+
* span type
713+
* span subtype
714+
* destination resource (e.g. the Database name)
715+
716+
[float]
717+
[[config-span-compression-same-kind-max-duration]]
718+
==== `span_compression_same_kind_max_duration`
719+
720+
<<dynamic-configuration, image:./images/dynamic-config.svg[] >>
721+
722+
[options="header"]
723+
|============
724+
| Environment | Django/Flask | Default
725+
| `ELASTIC_APM_SPAN_COMPRESSION_SAME_KIND_MAX_DURATION` | `SPAN_COMPRESSION_SAME_KIND_MAX_DURATION` | `"5ms"`
726+
|============
727+
728+
Consecutive spans to the same destination that are under this threshold will be compressed into a single composite span.
729+
This reduces the collection, processing, and storage overhead, and removes clutter from the UI.
730+
The tradeoff is that metadata such as database statements of all the compressed spans will not be collected.
731+
732+
Two spans are considered to be of the same kind if the following attributes are identical:
733+
* span type
734+
* span subtype
735+
* destination resource (e.g. the Database name)
736+
694737
[float]
695738
[[config-api-request-size]]
696739
==== `api_request_size`

elasticapm/conf/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,18 @@ class Config(_ConfigBase):
579579
],
580580
type=int,
581581
)
582+
span_compression_exact_match_max_duration = _ConfigValue(
583+
"span_compression_exact_match_max_duration",
584+
default=5,
585+
validators=[duration_validator],
586+
type=int,
587+
)
588+
span_compression_same_kind_max_duration = _ConfigValue(
589+
"span_compression_exact_match_max_duration",
590+
default=5,
591+
validators=[duration_validator],
592+
type=int,
593+
)
582594
collect_local_variables = _ConfigValue("COLLECT_LOCAL_VARIABLES", default="errors")
583595
source_lines_error_app_frames = _ConfigValue("SOURCE_LINES_ERROR_APP_FRAMES", type=int, default=5)
584596
source_lines_error_library_frames = _ConfigValue("SOURCE_LINES_ERROR_LIBRARY_FRAMES", type=int, default=5)

elasticapm/instrumentation/packages/dbapi2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ def _trace_sql(self, method, sql, params, action=QUERY_ACTION):
237237
span_action=action,
238238
extra={"db": {"type": "sql", "statement": sql_string}, "destination": self._self_destination_info},
239239
skip_frames=1,
240+
leaf=True,
240241
) as span:
241242
if params is None:
242243
result = method(sql)

elasticapm/instrumentation/packages/httpcore.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ def call(self, module, method, wrapped, instance, args, kwargs):
9696
span_id=parent_id, trace_options=TracingOptions(recorded=True)
9797
)
9898
self._set_disttracing_headers(headers, trace_parent, transaction)
99+
if leaf_span:
100+
leaf_span.dist_tracing_propagated = True
99101
response = wrapped(*args, **kwargs)
100102
if len(response) > 4:
101103
# httpcore < 0.11.0

elasticapm/instrumentation/packages/httplib2.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ def call(self, module, method, wrapped, instance, args, kwargs):
9393
span_id=parent_id, trace_options=TracingOptions(recorded=True)
9494
)
9595
self._set_disttracing_headers(params["headers"], trace_parent, transaction)
96+
if leaf_span:
97+
leaf_span.dist_tracing_propagated = True
9698

9799
response, content = wrapped(*args, **kwargs)
98100
if span.context:

elasticapm/instrumentation/packages/urllib.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ def call(self, module, method, wrapped, instance, args, kwargs):
9696
span_id=parent_id, trace_options=TracingOptions(recorded=True)
9797
)
9898
self._set_disttracing_headers(request_object, trace_parent, transaction)
99+
if leaf_span:
100+
leaf_span.dist_tracing_propagated = True
99101
response = wrapped(*args, **kwargs)
100102
if response:
101103
status = getattr(response, "status", None) or response.getcode() # Python 2 compat

elasticapm/instrumentation/packages/urllib3.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ def call(self, module, method, wrapped, instance, args, kwargs):
127127
span_id=parent_id, trace_options=TracingOptions(recorded=True)
128128
)
129129
args, kwargs = update_headers(args, kwargs, instance, transaction, trace_parent)
130+
if leaf_span:
131+
leaf_span.dist_tracing_propagated = True
130132
response = wrapped(*args, **kwargs)
131133
if response:
132134
if span.context:

0 commit comments

Comments
 (0)