Skip to content

Commit cb295d5

Browse files
committed
Only store isolationscope as WeakRef
1 parent 57a8b3a commit cb295d5

File tree

2 files changed

+23
-28
lines changed

2 files changed

+23
-28
lines changed

packages/core/src/tracing/utils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ function unwrapScopeFromWeakRef(scopeRef: ScopeWeakRef | undefined): Scope | und
5151
export function setCapturedScopesOnSpan(span: Span | undefined, scope: Scope, isolationScope: Scope): void {
5252
if (span) {
5353
addNonEnumerableProperty(span, ISOLATION_SCOPE_ON_START_SPAN_FIELD, wrapScopeWithWeakRef(isolationScope));
54-
addNonEnumerableProperty(span, SCOPE_ON_START_SPAN_FIELD, wrapScopeWithWeakRef(scope));
55-
// Testing
56-
setTimeout(() => [scope], 10_000); // keep this reference around for at least 10s
54+
// We don't wrap the scope with a WeakRef here because webkit aggressively garbage collects
55+
// and scopes are not held in memory for long periods of time.
56+
addNonEnumerableProperty(span, SCOPE_ON_START_SPAN_FIELD, scope);
5757
}
5858
}
5959

@@ -65,7 +65,7 @@ export function getCapturedScopesOnSpan(span: Span): { scope?: Scope; isolationS
6565
const spanWithScopes = span as SpanWithScopes;
6666

6767
return {
68-
scope: unwrapScopeFromWeakRef(spanWithScopes[SCOPE_ON_START_SPAN_FIELD]),
68+
scope: spanWithScopes[SCOPE_ON_START_SPAN_FIELD],
6969
isolationScope: unwrapScopeFromWeakRef(spanWithScopes[ISOLATION_SCOPE_ON_START_SPAN_FIELD]),
7070
};
7171
}

packages/core/test/lib/tracing/utils.test.ts

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,18 @@ describe('tracing utils', () => {
4444
expect(retrieved.isolationScope).toBeUndefined();
4545
});
4646

47-
it('uses WeakRef', () => {
47+
it('uses WeakRef only for isolation scopes', () => {
4848
const span = createMockSpan();
4949
const scope = new Scope();
5050
const isolationScope = new Scope();
5151

5252
setCapturedScopesOnSpan(span, scope, isolationScope);
5353

54-
// Check that WeakRef instances were stored
54+
// Check that only isolation scope is wrapped with WeakRef
5555
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5656
const spanWithScopes = span as any;
57-
expect(spanWithScopes._sentryScope).toBeInstanceOf(WeakRef);
58-
expect(spanWithScopes._sentryIsolationScope).toBeInstanceOf(WeakRef);
57+
expect(spanWithScopes._sentryScope).toBe(scope); // Regular scope stored directly
58+
expect(spanWithScopes._sentryIsolationScope).toBeInstanceOf(WeakRef); // Isolation scope wrapped
5959

6060
// Verify we can still retrieve the scopes
6161
const retrieved = getCapturedScopesOnSpan(span);
@@ -76,13 +76,13 @@ describe('tracing utils', () => {
7676

7777
setCapturedScopesOnSpan(span, scope, isolationScope);
7878

79-
// Check that scopes were stored directly
79+
// Check that both scopes are stored directly when WeakRef is not available
8080
// eslint-disable-next-line @typescript-eslint/no-explicit-any
8181
const spanWithScopes = span as any;
82-
expect(spanWithScopes._sentryScope).toBe(scope);
83-
expect(spanWithScopes._sentryIsolationScope).toBe(isolationScope);
82+
expect(spanWithScopes._sentryScope).toBe(scope); // Regular scope always stored directly
83+
expect(spanWithScopes._sentryIsolationScope).toBe(isolationScope); // Isolation scope falls back to direct storage
8484

85-
// When WeakRef is available, check that stored values are not WeakRef instances
85+
// When WeakRef is available, ensure regular scope is not wrapped but isolation scope would be
8686
if (originalWeakRef) {
8787
expect(spanWithScopes._sentryScope).not.toBeInstanceOf(originalWeakRef);
8888
expect(spanWithScopes._sentryIsolationScope).not.toBeInstanceOf(originalWeakRef);
@@ -106,46 +106,41 @@ describe('tracing utils', () => {
106106

107107
setCapturedScopesOnSpan(span, scope, isolationScope);
108108

109-
// Mock WeakRef.deref to return undefined (simulating garbage collection)
109+
// Mock WeakRef.deref to return undefined for isolation scope (simulating garbage collection)
110+
// Regular scope is stored directly, so it should always be available
110111
// eslint-disable-next-line @typescript-eslint/no-explicit-any
111112
const spanWithScopes = span as any;
112-
const mockScopeWeakRef = {
113-
deref: vi.fn().mockReturnValue(undefined),
114-
};
115113
const mockIsolationScopeWeakRef = {
116114
deref: vi.fn().mockReturnValue(undefined),
117115
};
118116

119-
spanWithScopes._sentryScope = mockScopeWeakRef;
117+
// Keep the regular scope as is (stored directly)
118+
// Only replace the isolation scope with a mock WeakRef
120119
spanWithScopes._sentryIsolationScope = mockIsolationScopeWeakRef;
121120

122121
const retrieved = getCapturedScopesOnSpan(span);
123-
expect(retrieved.scope).toBeUndefined();
124-
expect(retrieved.isolationScope).toBeUndefined();
125-
expect(mockScopeWeakRef.deref).toHaveBeenCalled();
122+
expect(retrieved.scope).toBe(scope); // Regular scope should still be available
123+
expect(retrieved.isolationScope).toBeUndefined(); // Isolation scope should be undefined due to GC
126124
expect(mockIsolationScopeWeakRef.deref).toHaveBeenCalled();
127125
});
128126

129127
it('handles corrupted WeakRef objects gracefully', () => {
130128
const span = createMockSpan();
129+
const scope = new Scope();
131130

132-
// Simulate corrupted WeakRef objects
131+
// Set up a regular scope (stored directly) and a corrupted isolation scope WeakRef
133132
// eslint-disable-next-line @typescript-eslint/no-explicit-any
134133
const spanWithScopes = span as any;
135-
spanWithScopes._sentryScope = {
136-
deref: vi.fn().mockImplementation(() => {
137-
throw new Error('WeakRef deref failed');
138-
}),
139-
};
134+
spanWithScopes._sentryScope = scope; // Regular scope stored directly
140135
spanWithScopes._sentryIsolationScope = {
141136
deref: vi.fn().mockImplementation(() => {
142137
throw new Error('WeakRef deref failed');
143138
}),
144139
};
145140

146141
const retrieved = getCapturedScopesOnSpan(span);
147-
expect(retrieved.scope).toBeUndefined();
148-
expect(retrieved.isolationScope).toBeUndefined();
142+
expect(retrieved.scope).toBe(scope); // Regular scope should still be available
143+
expect(retrieved.isolationScope).toBeUndefined(); // Isolation scope should be undefined due to error
149144
});
150145

151146
it('preserves scope data when using WeakRef', () => {

0 commit comments

Comments
 (0)