-
Notifications
You must be signed in to change notification settings - Fork 775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DRAFT] fixing InMemoryExporter & Metrics bug. new class: ExportableMetricCopy. new ctor: InMemoryExporter(Func) #3198
[DRAFT] fixing InMemoryExporter & Metrics bug. new class: ExportableMetricCopy. new ctor: InMemoryExporter(Func) #3198
Conversation
Codecov Report
@@ Coverage Diff @@
## main #3198 +/- ##
==========================================
- Coverage 85.48% 85.19% -0.29%
==========================================
Files 261 262 +1
Lines 9410 9452 +42
==========================================
+ Hits 8044 8053 +9
- Misses 1366 1399 +33
|
src/OpenTelemetry.Exporter.InMemory/InMemoryExporterMetricsExtensions.cs
Outdated
Show resolved
Hide resolved
update tests to use InMemoryExporter(Func).
…yMothra/opentelemetry-dotnet into 2361_ExportableMetricCopy
src/OpenTelemetry.Exporter.InMemory/OpenTelemetry.Exporter.InMemory.csproj
Outdated
Show resolved
Hide resolved
src/OpenTelemetry.Exporter.InMemory/InMemoryExporterMetricsExtensions.cs
Show resolved
Hide resolved
.AddInMemoryExporter(metrics) | ||
.AddInMemoryExporter(exportFunc: batch => | ||
{ | ||
foreach (var metric in batch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way (e.g. list.AddRange) to reduce the boilerplate code (ideally keeping it a one liner)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had that same thought while i was writing this. i'll spend some time on this today
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reyang
AddRange doesn't work here because there's not a built in way to convert Batch to IEnumerable.
If we need to convert Batch to something, I would prefer to convert it to a List.
My initial idea was to add a helper method to the Batch class itself.
Instead, my most recent change adds a helper method to only this test class.
See: private static List<Metric> BatchToList(Batch<Metric> batch)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks better, still have room for simplification. Could you make it even simpler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The simplest would be to add an additional Extension method that accepts Action<T>
. I didn't think I could get away with adding both Func and Action to the PublicApi and between the two I chose Func. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One simple way would be having an extension method just in this test project - AddInMemoryExporter(some list type)
which handles the boilerplate job.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put a new extension method AddInMemoryExporter(List<Metric>)
in Tests.Shared
so it can be reused by multiple projects.
This helps reduce the number of changes in this PR, the individual Prometheus and OTLP unit tests don't need any changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we want to encourage more use of this copy approach given the fact that Metric can be changed after the export.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with some suggestions (clean up, reduce boilerplate, etc.).
This PR got very large. |
…yMothra/opentelemetry-dotnet into 2361_ExportableMetricCopy
test/OpenTelemetry.Tests/Trace/BatchExportActivityProcessorTest.cs
Outdated
Show resolved
Hide resolved
…yMothra/opentelemetry-dotnet into 2361_ExportableMetricCopy
@cijothomas @CodeBlanch, this is ready for review :) |
|
||
public InMemoryExporter(ICollection<T> exportedItems) | ||
{ | ||
if (typeof(T) == typeof(Metrics.Metric)) | ||
{ | ||
throw new NotSupportedException("Exported Metrics are not trustworthy because they can continue to be updated after export. Recommend use ExportableMetricCopy."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure I follow the reason behind this throw...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is by design to force the use of the Copy.
The thought behind this was to prevent users from creating the InMemoryExporter<Metric>
because the Metric
is known to be faulty. #3198 (comment)
I've documented some scenarios where the Metric
can and cannot be trusted: #2361 (comment)
That being said, Prometheus and OTLP have some units tests where they need the actual Metric
to provide to their own internals. (documented in the Summary above).
I'm eager to hear your perspective on this!
I'm trying to find the happy medium to support all these use cases while protecting users.
If we decide to allow users to create InMemoryExporter<Metric>
we also need to inform/educate users on the risks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to prevent users from using the one. A lot of tests are already using it, and working fine. (they understand the documented limitation of Metric that it may be updated after Export() call, but they are orchestrating things so as to not be affected)
This can be additional option, for InMemory users, who wants to test metrics, without being affected by the limitation that the memory could be updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To unblock progress - lets split PR into sub Prs.
- Add the additional option in InMemoryExporter to use MetricCopy, leave existing functionality untouched. i.e don't change unit tests, except the one breaking due to the metric-being-updated-after-export issue.
- We can use the SIG meeting to see if there are strong reasons favoring/against keeping the current exporter, and based on that keep it or remove it.
@@ -83,7 +83,7 @@ public void Setup() | |||
this.bounds[i] = i * MaxValue / this.bounds.Length; | |||
} | |||
|
|||
var exportedItems = new List<Metric>(); | |||
var exportedItems = new List<ExportableMetricCopy>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it required to force every test to use the new MetricCopy unless they need to rely on it?
Or is it by design that InMemoryExporter will force one to use the MetricCopy option only? What I don't need/care about the fact that Metric could be update after export() call is done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, current approach was to force the use of the Copy.
See the comment above for more details #3198 (comment)
This PR was marked stale due to lack of activity and will be closed in 7 days. Commenting or Pushing will instruct the bot to automatically remove the label. This bot runs once per day. |
Fixes #2361
Changes
ExportableMetricCopy
. This is used for a strategic selective copy of the minimum members to facilitate unit testing.InMemoryExporter
to acceptFunc<Batch<T>, ExportResult
.This allows the export behavior to be fully replaced and provides greater flexibility for unit tests.
AddInMemoryExporter(ICollection<ExportableMetricCopy>)
added toInMemoryExporterMetricsExtensions
.Please provide a brief description of the changes here.
Metric
instances are reused by design.As a side effect, the
InMemoryExporter
will repeatedly export duplicates ofMetric
instances.Because of this,
Metric
s can be modified after export. See also: #2361 (comment)This change introduces a new class
ExportableMetricCopy
to be used in unit tests.This class is a collection of minimum members needed for existing unit tests.
An end user has the option to use either this copy or the original
Metric
.This change also introduces a new ctor
public InMemoryExporter(Func<Batch<T>, ExportResult> exportFunc)
.This is needed to export a type that differs from the
Batch
type, such as convertingMetric
toExportableMetricCopy
.This is also used by other tests:
PrometheusSerializerTests
needsMetric
to testPrometheusSerializer.WriteMetric()
OtlpMetricsExporterTests
needsBatch<Metric>
to testExportMetricsServiceRequest.AddMetrics()
For significant contributions please make sure you have completed the following items:
CHANGELOG.md
updated for non-trivial changesTodo
Alternate approaches investigated.
Metric
has many obstacles. (NotSerializeable
, missing parameterless constructors, significant MetricPoint buffer could cause OOM).Metric
class for a selective copy would give a false impression of a deep-clone and introduces a LARGE maintenance tax.