Skip to content

Commit adeec8f

Browse files
committed
Implement optimizations for all integrations
1 parent 3850cdb commit adeec8f

20 files changed

Lines changed: 521 additions & 34 deletions

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AWS/Kinesis/ContextPropagation.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ public static void InjectTraceIntoData<TRecordRequest>(Tracer tracer, TRecordReq
6464
if (dataStreamsManager != null && dataStreamsManager.IsEnabled)
6565
{
6666
var payloadSize = jsonData?.Count > 0 && record.Data != null ? record.Data.Length : 0;
67-
var edgeTags = new[] { "direction:out", $"topic:{streamName}", "type:kinesis" };
67+
var edgeTags = dataStreamsManager.GetOrCreateEdgeTags(
68+
new KinesisEdgeTagCacheKey(streamName!, isConsume: false),
69+
static k => ["direction:out", $"topic:{k.StreamName}", "type:kinesis"]);
6870
scope.Span.SetDataStreamsCheckpoint(
6971
dataStreamsManager,
7072
CheckpointKind.Produce,

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AWS/Kinesis/GetRecordsAsyncIntegration.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ internal static TResponse OnAsyncMethodEnd<TTarget, TResponse>(TTarget instance,
6161
var dataStreamsManager = Tracer.Instance.TracerManager.DataStreamsManager;
6262
if (dataStreamsManager is { IsEnabled: true })
6363
{
64-
var edgeTags = new[] { "direction:in", $"topic:{(string)state.State}", "type:kinesis" };
64+
var edgeTags = dataStreamsManager.GetOrCreateEdgeTags(
65+
new KinesisEdgeTagCacheKey((string)state.State, isConsume: true),
66+
static k => ["direction:in", $"topic:{k.StreamName}", "type:kinesis"]);
6567
foreach (var o in response.Records)
6668
{
6769
var record = o.DuckCast<IRecord>();

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AWS/Kinesis/GetRecordsIntegration.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ internal static CallTargetReturn<TResponse> OnMethodEnd<TTarget, TResponse>(TTar
7878
var dataStreamsManager = Tracer.Instance.TracerManager.DataStreamsManager;
7979
if (dataStreamsManager is { IsEnabled: true })
8080
{
81-
var edgeTags = new[] { "direction:in", $"topic:{(string)state.State}", "type:kinesis" };
81+
var edgeTags = dataStreamsManager.GetOrCreateEdgeTags(
82+
new KinesisEdgeTagCacheKey((string)state.State, isConsume: true),
83+
static k => ["direction:in", $"topic:{k.StreamName}", "type:kinesis"]);
8284
foreach (var o in response.Records)
8385
{
8486
var record = o.DuckCast<IRecord>();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// <copyright file="KinesisEdgeTagCacheKey.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#nullable enable
7+
8+
using System;
9+
10+
namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AWS.Kinesis;
11+
12+
/// <summary>
13+
/// Value-type cache key for Kinesis edge tags. Using a named struct avoids boxing and
14+
/// is compatible with all supported target frameworks.
15+
/// <see cref="IsConsume"/> distinguishes produce (direction:out) from consume (direction:in)
16+
/// so that both directions share a single cache type without key collision.
17+
/// </summary>
18+
internal readonly struct KinesisEdgeTagCacheKey : IEquatable<KinesisEdgeTagCacheKey>
19+
{
20+
public readonly string StreamName;
21+
public readonly bool IsConsume;
22+
23+
public KinesisEdgeTagCacheKey(string streamName, bool isConsume)
24+
{
25+
StreamName = streamName;
26+
IsConsume = isConsume;
27+
}
28+
29+
public bool Equals(KinesisEdgeTagCacheKey other)
30+
=> StreamName == other.StreamName && IsConsume == other.IsConsume;
31+
32+
public override bool Equals(object? obj)
33+
=> obj is KinesisEdgeTagCacheKey other && Equals(other);
34+
35+
public override int GetHashCode()
36+
{
37+
unchecked
38+
{
39+
int hash = 17;
40+
hash = (hash * 31) + (StreamName?.GetHashCode() ?? 0);
41+
hash = (hash * 31) + IsConsume.GetHashCode();
42+
return hash;
43+
}
44+
}
45+
}

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AWS/SNS/AwsSnsHandlerCommon.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ public static CallTargetState BeforePublish<TPublishRequest>(TPublishRequest req
3737
if (scope?.Span.Context is { } context && !string.IsNullOrEmpty(topicName))
3838
{
3939
var dataStreamsManager = tracer.TracerManager.DataStreamsManager;
40-
// avoid allocation if edgeTags are not going to be used
41-
var edgeTags = dataStreamsManager is { IsEnabled: true } ? ["direction:out", $"topic:{topicName}", "type:sns"] : Array.Empty<string>();
40+
var edgeTags = dataStreamsManager is { IsEnabled: true }
41+
? dataStreamsManager.GetOrCreateEdgeTags(
42+
new SnsEdgeTagCacheKey(topicName!),
43+
static k => ["direction:out", $"topic:{k.TopicName}", "type:sns"])
44+
: Array.Empty<string>();
4245

4346
if (sendType == SendType.SingleMessage)
4447
{
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// <copyright file="SnsEdgeTagCacheKey.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#nullable enable
7+
8+
using System;
9+
10+
namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AWS.SNS;
11+
12+
/// <summary>
13+
/// Value-type cache key for SNS produce edge tags. Using a named struct avoids boxing and
14+
/// is compatible with all supported target frameworks.
15+
/// </summary>
16+
internal readonly struct SnsEdgeTagCacheKey : IEquatable<SnsEdgeTagCacheKey>
17+
{
18+
public readonly string TopicName;
19+
20+
public SnsEdgeTagCacheKey(string topicName)
21+
{
22+
TopicName = topicName;
23+
}
24+
25+
public bool Equals(SnsEdgeTagCacheKey other)
26+
=> TopicName == other.TopicName;
27+
28+
public override bool Equals(object? obj)
29+
=> obj is SnsEdgeTagCacheKey other && Equals(other);
30+
31+
public override int GetHashCode()
32+
=> TopicName?.GetHashCode() ?? 0;
33+
}

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AWS/SQS/AwsSqsHandlerCommon.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ private static void InjectForSingleMessage<TSendMessageRequest>(Tracer tracer, T
6666
var dataStreamsManager = tracer.TracerManager.DataStreamsManager;
6767
if (dataStreamsManager != null && dataStreamsManager.IsEnabled)
6868
{
69-
var edgeTags = new[] { "direction:out", $"topic:{queueName}", "type:sqs" };
69+
var edgeTags = dataStreamsManager.GetOrCreateEdgeTags(
70+
new SqsEdgeTagCacheKey(queueName, isConsume: false),
71+
static k => ["direction:out", $"topic:{k.QueueName}", "type:sqs"]);
7072
scope.Span.SetDataStreamsCheckpoint(dataStreamsManager, CheckpointKind.Produce, edgeTags, payloadSizeBytes: 0, timeInQueueMs: 0);
7173
}
7274

@@ -81,14 +83,18 @@ private static void InjectForBatch<TSendMessageBatchRequest>(Tracer tracer, TSen
8183
return;
8284
}
8385

84-
var edgeTags = new[] { "direction:out", $"topic:{queueName}", "type:sqs" };
86+
var dataStreamsManager = tracer.TracerManager.DataStreamsManager;
87+
var edgeTags = dataStreamsManager is { IsEnabled: true }
88+
? dataStreamsManager.GetOrCreateEdgeTags(
89+
new SqsEdgeTagCacheKey(queueName, isConsume: false),
90+
static k => ["direction:out", $"topic:{k.QueueName}", "type:sqs"])
91+
: new[] { "direction:out", $"topic:{queueName}", "type:sqs" };
8592
foreach (var e in requestProxy.Entries)
8693
{
8794
var entry = e.DuckCast<IContainsMessageAttributes>();
8895
if (entry != null)
8996
{
9097
// this has no effect if DSM is disabled
91-
var dataStreamsManager = tracer.TracerManager.DataStreamsManager;
9298
scope.Span.SetDataStreamsCheckpoint(dataStreamsManager, CheckpointKind.Produce, edgeTags, payloadSizeBytes: 0, timeInQueueMs: 0);
9399
// this needs to be done for context propagation even when DSM is disabled
94100
// (when DSM is enabled, it injects the pathway context on top of the trace context)
@@ -148,7 +154,9 @@ internal static TResponse AfterReceive<TResponse>(TResponse response, Exception?
148154
var dataStreamsManager = Tracer.Instance.TracerManager.DataStreamsManager;
149155
if (dataStreamsManager is { IsEnabled: true })
150156
{
151-
var edgeTags = new[] { "direction:in", $"topic:{(string)state.State}", "type:sqs" };
157+
var edgeTags = dataStreamsManager.GetOrCreateEdgeTags(
158+
new SqsEdgeTagCacheKey((string)state.State, isConsume: true),
159+
static k => ["direction:in", $"topic:{k.QueueName}", "type:sqs"]);
152160
foreach (var o in response.Messages)
153161
{
154162
var message = o.DuckCast<IMessage>();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// <copyright file="SqsEdgeTagCacheKey.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#nullable enable
7+
8+
using System;
9+
10+
namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AWS.SQS;
11+
12+
/// <summary>
13+
/// Value-type cache key for SQS edge tags. Using a named struct avoids boxing and
14+
/// is compatible with all supported target frameworks.
15+
/// <see cref="IsConsume"/> distinguishes produce (direction:out) from consume (direction:in)
16+
/// so that both directions share a single cache type without key collision.
17+
/// </summary>
18+
internal readonly struct SqsEdgeTagCacheKey : IEquatable<SqsEdgeTagCacheKey>
19+
{
20+
public readonly string QueueName;
21+
public readonly bool IsConsume;
22+
23+
public SqsEdgeTagCacheKey(string queueName, bool isConsume)
24+
{
25+
QueueName = queueName;
26+
IsConsume = isConsume;
27+
}
28+
29+
public bool Equals(SqsEdgeTagCacheKey other)
30+
=> QueueName == other.QueueName && IsConsume == other.IsConsume;
31+
32+
public override bool Equals(object? obj)
33+
=> obj is SqsEdgeTagCacheKey other && Equals(other);
34+
35+
public override int GetHashCode()
36+
{
37+
unchecked
38+
{
39+
int hash = 17;
40+
hash = (hash * 31) + (QueueName?.GetHashCode() ?? 0);
41+
hash = (hash * 31) + IsConsume.GetHashCode();
42+
return hash;
43+
}
44+
}
45+
}

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Azure/ServiceBus/ProcessMessageIntegration.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public sealed class ProcessMessageIntegration
3434
{
3535
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(ProcessMessageIntegration));
3636

37+
// Used when the entity path is unknown — direction:in and type:servicebus but no topic tag
38+
private static readonly string[] DefaultConsumeEdgeTags = ["direction:in", "type:servicebus"];
39+
3740
/// <summary>
3841
/// OnMethodBegin callback
3942
/// </summary>
@@ -81,11 +84,12 @@ internal static CallTargetState OnMethodBegin<TTarget, TMessage>(TTarget instanc
8184

8285
var namespaceString = instance.Processor.EntityPath;
8386

84-
// TODO: we could pool these arrays to reduce allocations
8587
// NOTE: the tags must be sorted in alphabetical order
8688
var edgeTags = string.IsNullOrEmpty(namespaceString)
87-
? new[] { "direction:in", "type:servicebus" }
88-
: new[] { "direction:in", $"topic:{namespaceString}", "type:servicebus" };
89+
? DefaultConsumeEdgeTags
90+
: dataStreamsManager.GetOrCreateEdgeTags(
91+
new ServiceBusEdgeTagCacheKey(namespaceString),
92+
static k => ["direction:in", $"topic:{k.EntityPath}", "type:servicebus"]);
8993
var msgSize = dataStreamsManager.IsInDefaultState ? 0 : AzureServiceBusCommon.GetMessageSize(message);
9094
span.SetDataStreamsCheckpoint(
9195
dataStreamsManager,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// <copyright file="ServiceBusEdgeTagCacheKey.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#nullable enable
7+
8+
using System;
9+
10+
namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Azure.ServiceBus;
11+
12+
/// <summary>
13+
/// Value-type cache key for Azure Service Bus consume edge tags. Using a named struct avoids boxing and
14+
/// is compatible with all supported target frameworks.
15+
/// </summary>
16+
internal readonly struct ServiceBusEdgeTagCacheKey : IEquatable<ServiceBusEdgeTagCacheKey>
17+
{
18+
public readonly string EntityPath;
19+
20+
public ServiceBusEdgeTagCacheKey(string entityPath)
21+
{
22+
EntityPath = entityPath;
23+
}
24+
25+
public bool Equals(ServiceBusEdgeTagCacheKey other)
26+
=> EntityPath == other.EntityPath;
27+
28+
public override bool Equals(object? obj)
29+
=> obj is ServiceBusEdgeTagCacheKey other && Equals(other);
30+
31+
public override int GetHashCode()
32+
=> EntityPath?.GetHashCode() ?? 0;
33+
}

0 commit comments

Comments
 (0)