Skip to content

Commit 6af2fba

Browse files
adinauerclaude
andcommitted
perf(core): Short-circuit combined scope breadcrumbs
Avoid allocating and sorting a merged breadcrumb queue when only one component scope has breadcrumbs. This keeps the full merge path for multi-scope breadcrumbs and returns the default write scope queue when all scopes are empty. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 24bca2e commit 6af2fba

2 files changed

Lines changed: 61 additions & 3 deletions

File tree

sentry/src/main/java/io/sentry/CombinedScopeView.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,31 @@ public void setFingerprint(@NotNull List<String> fingerprint) {
171171

172172
@Override
173173
public @NotNull Queue<Breadcrumb> getBreadcrumbs() {
174+
final @NotNull Queue<Breadcrumb> globalBreadcrumbs = globalScope.getBreadcrumbs();
175+
final @NotNull Queue<Breadcrumb> isolationBreadcrumbs = isolationScope.getBreadcrumbs();
176+
final @NotNull Queue<Breadcrumb> currentBreadcrumbs = scope.getBreadcrumbs();
177+
178+
final boolean hasGlobalBreadcrumbs = !globalBreadcrumbs.isEmpty();
179+
final boolean hasIsolationBreadcrumbs = !isolationBreadcrumbs.isEmpty();
180+
final boolean hasCurrentBreadcrumbs = !currentBreadcrumbs.isEmpty();
181+
182+
if (!hasGlobalBreadcrumbs && !hasIsolationBreadcrumbs && !hasCurrentBreadcrumbs) {
183+
return getDefaultWriteScope().getBreadcrumbs();
184+
}
185+
if (!hasIsolationBreadcrumbs && !hasCurrentBreadcrumbs) {
186+
return globalBreadcrumbs;
187+
}
188+
if (!hasGlobalBreadcrumbs && !hasCurrentBreadcrumbs) {
189+
return isolationBreadcrumbs;
190+
}
191+
if (!hasGlobalBreadcrumbs && !hasIsolationBreadcrumbs) {
192+
return currentBreadcrumbs;
193+
}
194+
174195
final @NotNull List<Breadcrumb> allBreadcrumbs = new ArrayList<>();
175-
allBreadcrumbs.addAll(globalScope.getBreadcrumbs());
176-
allBreadcrumbs.addAll(isolationScope.getBreadcrumbs());
177-
allBreadcrumbs.addAll(scope.getBreadcrumbs());
196+
allBreadcrumbs.addAll(globalBreadcrumbs);
197+
allBreadcrumbs.addAll(isolationBreadcrumbs);
198+
allBreadcrumbs.addAll(currentBreadcrumbs);
178199
Collections.sort(allBreadcrumbs);
179200

180201
final @NotNull Queue<Breadcrumb> breadcrumbs =

sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import junit.framework.TestCase.assertTrue
1111
import kotlin.test.Test
1212
import kotlin.test.assertEquals
1313
import kotlin.test.assertNotNull
14+
import kotlin.test.assertNotSame
1415
import kotlin.test.assertNull
1516
import kotlin.test.assertSame
1617
import org.junit.Assert.assertNotEquals
@@ -72,6 +73,42 @@ class CombinedScopeViewTest {
7273
assertEquals("current 2", breadcrumbs.poll().message)
7374
}
7475

76+
@Test
77+
fun `returns single non-empty breadcrumb queue directly`() {
78+
var combined = fixture.getSut()
79+
fixture.globalScope.addBreadcrumb(Breadcrumb.info("global"))
80+
assertSame(fixture.globalScope.breadcrumbs, combined.breadcrumbs)
81+
82+
combined = fixture.getSut()
83+
fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation"))
84+
assertSame(fixture.isolationScope.breadcrumbs, combined.breadcrumbs)
85+
86+
combined = fixture.getSut()
87+
fixture.scope.addBreadcrumb(Breadcrumb.info("current"))
88+
assertSame(fixture.scope.breadcrumbs, combined.breadcrumbs)
89+
}
90+
91+
@Test
92+
fun `returns default write scope breadcrumbs when all scopes are empty`() {
93+
val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT })
94+
95+
assertSame(fixture.scope.breadcrumbs, combined.breadcrumbs)
96+
}
97+
98+
@Test
99+
fun `returns merged breadcrumb copy when multiple scopes have breadcrumbs`() {
100+
val combined = fixture.getSut()
101+
102+
fixture.globalScope.addBreadcrumb(Breadcrumb.info("global"))
103+
fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation"))
104+
105+
val breadcrumbs = combined.breadcrumbs
106+
107+
assertNotSame(fixture.globalScope.breadcrumbs, breadcrumbs)
108+
assertNotSame(fixture.isolationScope.breadcrumbs, breadcrumbs)
109+
assertEquals(2, breadcrumbs.size)
110+
}
111+
75112
@Test
76113
fun `oldest breadcrumbs are dropped first`() {
77114
val options = SentryOptions().also { it.maxBreadcrumbs = 5 }

0 commit comments

Comments
 (0)