Skip to content

Latest commit

 

History

History
213 lines (172 loc) · 9.77 KB

async-support-net.mdx

File metadata and controls

213 lines (172 loc) · 9.77 KB
title tags metaDescription redirects freshnessValidatedDate
Async support in .NET
Agents
NET agent
Other features
How to activate asynchronous mode with New Relic's .NET agent, plus a summary of new, disabled, or unavailable features when async mode is active.
/docs/agents/net-agent/other-features/async-support-net
/docs/agents/net-agent/features/enable-async-net-agent
/docs/agents/net-agent/features/try-out-net-agents-async-mode
/docs/agents/net-agent/features/async-mode-net
/docs/agents/net-agent/features/async-support-net
/docs/agents/net-agent/additional-installation/async-support-net
never

New Relic's .NET agent automatically includes asynchronous framework instrumentation as of agent version 6.0. With the standard async-await pattern, introduced in .NET 4.5, calls to async methods can return even though the work being done in the called method is still in progress. The .NET agent observes this in-progress asynchronous work and waits for it to complete before recording timings.

Features supporting async instrumentation [#features]

With the addition of async support, additional features are available in our .NET agent. However, as part of this enhancement, a small number of features previously provided by the agent currently are not available. Except as noted, the agent does not instrument async methods for any of the other supported frameworks for the .NET agent.

The agent instruments these `HttpClient` async methods:
* `SendAsync`
* `GetAsync`
* `PostAsync`
* `PutAsync`
* `DeleteAsync`
* `GetStringAsync`
* `GetStreamAsync`
* `GetByteArrayAsync`
The agent instruments these `RestClient` async methods:
* `ExecuteTaskAsync`
* `ExecuteGetTaskAsync`
* `ExecutePostTaskAsync`

<Collapser id="SqlCommand" title="SqlCommand async methods"

The agent instruments these `SqlCommand` async methods:

* `ExecuteReaderAsync`
* `ExecuteNonQueryAsync`
* `ExecuteScalarAsync`
* `ExecuteXmlReaderAsync`

<Collapser id="SqlDataReader" title="SqlDataReader async methods"

The agent instruments these `SqlDataReader` async methods:

* `NextResultAsync`
* `ReadAsync`

<Collapser id="NpgsqlCommand" title="NpgsqlCommand async methods (Postgres)"

The agent instruments these `NpgsqlCommand` async methods (Postgres):

* `ExecuteReaderAsync`
* `ExecuteNonQueryAsync`
* `ExecuteScalarAsync`

<Collapser id="custom-instrumentation" title="Custom instrumentation"

The .NET agent supports [custom instrumentation](/docs/agents/net-agent/instrumentation/net-custom-transactions#example-custom-txn-async) of your own async methods.

Known limitations [#known-issues]

Here is a summary of known limitations for async instrumentation with our .NET agent.

It is expected for response time to be less than the total time spent in `async`-`await` usage scenerios. Consider the following code example for a web endpoint:
```cs
async Task<string> WebEndpointExample() 
{
    await DoSomethingForSomeSecondsAsync(5); //kick off a 5-second-work to be done.
    return "Complete";
}

[Trace]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task DoSomethingForSomeSecondsAsync(int seconds)
{
    await Task.Delay(TimeSpan.FromSeconds(seconds));
}
```

In this code example, it takes approximately 5 seconds for the `WebEndpointExample` to complete, so the response time for the transaction that represents the request to the `WebEndpointExample` endpoint will be approximately 5 seconds.

The agent also captures the "busy" time (the time that the instrumented method is actually executing) of each individual segment that constitutes the transaction. They are `WebEndpointExample` and `DoSomethingForSomeSecondsAsync`. Ideally, the total execution time of the two segments is equal to the response time (approximately 5 seconds).

It is easy to see that the execution time of `DoSomethingForSomeSecondsAsync` is 5 seconds. However, the execution time of the `WebEndpointExample` should be close to 0 seconds. (It doesn't do any work; it `await`s for the `DoSomethingForSomeSecondsAsync` to complete.)

However, the agent still measures its execution time as approximately 5 seconds. This is due to the agent's inability to detect blocked time (not CPU time) when a method is `await`ing for another. Hence the total time is reported as 10 seconds, which is greater than the response time (approximately 5 seconds).

At the same time, the agent cannot assume calling to `async` methods would always block the caller for the entire time. The next example demonstrates this:

```cs
async Task<string> WebEndpointExample()
{
    var task = DoSomethingForSomeSecondsAsync(5); //kick off a 5-second-work to be done.

    //Do something less than 5 seconds here.

    await task;
    return "Complete";
}

[Trace]
[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task DoSomethingForSomeSecondsAsync(int seconds)
{
    await Task.Delay(TimeSpan.FromSeconds(seconds));
}
```

In this example, the response time is still approximately 5 seconds, but the actual execution time of the `WebEndpointExample` is no longer approximately 0.

<Collapser id="asp-pipeline" title="Requires updated ASP pipeline"

The .NET agent will not instrument async methods if the legacy ASP pipeline is present. Since Microsoft replaced the legacy ASP pipeline well before async methods were introduced, this issue typically only affects applications created under .NET Framework 4.0 or lower, then migrated to .NET Framework 4.5 or higher.

To see if this issue affects your application, and how to resolve it if it does, [review the troubleshooting procedures](/docs/agents/net-agent/troubleshooting/missing-async-metrics).

<Collapser id="task-not-void" title="Return type Task requirements"

The .NET agent does not support instrumentation of async methods that have return type of anything other than `Task` or `Task<T>`. The agent does not support `async void` methods.

For more information, refer to the Microsoft documentation about [async return types](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types):

* [`Task<TResult>`](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types#BKMK_TaskTReturnType) return type
* [Async void](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types#BKMK_VoidReturnType)
* [Generalized async return types](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types#generalized-async-return-types-and-valuetasktresult)

<Collapser id="begin-end-style" title="No instrumentation for begin and end style"

The .NET agent does not instrument any .NET methods that use the `begin*` and `end*` style, except for [WCF applications](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-call-wcf-service-operations-asynchronously). Outside of this exception, if your application calls these types of methods, the agent will not create segments for them. However, the rest of your transactions and segments will be created correctly.

<Collapser id="manual-created-threads-capture" title="Scoped metrics/segments in manually created threads"

The .NET agent does not capture scoped metrics or segments within threads that are manually created by your application.

<Collapser id="async-await" title="Instrumented async methods must use await, not Task.Result"

If your application calls instrumented async methods, use `await` rather than `Task` related methods like `Task.Result()` to wait for the results. Otherwise, instrumentation may not work properly.

In general, avoid using `Task.Result()` when calling async methods. It can lead to [deadlocks](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html).

<Collapser id="continue-with-timing" title="ContinueWith block may affect timing measurements"

If you add your own `ContinueWith({})` block to the promise returned by an instrumented async method, it may affect timing measurements reported by the instrumentation. For example, the time may include the time your `ContinueWith` takes to execute.

<Collapser id="iis-wcf-nesting" title="Problem with nesting in IIS-hosted WCF apps"

IIS-hosted [WCF services](/docs/agents/net-agent/instrumentation/instrumenting-wcf-applications) do not properly nest the <DNT>**WCF**</DNT> segment under the `ExecuteRequestHandler` segment. The two segments will appear to be siblings within a transaction trace, even though their reported [total time](/docs/data-analysis/user-interface-functions/response-time#response-time-total-time) will be accurate.

<Collapser id="stack-traces-segments" title="Segments don't auto-create stack traces"

Segments in a transaction trace will not generate stack traces automatically, even if they run longer than `transaction_tracer.stack_trace_threshold`.