-
-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce scope stack abstraction (#1124)
- Loading branch information
Showing
14 changed files
with
361 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/Sentry.AspNet/Internal/HttpContextScopeStackContainer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System.Collections.Generic; | ||
using System.Web; | ||
using Sentry.Internal.ScopeStack; | ||
|
||
namespace Sentry.AspNet.Internal | ||
{ | ||
internal class HttpContextScopeStackContainer : IScopeStackContainer | ||
{ | ||
private const string FieldName = "__SentryScopeStack"; | ||
|
||
public KeyValuePair<Scope, ISentryClient>[]? Stack | ||
{ | ||
get => HttpContext.Current.Items[FieldName] as KeyValuePair<Scope, ISentryClient>[]; | ||
set => HttpContext.Current.Items[FieldName] = value; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/Sentry/Internal/ScopeStack/AsyncLocalScopeStackContainer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
|
||
namespace Sentry.Internal.ScopeStack | ||
{ | ||
internal class AsyncLocalScopeStackContainer : IScopeStackContainer | ||
{ | ||
private readonly AsyncLocal<KeyValuePair<Scope, ISentryClient>[]?> _asyncLocalScope = new(); | ||
|
||
public KeyValuePair<Scope, ISentryClient>[]? Stack | ||
{ | ||
get => _asyncLocalScope.Value; | ||
set => _asyncLocalScope.Value = value; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Sentry.Internal.ScopeStack | ||
{ | ||
internal class GlobalScopeStackContainer : IScopeStackContainer | ||
{ | ||
public KeyValuePair<Scope, ISentryClient>[]? Stack { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Sentry.Internal.ScopeStack | ||
{ | ||
internal interface IScopeStackContainer | ||
{ | ||
KeyValuePair<Scope, ISentryClient>[]? Stack { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
test/Sentry.Tests/Internals/ScopeStack/AsyncLocalScopeStackContainerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using NSubstitute; | ||
using Sentry.Internal.ScopeStack; | ||
using Xunit; | ||
|
||
namespace Sentry.Tests.Internals.ScopeStack | ||
{ | ||
public class AsyncLocalScopeStackContainerTests | ||
{ | ||
[Fact] | ||
public async Task Scopes_are_not_shared_between_parallel_async_executions() | ||
{ | ||
// Arrange | ||
var container = new AsyncLocalScopeStackContainer(); | ||
|
||
var scope1 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
var scope2 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
// Act & assert | ||
var task1 = Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
await Task.Yield(); | ||
container.Stack.Should().BeNull(); | ||
}); | ||
|
||
var task2 = Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
container.Stack = new[] {scope1}; | ||
await Task.Yield(); | ||
container.Stack.Should().BeEquivalentTo(new[] {scope1}); | ||
}); | ||
|
||
var task3 = Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
container.Stack = new[] {scope2}; | ||
await Task.Yield(); | ||
container.Stack.Should().BeEquivalentTo(new[] {scope2}); | ||
}); | ||
|
||
await Task.WhenAll(task1, task2, task3); | ||
|
||
container.Stack.Should().BeNull(); | ||
} | ||
|
||
[Fact] | ||
public async Task Scopes_are_not_shared_between_sequential_async_executions() | ||
{ | ||
// Arrange | ||
var container = new AsyncLocalScopeStackContainer(); | ||
|
||
var scope1 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
var scope2 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
// Act & assert | ||
await Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
await Task.Yield(); | ||
container.Stack.Should().BeNull(); | ||
}); | ||
|
||
await Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
container.Stack = new[] {scope1}; | ||
await Task.Yield(); | ||
container.Stack.Should().BeEquivalentTo(new[] {scope1}); | ||
}); | ||
|
||
await Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
container.Stack = new[] {scope2}; | ||
await Task.Yield(); | ||
container.Stack.Should().BeEquivalentTo(new[] {scope2}); | ||
}); | ||
|
||
container.Stack.Should().BeNull(); | ||
} | ||
|
||
[Fact] | ||
public async Task Scopes_are_shared_between_nested_async_executions() | ||
{ | ||
// Arrange | ||
var container = new AsyncLocalScopeStackContainer(); | ||
|
||
var scope1 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
var scope2 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
// Act & assert | ||
await Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
await Task.Yield(); | ||
container.Stack.Should().BeNull(); | ||
await Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
container.Stack = new[] {scope1}; | ||
await Task.Yield(); | ||
container.Stack.Should().BeEquivalentTo(new[] {scope1}); | ||
await Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeEquivalentTo(new[] {scope1}); | ||
await Task.Yield(); | ||
container.Stack = new[] {scope2}; | ||
container.Stack.Should().BeEquivalentTo(new[] {scope2}); | ||
}).ConfigureAwait(false); | ||
}).ConfigureAwait(false); | ||
}); | ||
|
||
container.Stack.Should().BeNull(); | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
test/Sentry.Tests/Internals/ScopeStack/GlobalScopeStackContainerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using NSubstitute; | ||
using Sentry.Internal.ScopeStack; | ||
using Xunit; | ||
|
||
namespace Sentry.Tests.Internals.ScopeStack | ||
{ | ||
public class GlobalScopeStackContainerTests | ||
{ | ||
[Fact] | ||
public async Task Scopes_are_shared_between_parallel_async_executions() | ||
{ | ||
// Arrange | ||
var container = new GlobalScopeStackContainer(); | ||
|
||
var scope1 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
var scope2 = new KeyValuePair<Scope, ISentryClient>( | ||
Substitute.For<Scope>(), | ||
Substitute.For<ISentryClient>() | ||
); | ||
|
||
// Act & assert | ||
await Task.Run(async () => | ||
{ | ||
container.Stack.Should().BeNull(); | ||
container.Stack = new[] {scope1, scope2}; | ||
await Task.Yield(); | ||
container.Stack.Should().BeEquivalentTo(new[] {scope1, scope2}); | ||
}); | ||
|
||
container.Stack.Should().BeEquivalentTo(new[] {scope1, scope2}); | ||
} | ||
} | ||
} |
Oops, something went wrong.