Skip to content

Commit f51d779

Browse files
authored
Fix exception in Kafka on .NET Framework (#8366)
## Summary of changes Ran Kafka locally as I was attempting to port it to Testcontainers and was actually get exceptions / errors on it, this resolves them (or at least it seems to resolve them). Also noticed them in Error Tracking, but only a single instance, but I'm not 100% sure if it was the same, Error Tracking is linked in the linked Jira Ticket. ## Reason for change Previously this was using `IsCompletedSuccessfully` and then doing a cast and enumeration of all properties and was failing with the following error as `IsCompletedSuccessfully` isn't available on .NET Framework: ``` 2026-03-24 15:43:11.768 -04:00 [WRN] Error extracting cluster_id from Kafka metadata Datadog.Trace.DuckTyping.DuckTypePropertyOrFieldNotFoundException: The property or field 'IsCompletedSuccessfully' for the proxy property 'IsCompletedSuccessfully' was not found in the instance of type 'System.Threading.Tasks.Task`1[[Confluent.Kafka.Admin.DescribeClusterResult, Confluent.Kafka, Version=2.6.1.0, Culture=neutral, PublicKeyToken=12c514ca49093d1e]]'. at Datadog.Trace.DuckTyping.DuckTypePropertyOrFieldNotFoundException.Throw(String name, String duckAttributeName, Type type) at Datadog.Trace.DuckTyping.DuckType.CreateProperties(TypeBuilder proxyTypeBuilder, Type proxyDefinitionType, Type targetType, FieldInfo instanceField) at Datadog.Trace.DuckTyping.DuckType.CreateProxyType(Type proxyDefinitionType, Type targetType, Boolean dryRun) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Datadog.Trace.DuckTyping.DuckType.CreateTypeResult.ThrowOnError[T](Object instance) at Datadog.Trace.DuckTyping.DuckType.CreateTypeResult.CreateInstance[T](Object instance) at Datadog.Trace.DuckTyping.DuckType.CreateCache`1.Create(Object instance) at Datadog.Trace.ClrProfiler.AutoInstrumentation.Kafka.KafkaHelper.DescribeClusterWithTimeout(IAdminClient adminClient, Type describeClusterOptionsType) at Datadog.Trace.ClrProfiler.AutoInstrumentation.Kafka.KafkaHelper.GetClusterId(String bootstrapServers, Object clientInstance) { MachineName: ".", Process: "[59476 Samples.Kafka]", AppDomain: "[1 Samples.Kafka.exe]", TracerVersion: "3.41.0.0" } ``` ## Implementation details Remove `IsCompletedSuccessfully` from `IDuckTypeTask` as it isn't there on .NET Framework, attempted initially to go with TaskStatus but this caused failures for xUnit/MS Test integration tests as those appear to be using ValueTask, which doesn't have it. Removing `IsCompletedSuccessfully` required some more changes to `KafkaHelper` ## Test coverage Applied the fix, re-ran didn't see again, however, the Error Tracking one may be a different bug as it appears that it is on .NET 9.0 😕 Edit: yes appears to be a different error that came up during development that was addressed ## Other details <!-- Fixes #{issue} --> Fixes https://datadoghq.atlassian.net/browse/APMLP-1146 <!-- ⚠️ Note: Where possible, please obtain 2 approvals prior to merging. Unless CODEOWNERS specifies otherwise, for external teams it is typically best to have one review from a team member, and one review from apm-dotnet. Trivial changes do not require 2 reviews. MergeQueue is NOT enabled in this repository. If you have write access to the repo, the PR has 1-2 approvals (see above), and all of the required checks have passed, you can use the Squash and Merge button to merge the PR. If you don't have write access, or you need help, reach out in the #apm-dotnet channel in Slack. -->
1 parent afb0834 commit f51d779

2 files changed

Lines changed: 11 additions & 33 deletions

File tree

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Kafka/KafkaHelper.cs

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -462,32 +462,20 @@ internal static void TryInjectHeaders<TTopicPartitionMarker, TMessage>(
462462
options.DuckCast<IDescribeClusterOptions>().RequestTimeout = TimeSpan.FromSeconds(2);
463463

464464
var duckTask = adminClient.DescribeClusterAsync(options);
465-
var describeResult = SafeGetResult<IDuckTypeTask<IDescribeClusterResult>, IDescribeClusterResult>(duckTask);
466-
return describeResult?.ClusterId;
467465

468-
static TResult? SafeGetResult<TTask, TResult>(TTask task)
469-
where TTask : IDuckTypeTask<TResult>
470-
where TResult : IDescribeClusterResult
466+
var originalContext = SynchronizationContext.Current;
467+
try
471468
{
472-
if (task.IsCompletedSuccessfully)
473-
{
474-
return task.Result;
475-
}
476-
477-
var originalContext = SynchronizationContext.Current;
478-
try
479-
{
480-
// Set the synchronization context to null to avoid deadlocks.
481-
SynchronizationContext.SetSynchronizationContext(null);
469+
// Set the synchronization context to null to avoid deadlocks.
470+
SynchronizationContext.SetSynchronizationContext(null);
482471

483-
// Wait synchronously for the task to complete.
484-
return task.GetAwaiter().GetResult();
485-
}
486-
finally
487-
{
488-
// Restore the original synchronization context.
489-
SynchronizationContext.SetSynchronizationContext(originalContext);
490-
}
472+
// Wait synchronously for the task to complete.
473+
return duckTask.GetAwaiter().GetResult()?.ClusterId;
474+
}
475+
finally
476+
{
477+
// Restore the original synchronization context.
478+
SynchronizationContext.SetSynchronizationContext(originalContext);
491479
}
492480
}
493481

tracer/src/Datadog.Trace/DuckTyping/IDuckTypeTask.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ namespace Datadog.Trace.DuckTyping;
1212
/// <typeparam name="T">Type of the result</typeparam>
1313
public interface IDuckTypeTask<out T> : IDuckType
1414
{
15-
/// <summary>
16-
/// Gets a value indicating whether if the task is completed
17-
/// </summary>
18-
bool IsCompletedSuccessfully { get; }
19-
2015
/// <summary>
2116
/// Gets the result of the task
2217
/// </summary>
@@ -34,11 +29,6 @@ public interface IDuckTypeTask<out T> : IDuckType
3429
/// </summary>
3530
public interface IDuckTypeTask : IDuckType
3631
{
37-
/// <summary>
38-
/// Gets a value indicating whether if the task is completed
39-
/// </summary>
40-
bool IsCompletedSuccessfully { get; }
41-
4232
/// <summary>
4333
/// Gets the awaiter for the task
4434
/// </summary>

0 commit comments

Comments
 (0)