Skip to content

Commit 11f512d

Browse files
authored
Merge branch 'master' into smola/unique-onrootspanfinished
2 parents 7d4e2b4 + bd6b34d commit 11f512d

File tree

23 files changed

+453
-35
lines changed

23 files changed

+453
-35
lines changed

.gitlab/benchmarks.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ variables:
1010
image: $BASE_CI_IMAGE
1111
script:
1212
- export ARTIFACTS_DIR="$(pwd)/reports" && mkdir -p "${ARTIFACTS_DIR}"
13+
- export CIRCLE_CI_TOKEN=$(aws ssm get-parameter --region us-east-1 --name ci.dd-trace-java.circleci_token --with-decryption --query "Parameter.Value" --out text)
1314
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.ddbuild.io/DataDog/".insteadOf "https://github.com/DataDog/"
1415
- git clone --branch dd-trace-java/tracer-benchmarks https://github.com/DataDog/benchmarking-platform.git /platform && cd /platform
1516
artifacts:

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ public static synchronized void run(
8686
if (config.isDebuggerExceptionEnabled()) {
8787
defaultExceptionDebugger =
8888
new DefaultExceptionDebugger(
89-
configurationUpdater, classNameFiltering, EXCEPTION_CAPTURE_INTERVAL);
89+
configurationUpdater,
90+
classNameFiltering,
91+
EXCEPTION_CAPTURE_INTERVAL,
92+
config.getDebuggerMaxExceptionPerSecond());
9093
DebuggerContext.initExceptionDebugger(defaultExceptionDebugger);
9194
}
9295
if (config.isDebuggerInstrumentTheWorld()) {

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.datadog.debugger.agent.DebuggerAgent;
88
import com.datadog.debugger.exception.ExceptionProbeManager.ThrowableState;
99
import com.datadog.debugger.sink.Snapshot;
10+
import com.datadog.debugger.util.CircuitBreaker;
1011
import com.datadog.debugger.util.ClassNameFiltering;
1112
import com.datadog.debugger.util.ExceptionHelper;
1213
import datadog.trace.bootstrap.debugger.DebuggerContext;
@@ -31,24 +32,29 @@ public class DefaultExceptionDebugger implements DebuggerContext.ExceptionDebugg
3132
private final ExceptionProbeManager exceptionProbeManager;
3233
private final ConfigurationUpdater configurationUpdater;
3334
private final ClassNameFiltering classNameFiltering;
35+
private final CircuitBreaker circuitBreaker;
3436

3537
public DefaultExceptionDebugger(
3638
ConfigurationUpdater configurationUpdater,
3739
ClassNameFiltering classNameFiltering,
38-
Duration captureInterval) {
40+
Duration captureInterval,
41+
int maxExceptionPerSecond) {
3942
this(
4043
new ExceptionProbeManager(classNameFiltering, captureInterval),
4144
configurationUpdater,
42-
classNameFiltering);
45+
classNameFiltering,
46+
maxExceptionPerSecond);
4347
}
4448

4549
DefaultExceptionDebugger(
4650
ExceptionProbeManager exceptionProbeManager,
4751
ConfigurationUpdater configurationUpdater,
48-
ClassNameFiltering classNameFiltering) {
52+
ClassNameFiltering classNameFiltering,
53+
int maxExceptionPerSecond) {
4954
this.exceptionProbeManager = exceptionProbeManager;
5055
this.configurationUpdater = configurationUpdater;
5156
this.classNameFiltering = classNameFiltering;
57+
this.circuitBreaker = new CircuitBreaker(maxExceptionPerSecond, Duration.ofSeconds(1));
5258
}
5359

