Skip to content

Commit db9a48e

Browse files
authored
fix: Clearing transaction context for held transactions and async WCF client instrumentation timing. (#1608)
1 parent 96b9cac commit db9a48e

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

src/Agent/NewRelic/Agent/Core/Transactions/Transaction.cs

+5
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ public void End(bool captureResponseTime = true)
133133
{
134134
LogFinest("Response time captured.");
135135
}
136+
137+
// Once the response time is captured, the only work remaining for the transaction can be
138+
// async work that is holding onto the transaction. We need to remove the transaction
139+
// from the transaction context so that it will not be reused.
140+
Agent._transactionService.RemoveOutstandingInternalTransactions(true, true);
136141
}
137142

138143
var remainingUnitsOfWork = NoticeUnitOfWorkEnds();

src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Wcf3/ServiceChannelProxyWrapper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ void OnSuccess(System.Runtime.Remoting.Messaging.IMethodReturnMessage methodRetu
124124
segment.RemoveSegmentFromCallStack();
125125
transaction.Hold();
126126
var task = (Task)methodReturnMessage.ReturnValue;
127-
task.ContinueWith(ContinueWork);
127+
task.ContinueWith(ContinueWork, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.HideScheduler);
128128

129129
void ContinueWork(Task t)
130130
{

tests/Agent/UnitTests/CompositeTests/TransactionTests.cs

+61
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using NewRelic.Agent.Api;
55
using NewRelic.Agent.Core;
66
using NewRelic.Agent.Core.Attributes;
7+
using NewRelic.Agent.Core.Transactions;
78
using NewRelic.Agent.Extensions.Providers.Wrapper;
89
using NewRelic.Agent.TestUtilities;
910
using NewRelic.Testing.Assertions;
@@ -676,5 +677,65 @@ public void ResponseTimeShouldOnlyBeCapturedOnce()
676677
var timingMetric = _compositeTestAgent.Metrics.First(x => x.MetricName.Name == "WebTransaction");
677678
Assert.LessOrEqual(timingMetric.Data.Value1, upperBoundStopWatch.Elapsed.TotalSeconds);
678679
}
680+
681+
[Test]
682+
public void TransactionShouldBeRemovedFromContextStorageWhenResponseTimeIsCaptured()
683+
{
684+
// Setup a transaction where fire and forget async work is tracked
685+
var transaction = _agent.CreateTransaction(
686+
isWeb: false,
687+
category: "category",
688+
transactionDisplayName: "transactionName",
689+
doNotTrackAsUnitOfWork: true);
690+
var segment = _agent.StartTransactionSegmentOrThrow("fireAndForgetSegment");
691+
transaction.Hold();
692+
// Response time should be tracked and the current transaction removed from storage.
693+
transaction.End();
694+
695+
var transactionAfterCallToEnd = _agent.CurrentTransaction;
696+
697+
// The fire and forget segment ends and then releases the transaction
698+
segment.End();
699+
transaction.Release();
700+
701+
_compositeTestAgent.Harvest();
702+
703+
//Use the OtherTransaction/all metric to confirm that the transaction was transformed and harvested
704+
var timingMetric = _compositeTestAgent.Metrics.First(x => x.MetricName.Name == "OtherTransaction/all");
705+
NrAssert.Multiple(
706+
() => Assert.IsFalse(transactionAfterCallToEnd.IsValid, "The current transaction should be the NoOpTransaction."),
707+
() => Assert.AreEqual(1.0, timingMetric.Data.Value0, "The transaction should be harvested.")
708+
);
709+
}
710+
711+
[Test]
712+
public void TransactionShouldNotBeRemovedFromContextStorageWhenResponseTimeIsNotCaptured()
713+
{
714+
// Setup a transaction where fire and forget async work is tracked
715+
var transaction = _agent.CreateTransaction(
716+
isWeb: false,
717+
category: "category",
718+
transactionDisplayName: "transactionName",
719+
doNotTrackAsUnitOfWork: true);
720+
var segment = _agent.StartTransactionSegmentOrThrow("fireAndForgetSegment");
721+
transaction.Hold();
722+
// Response time should be tracked and the current transaction removed from storage.
723+
transaction.End(captureResponseTime: false);
724+
725+
var transactionAfterCallToEnd = _agent.CurrentTransaction;
726+
727+
// The fire and forget segment ends and then releases the transaction
728+
segment.End();
729+
transaction.Release();
730+
731+
_compositeTestAgent.Harvest();
732+
733+
//Use the OtherTransaction/all metric to confirm that the transaction was transformed and harvested
734+
var timingMetric = _compositeTestAgent.Metrics.First(x => x.MetricName.Name == "OtherTransaction/all");
735+
NrAssert.Multiple(
736+
() => Assert.IsTrue(transactionAfterCallToEnd.IsValid, "The current transaction should not be the NoOpTransaction."),
737+
() => Assert.AreEqual(1.0, timingMetric.Data.Value0, "The transaction should be harvested.")
738+
);
739+
}
679740
}
680741
}

0 commit comments

Comments
 (0)