Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2d57966
Finish idle transaction using timeout option
SeanFeldman Jun 27, 2023
8f9e7a5
Ensure timer is cancelled when either triggered or transaction ends n…
SeanFeldman Jun 27, 2023
7254d6e
Add verification
SeanFeldman Jun 27, 2023
fc734ed
Remove destructor
SeanFeldman Jun 27, 2023
b625620
Update CHANGELOG.md
SeanFeldman Jun 27, 2023
1a03d78
Approve API changes
SeanFeldman Jun 27, 2023
5ca5599
Fix .NET 3.1 approval
SeanFeldman Jun 27, 2023
29ce332
Move idle timeout handling to the correct constructor
SeanFeldman Jun 27, 2023
8a1d79b
Add support for IdleTimeout to SentryOptions
SeanFeldman Jun 27, 2023
81950f9
Use SentryOptions configured IdleTimeout with TransactionTracer
SeanFeldman Jun 27, 2023
b653afc
Approve API changes for SentryOptions and updated TransactionTracer
SeanFeldman Jun 27, 2023
c36453c
Remove overload constructor in favor of resolving options from the hub
SeanFeldman Jul 5, 2023
908bdc1
Use explicit cast to determine if Hub type is used
SeanFeldman Jul 5, 2023
59101b2
Remove unnecessary member field
SeanFeldman Jul 5, 2023
af05fee
Avoid race condition
SeanFeldman Jul 5, 2023
e1dfb95
Remove IDisposable and dispose timer if the idle timeout timer has be…
SeanFeldman Jul 5, 2023
5262161
Merge remote-tracking branch 'origin/main' into 1074-transacton-idle-…
SeanFeldman Jul 10, 2023
1695110
Update src/Sentry/TransactionTracer.cs
SeanFeldman Jul 10, 2023
4c691b8
Do not finish transaction that is marked as a Sentry OTel transaction
SeanFeldman Jul 11, 2023
3db4a20
Do not complete OTel transaction
SeanFeldman Jul 12, 2023
696f636
Remove duplicated AssertionScope
SeanFeldman Jul 12, 2023
7e88587
Verify transaction was marked as a sentry request
SeanFeldman Jul 14, 2023
6701ad7
Remove redundant test (covered by TransactionTests)
SeanFeldman Jul 14, 2023
31f7826
Allow transaction idle timeout override
SeanFeldman Jul 17, 2023
dba21ec
Defer first execution until idle timeout kicks in
SeanFeldman Jul 17, 2023
f32c63a
Verify idle timeout override works
SeanFeldman Jul 17, 2023
060a8b9
Fix test name and tweak times
SeanFeldman Jul 18, 2023
3242e07
EXPERIMENTAL - increase delay to a few seconds
SeanFeldman Jul 18, 2023
6f49243
Apply suggestions from code review
SeanFeldman Jul 18, 2023
432b594
Update public API
SeanFeldman Jul 18, 2023
bdb7ed7
Take into consideration Sentry options provided idle timeout (acciden…
SeanFeldman Jul 18, 2023
76bf453
Simplification
SeanFeldman Jul 19, 2023
f8c5913
Apply suggestions from code review
SeanFeldman Jul 20, 2023
ea7db6f
Update src/Sentry/TransactionTracer.cs
SeanFeldman Jul 20, 2023
ebd70ff
Fix code review suggestions
SeanFeldman Jul 20, 2023
6d9e1d1
Update CONTRIBUTING.md
jamescrosswell Jul 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425))
- Add binding to `SwiftAsyncStacktraces` on iOS ([#2436](https://github.com/getsentry/sentry-dotnet/pull/2436))
- Support transaction finishing automatically with 'idle timeout' ([#2452](https://github.com/getsentry/sentry-dotnet/pull/2452))

### Fixes

Expand Down
5 changes: 5 additions & 0 deletions src/Sentry/SentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,11 @@ public bool JsonPreserveReferences
/// </summary>
internal bool InitNativeSdks { get; set; } = true;

/// <summary>
/// Idling transaction timeout after which transaction will be finished automatically.
/// </summary>
public TimeSpan? IdleTimeout { get; set; }

/// <summary>
/// Creates a new instance of <see cref="SentryOptions"/>
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions src/Sentry/TransactionTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace Sentry;
public class TransactionTracer : ITransaction, IHasDistribution, IHasTransactionNameSource, IHasMeasurements
{
private readonly IHub _hub;
private readonly Timer? _idleTimer;
private long _idleTimerStopped;
private readonly SentryStopwatch _stopwatch = SentryStopwatch.StartNew();

/// <inheritdoc />
Expand Down Expand Up @@ -174,6 +176,7 @@ public IReadOnlyList<string> Fingerprint

internal ITransactionProfiler? TransactionProfiler { get; set; }

// TODO: mark as internal in version 4
/// <summary>
/// Initializes an instance of <see cref="Transaction"/>.
/// </summary>
Expand All @@ -182,6 +185,7 @@ public TransactionTracer(IHub hub, string name, string operation)
{
}

// TODO: mark as internal in version 4
/// <summary>
/// Initializes an instance of <see cref="Transaction"/>.
/// </summary>
Expand All @@ -201,6 +205,7 @@ public TransactionTracer(IHub hub, string name, string operation, TransactionNam
public TransactionTracer(IHub hub, ITransactionContext context)
{
_hub = hub;
var idleTimeout = (hub as Hub)?.Options.IdleTimeout;
Name = context.Name;
NameSource = context is IHasTransactionNameSource c ? c.NameSource : TransactionNameSource.Custom;
Operation = context.Operation;
Expand All @@ -210,6 +215,19 @@ public TransactionTracer(IHub hub, ITransactionContext context)
Description = context.Description;
Status = context.Status;
IsSampled = context.IsSampled;

if (idleTimeout != null)
{
_idleTimer = new Timer(state =>
{
if (state is not TransactionTracer transactionTracer)
{
return;
}

transactionTracer.Finish(Status ?? SpanStatus.Ok);
}, this, TimeSpan.Zero, idleTimeout.Value);
}
}

/// <inheritdoc />
Expand Down Expand Up @@ -260,6 +278,13 @@ public ISpan StartChild(string operation) =>
/// <inheritdoc />
public void Finish()
{
if (Interlocked.Exchange(ref _idleTimerStopped, 1) == 0)
{
_idleTimer?.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);

_idleTimer?.Dispose();
}

TransactionProfiler?.Finish();
Status ??= SpanStatus.Ok;
EndTimestamp = _stopwatch.CurrentDateTimeOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down
1 change: 1 addition & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down
32 changes: 30 additions & 2 deletions test/Sentry.Tests/HubTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,7 @@ public void CaptureTransaction_HubEnabled(bool enabled)
[Fact]
public void CaptureTransaction_Client_Gets_Hint()
{
// Arrange
// Arrange
var hub = _fixture.GetSut();

// Act
Expand All @@ -1146,7 +1146,7 @@ public void CaptureTransaction_Client_Gets_Hint()
[Fact]
public void CaptureTransaction_Client_Gets_ScopeAttachments()
{
// Arrange
// Arrange
var hub = _fixture.GetSut();
List<Attachment> attachments = new List<Attachment> {
AttachmentHelper.FakeAttachment("foo"),
Expand Down Expand Up @@ -1336,4 +1336,32 @@ public async Task WithScopeAsyncT_Works()
}

private static Scope GetCurrentScope(Hub hub) => hub.ScopeManager.GetCurrent().Key;

[Fact]
public async Task IdleTimeout_auto_finishes_transaction()
{
// Arrange
_fixture.Options.IdleTimeout = TimeSpan.FromMilliseconds(2);
var hub = _fixture.GetSut();
var context = new TransactionContext(
SpanId.Create(),
SpanId.Create(),
SentryId.Create(),
"name",
"operation",
"description",
SpanStatus.Ok,
null,
true,
TransactionNameSource.Component
);

// Act
var transaction = hub.StartTransaction(context);

await Task.Delay(TimeSpan.FromMilliseconds(5));

// Assert
transaction.IsFinished.Should().BeTrue();
}
}
33 changes: 33 additions & 0 deletions test/Sentry.Tests/Protocol/TransactionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -504,4 +504,37 @@ public void ISpan_GetTransaction_FromSpan()
// Assert
Assert.Same(transaction, result);
}

[Fact]
public async Task Idle_transaction_should_finish_with_idle_timeout_specified()
{
// Arrange
var client = Substitute.For<ISentryClient>();
var options = new SentryOptions
{
Dsn = ValidDsn,
IdleTimeout = TimeSpan.FromMilliseconds(2)
};
var hub = new Hub(options, client);
var context = new TransactionContext(
SpanId.Create(),
SpanId.Create(),
SentryId.Create(),
"my name",
"my operation",
"description",
SpanStatus.Ok,
null,
true,
TransactionNameSource.Component
);

var transaction = new TransactionTracer(hub, context);

// Act
await Task.Delay(5);

// Assert
transaction.IsFinished.Should().BeTrue();
}
}
7 changes: 7 additions & 0 deletions test/Sentry.Tests/SentryOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,11 @@ public void FailedRequestTargets_ByDefault_MatchesAnyUrl()
var sut = new SentryOptions();
Assert.Contains(".*", sut.FailedRequestTargets);
}

[Fact]
public void IdleTimeout_ByDefault_IsNull()
{
var sut = new SentryOptions();
sut.IdleTimeout.IsNull();
}
}