Skip to content
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

Suppress instrumentation upon calling ActivityExporter.ExportAsync #977

Conversation

alanwest
Copy link
Member

@alanwest alanwest commented Aug 3, 2020

Reopening this PR for visibility now that #960 is merged. I had originally opened this as #967

Keeping it in draft state for now because tomorrow I'll try to get a sense of performance overhead. If things look ok, I'll get some tests added, too.

@alanwest alanwest requested a review from a team August 3, 2020 03:50
@alanwest alanwest closed this Aug 3, 2020
@alanwest alanwest reopened this Aug 3, 2020
@alanwest alanwest marked this pull request as draft August 3, 2020 03:52
@@ -28,13 +28,29 @@ public abstract class ActivityProcessor
/// Activity start hook.
/// </summary>
/// <param name="activity">Instance of activity to process.</param>
public abstract void OnStart(Activity activity);
public void OnStart(Activity activity)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you open to renaming these? I think the more typical .NET-y style for this would be:

public void Start(Activity activity);
protected abstract void OnStart(Activity activity);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If others are open, I'm open to this. It does mean a changing the public API and would affect anyone out there that has created a custom processor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both ways are breaking, no?

Today:

public abstract void OnStart(Activity activity);

First version:

public void OnStart(Activity activity);
protected abstract void OnStartInternal(Activity activity);

Proposed version:

public void Start(Activity activity);
protected abstract void OnStart(Activity activity);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duh. 🤦 Yes, you're right.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could move this logic up into ActivityListener to avoid the break. Like what's I'm doing on #969. But I think this is a better long-term design. We don't want a lot of stuff in ActivityListener IMO. I was thinking once this is available, I would re-do how that is done to be in the Start or Stop implementation of base ActivityProcessor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally I would struggle with naming things. This time it is easier for me as the spec said we should use OnStart and OnEnd 😄.

Copy link
Member Author

@alanwest alanwest Aug 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ha! Yes I wondered about that right after submitting this question. Great! We won't touch it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanwest Separate PR works for me 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CodeBlanch no PR necessary 😄. As Reiley pointed out OnStart and OnEnd are the names specified in the spec for the public API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Sorry I didn't realize you changed them to Start/OnStart already. That works for me. I think if there wasn't a spec involved something like StartActivity/OnActivityStarted would be better (what you were getting at), but good to stay as close to the spec as possible.

public abstract void OnStart(Activity activity);
public void OnStart(Activity activity)
{
if (Sdk.SuppressInstrumentation)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering out loud here... should the SuppressInstrumentation check go in ActivityProcessor or should it be part of the ActivitySampler? If SuppressInstrumentation == true forced a lower sampling decision (None or PropagationOnly) that would be better for perf I think.

var result = await this.exporter.ExportAsync(this.batch, cancellationToken).ConfigureAwait(false);
Sdk.SuppressInstrumentation = previous;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider a try-finally block (or adding the using syntactical sugar) to avoid accidental context leakage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good call. I do like the using approach and had planned on going that route.

…umentation

# Conflicts:
#	examples/Console/TestConsoleExporter.cs
#	src/OpenTelemetry/Trace/ActivityProcessor.cs
#	src/OpenTelemetry/Trace/BatchingActivityProcessor.cs
#	src/OpenTelemetry/Trace/Internal/NoopActivityProcessor.cs
#	src/OpenTelemetry/Trace/SimpleActivityProcessor.cs
#	test/OpenTelemetry.Tests/Implementation/Trace/Config/ActivityProcessorPipelineTests.cs
@@ -114,8 +114,12 @@ protected virtual void Dispose(bool disposing)
this.disposed = true;
}

protected abstract void OnStartInternal(Activity activity);
protected virtual void OnStartInternal(Activity activity)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanwest The "Internal" in the name is kind of non-standard. I think we were talking about this before but I lost track. Could we not do?

public void Start(Activity activity)
protected virtual void OnStart(Activity activity)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked through the changes yet, imagine we have a chain of processors, checking SuppressInstrumentation on every single processor seems to be a big perf hit.