5460
@Override
@@ -59,6 +65,9 @@ public void handleException(Throwable t, AgentSpan span) {
5965
}
6066
return;
6167
}
68+
if (!circuitBreaker.trip()) {
69+
return;
70+
}
6271
String fingerprint = Fingerprinter.fingerprint(t, classNameFiltering);
6372
if (fingerprint == null) {
6473
LOGGER.debug("Unable to fingerprint exception", t);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.datadog.debugger.util;
2+
3+
import java.time.Duration;
4+
import java.util.concurrent.atomic.AtomicInteger;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
// CircuitBreaker is a simple circuit breaker implementation that allows a certain number of trips
9+
// within a time window. If the number of trips exceeds the limit, the circuit breaker will trip and
10+
// return false until the time window has passed.
11+
public class CircuitBreaker {
12+
private static final Logger LOGGER = LoggerFactory.getLogger(CircuitBreaker.class);
13+
14+
private final int maxTrips;
15+
private final Duration timeWindow;
16+
private AtomicInteger count = new AtomicInteger(0);
17+
private volatile long lastResetTime = System.currentTimeMillis();
18+
private volatile long lastLoggingTime = System.currentTimeMillis();
19+
20+
public CircuitBreaker(int maxTrips, Duration timeWindow) {
21+
this.maxTrips = maxTrips;
22+
this.timeWindow = timeWindow;
23+
}
24+
25+
public boolean trip() {
26+
int localCount = count.incrementAndGet();
27+
if (localCount > maxTrips) {
28+
long currentTime = System.currentTimeMillis();
29+
if (currentTime - lastLoggingTime > Duration.ofMinutes(1).toMillis()) {
30+
lastLoggingTime = currentTime;
31+
LOGGER.debug("Circuit breaker opened");
32+
}
33+
if (currentTime - lastResetTime > timeWindow.toMillis()) {
34+
lastResetTime = currentTime;
35+
localCount = 1;
36+
count.set(localCount);
37+
}
38+
}
39+
return localCount <= maxTrips;
40+
}
41+
}

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public void setUp() {
6060
configurationUpdater = mock(ConfigurationUpdater.class);
6161
classNameFiltering = new ClassNameFiltering(emptySet());
6262
exceptionDebugger =
63-
new DefaultExceptionDebugger(configurationUpdater, classNameFiltering, Duration.ofHours(1));
63+
new DefaultExceptionDebugger(
64+
configurationUpdater, classNameFiltering, Duration.ofHours(1), 100);
6465
listener = new TestSnapshotListener(createConfig(), mock(ProbeStatusSink.class));
6566
DebuggerAgentHelper.injectSink(listener);
6667
}
@@ -203,7 +204,7 @@ public void doubleNestedException() {
203204
public void filteringOutErrors() {
204205
ExceptionProbeManager manager = mock(ExceptionProbeManager.class);
205206
exceptionDebugger =
206-
new DefaultExceptionDebugger(manager, configurationUpdater, classNameFiltering);
207+
new DefaultExceptionDebugger(manager, configurationUpdater, classNameFiltering, 100);
207208
exceptionDebugger.handleException(new AssertionError("test"), mock(AgentSpan.class));
208209
verify(manager, times(0)).isAlreadyInstrumented(any());
209210
}

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/ExceptionProbeInstrumentationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ private TestSnapshotListener setupExceptionDebugging(
326326
DebuggerContext.initValueSerializer(new JsonSnapshotSerializer());
327327
DefaultExceptionDebugger exceptionDebugger =
328328
new DefaultExceptionDebugger(
329-
exceptionProbeManager, configurationUpdater, classNameFiltering);
329+
exceptionProbeManager, configurationUpdater, classNameFiltering, 100);
330330
DebuggerContext.initExceptionDebugger(exceptionDebugger);
331331
configurationUpdater.accept(REMOTE_CONFIG, null);
332332
return listener;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.datadog.debugger.util;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import java.time.Duration;
6+
import java.util.concurrent.locks.LockSupport;
7+
import org.junit.jupiter.api.Test;
8+
9+
class CircuitBreakerTest {
10+
11+
@Test
12+
void noBreaker() {
13+
CircuitBreaker cb = new CircuitBreaker(3, Duration.ofMillis(10));
14+
for (int i = 0; i < 10; i++) {
15+
assertTrue(cb.trip());
16+
LockSupport.parkNanos(Duration.ofMillis(50).toNanos());
17+
}
18+
}
19+
20+
@Test
21+
void breaker() {
22+
CircuitBreaker cb = new CircuitBreaker(3, Duration.ofMillis(200));
23+
for (int i = 0; i < 3; i++) {
24+
assertTrue(cb.trip());
25+
}
26+
for (int i = 0; i < 100; i++) {
27+
assertFalse(cb.trip());
28+
}
29+
LockSupport.parkNanos(Duration.ofMillis(250).toNanos());
30+
for (int i = 0; i < 3; i++) {
31+
assertTrue(cb.trip());
32+
}
33+
for (int i = 0; i < 100; i++) {
34+
assertFalse(cb.trip());
35+
}
36+
}
37+
}

dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.groovy

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,18 @@ abstract class AgentTestRunner extends DDSpecification implements AgentBuilder.L
546546
return delegate.getDataTop(key)
547547
}
548548

549+
@Override
550+
void setMetaStructTop(String key, Object value) {
551+
check()
552+
delegate.setMetaStructTop(key, value)
553+
}
554+
555+
@Override
556+
void setMetaStructCurrent(String key, Object value) {
557+
check()
558+
delegate.setMetaStructCurrent(key, value)
559+
}
560+
549561
@Override
550562
void effectivelyBlocked() {
551563
check()

dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ public final class ConfigDefaults {
174174
static final boolean DEFAULT_DEBUGGER_SYMBOL_FORCE_UPLOAD = false;
175175
static final int DEFAULT_DEBUGGER_SYMBOL_FLUSH_THRESHOLD = 100; // nb of classes
176176
static final boolean DEFAULT_DEBUGGER_EXCEPTION_ENABLED = false;
177+
static final int DEFAULT_DEBUGGER_MAX_EXCEPTION_PER_SECOND = 100;
177178
static final boolean DEFAULT_DEBUGGER_EXCEPTION_ONLY_LOCAL_ROOT = true;
178179

179180
static final boolean DEFAULT_TRACE_REPORT_HOSTNAME = false;

dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public final class DebuggerConfig {
3131
public static final String DEBUGGER_SYMBOL_FLUSH_THRESHOLD = "symbol.database.flush.threshold";
3232
public static final String DEBUGGER_EXCEPTION_ENABLED = "exception.debugging.enabled";
3333
public static final String EXCEPTION_REPLAY_ENABLED = "exception.replay.enabled";
34+
public static final String DEBUGGER_MAX_EXCEPTION_PER_SECOND =
35+
"exception.replay.max.exception.analysis.limit";
3436
public static final String DEBUGGER_EXCEPTION_ONLY_LOCAL_ROOT =
3537
"internal.exception.replay.only.local.root";
3638
public static final String THIRD_PARTY_INCLUDES = "third.party.includes";

0 commit comments

Comments
 (0)