Copy link
Member Author

@alanwest alanwest Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanwest The "Internal" in the name is kind of non-standard. I think we were talking about this before but I lost track. Could we not do?

Agreed. I actually have this change staged up, just haven't pushed yet. Mostly just picked things up on the PR today and resolved the various conflicts.

I haven't looked through the changes yet, imagine we have a chain of processors, checking SuppressInstrumentation on every single processor seems to be a big perf hit.

Yes, I haven't spent enough time analyzing this yet. Earlier there was an idea from @CodeBlanch to see if this fits better in the sampler. Will consider this soon.

Copy link
Member

@reyang reyang Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given SuppressInstrumentation is used for folks to express whether they want the telemetry to be suppressed or not, how about the following approach:

  • If the code (app/lib) is using new ActivitySource.StartActivity, it will be responsible to checking the SuppressInstrumentation flag and not creating the activity if the flag is set.
    • In this way the sampler/processor/exporter won't receive the activity at all since it is not even created.
    • If the code is not performing the check, the code is faulty and we are noting doing another round of check in the processor - due to perf reason.
  • If the code is using legacy Activity, the adapter will be responsible to drop the activity before sending it to the sampler/processor.
    • In this way we gain perf since the adapter is only taking legacy activities.

Copy link
Member Author

@alanwest alanwest Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the code (app/lib) is using new ActivitySource.StartActivity, it will be responsible to checking the SuppressInstrumentation flag and not creating the activity if the flag is set.

This would require taking an OTel dependency. We want to avoid this need for library authors, no?

If the code is using legacy Activity, the adapter will be responsible to drop the activity before sending it to the sampler/processor.

Yes, this makes sense for the legacy route.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would worry a bit relying on others to do it perfectly. I'm envisioning a bunch of difficult-to-triage issues being opened when applications go into infinite loops. We could move the check into the ActivityListener? If SuppressInstrumentation is triggered, we return ActivityDataRequest.None from GetRequestedDataUsingContext (or whatever it ends up being called). If we do that, Activity is never created.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could move the check into the ActivityListener?

If perf looks good, putting the logic in the ActivityListener will work.

This would require taking an OTel dependency. We want to avoid this need for library authors, no?

Definitely yes. I think most library authors don't need to care about this (infinite loops). Only library authors who build libraries that can be used for exporting task will have to worry about it. Probably on for HTTP / TCP / UDP / Unix domain socket.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More data points:

  • In OpenTelemetry Python it seems to be fine to have only HTTP instrumentation doing the loop detection using SuppressInstrumentation, check this.
  • If only 5% of the scenario would use it, making it too generic and ask everyone to pay the perf tax could be a hard-sell (if the perf tax is low, for example 0.1%, I guess it'll be fine).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try on this on for size tomorrow:

  • Check in ActivityListener for the new ActivitySource way
  • And check in the adapter for the legacy

Then try to get some perf numbers. If we're not happy, then I'm definitely ok entertaining scoping this only to certain instrumentation HttpClient/HttpWebRequest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanwest FYI - I've shared a mini benchmark project which I used for refactor the provider/processor/pipeline #1039.

@@ -320,14 +320,18 @@ private async Task<int> ExportBatchAsync(CancellationToken cancellationToken)
this.batch.Add(nextActivity);
}

var result = await this.exporter.ExportAsync(this.batch, cancellationToken).ConfigureAwait(false);
if (result != ExportResult.Success)
using (Sdk.SuppressInstrumentation)
Copy link
Member Author

@alanwest alanwest Aug 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CodeBlanch @reyang

Uh oh this is a terrible bug waiting to happen. This should be:

using (Sdk.SuppressInstrumentation.Begin())

This bug would result in the static instance getting disposed. I think we need to rethink things a bit to prevent this from being possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+100

@alanwest
Copy link
Member Author

Closing and reopening this work in #1079

@alanwest alanwest closed this Aug 14, 2020
@alanwest alanwest deleted the alanwest/activity-processor-suppress-instrumentation branch August 24, 2020 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants