Skip to content

Commit a216a21

Browse files
Update modules
1 parent b7979d4 commit a216a21

File tree

79 files changed

+1859
-1445
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1859
-1445
lines changed

dd-java-agent/agent-featureflag/build.gradle

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,6 @@ java {
1212
targetCompatibility = JavaVersion.VERSION_1_8
1313
}
1414

15-
excludedClassesCoverage += [
16-
// Exposure POJO classes
17-
'com.datadog.featureflag.exposure.Allocation',
18-
'com.datadog.featureflag.exposure.ExposureEvent',
19-
'com.datadog.featureflag.exposure.ExposuresRequest',
20-
'com.datadog.featureflag.exposure.Flag',
21-
'com.datadog.featureflag.exposure.Subject',
22-
'com.datadog.featureflag.exposure.Variant',
23-
// UFC v1 POJO classes
24-
'com.datadog.featureflag.ufc.v1.Allocation',
25-
'com.datadog.featureflag.ufc.v1.ConditionConfiguration',
26-
'com.datadog.featureflag.ufc.v1.ConditionOperator',
27-
'com.datadog.featureflag.ufc.v1.Environment',
28-
'com.datadog.featureflag.ufc.v1.Flag',
29-
'com.datadog.featureflag.ufc.v1.Rule',
30-
'com.datadog.featureflag.ufc.v1.ServerConfiguration',
31-
'com.datadog.featureflag.ufc.v1.Shard',
32-
'com.datadog.featureflag.ufc.v1.ShardRange',
33-
'com.datadog.featureflag.ufc.v1.Split',
34-
'com.datadog.featureflag.ufc.v1.ValueType',
35-
'com.datadog.featureflag.ufc.v1.Variant'
36-
]
37-
3815
dependencies {
3916
api libs.slf4j
4017
implementation libs.moshi
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
package com.datadog.featureflag;
22

3-
import com.datadog.featureflag.exposure.ExposureEvent;
3+
import datadog.trace.api.featureflag.FeatureFlagGateway;
44

55
/**
66
* Defines an exposure writer responsible for sending exposure events to the EVP proxy.
77
* Implementations should use a background thread to perform these operations asynchronously.
88
*/
9-
public interface ExposureWriter extends AutoCloseable {
9+
public interface ExposureWriter extends AutoCloseable, FeatureFlagGateway.ExposureListener {
1010

1111
void init();
1212

1313
void close();
14-
15-
void write(ExposureEvent event);
1614
}

dd-java-agent/agent-featureflag/src/main/java/com/datadog/featureflag/ExposureWriterEvaluatorAdapter.java

Lines changed: 0 additions & 129 deletions
This file was deleted.

dd-java-agent/agent-featureflag/src/main/java/com/datadog/featureflag/ExposureWriterImpl.java

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
package com.datadog.featureflag;
22

3-
import static datadog.trace.util.AgentThreadFactory.AgentThread.LLMOBS_EVALS_PROCESSOR;
3+
import static datadog.trace.util.AgentThreadFactory.AgentThread.FEATURE_FLAG_EXPOSURE_PROCESSOR;
44
import static datadog.trace.util.AgentThreadFactory.newAgentThread;
5+
import static java.util.concurrent.TimeUnit.SECONDS;
56

6-
import com.datadog.featureflag.exposure.ExposureEvent;
7-
import com.datadog.featureflag.exposure.ExposuresRequest;
87
import com.squareup.moshi.JsonAdapter;
98
import com.squareup.moshi.Moshi;
109
import datadog.communication.ddagent.DDAgentFeaturesDiscovery;
1110
import datadog.communication.http.HttpRetryPolicy;
1211
import datadog.communication.http.OkHttpUtils;
1312
import datadog.trace.api.Config;
13+
import datadog.trace.api.featureflag.FeatureFlagGateway;
14+
import datadog.trace.api.featureflag.exposure.ExposureEvent;
15+
import datadog.trace.api.featureflag.exposure.ExposuresRequest;
16+
import datadog.trace.core.util.LRUCache;
1417
import java.util.ArrayList;
18+
import java.util.Collections;
1519
import java.util.HashMap;
1620
import java.util.List;
1721
import java.util.Map;
22+
import java.util.Set;
1823
import java.util.concurrent.TimeUnit;
1924
import okhttp3.Headers;
2025
import okhttp3.HttpUrl;
@@ -27,16 +32,24 @@
2732

2833
public class ExposureWriterImpl implements ExposureWriter {
2934

35+
private static final Logger LOGGER = LoggerFactory.getLogger(ExposureWriterImpl.class);
36+
private static final int DEFAULT_CAPACITY = 1 << 16; // 65536 elements
37+
private static final int DEFAULT_FLUSH_INTERVAL_IN_SECONDS = 1;
38+
private static final int FLUSH_THRESHOLD = 50;
3039
private static final String EXPOSURES_API_PATH = "api/v2/exposures";
3140
private static final String EVP_SUBDOMAIN_HEADER_NAME = "X-Datadog-EVP-Subdomain";
3241
private static final String EVP_SUBDOMAIN_HEADER_VALUE = "event-platform-intake";
33-
34-
private static final Logger log = LoggerFactory.getLogger(ExposureWriterImpl.class);
42+
private static final HttpRetryPolicy.Factory RETRY_POLICY =
43+
new HttpRetryPolicy.Factory(5, 100, 2.0, true);
3544

3645
private final MpscBlockingConsumerArrayQueue<ExposureEvent> queue;
3746
private final Thread serializerThread;
3847

39-
public ExposureWriterImpl(
48+
public ExposureWriterImpl(final HttpUrl agentUrl, final Config config) {
49+
this(DEFAULT_CAPACITY, DEFAULT_FLUSH_INTERVAL_IN_SECONDS, SECONDS, agentUrl, config);
50+
}
51+
52+
ExposureWriterImpl(
4053
final int capacity,
4154
final long flushInterval,
4255
final TimeUnit timeUnit,
@@ -59,27 +72,27 @@ public ExposureWriterImpl(
5972
}
6073
final ExposureSerializingHandler serializer =
6174
new ExposureSerializingHandler(queue, flushInterval, timeUnit, url, headers, context);
62-
this.serializerThread = newAgentThread(LLMOBS_EVALS_PROCESSOR, serializer);
75+
this.serializerThread = newAgentThread(FEATURE_FLAG_EXPOSURE_PROCESSOR, serializer);
6376
}
6477

6578
@Override
6679
public void init() {
80+
FeatureFlagGateway.addExposureListener(this);
6781
this.serializerThread.start();
6882
}
6983

7084
@Override
7185
public void close() {
86+
FeatureFlagGateway.removeExposureListener(this);
7287
this.serializerThread.interrupt();
7388
}
7489

7590
@Override
76-
public void write(final ExposureEvent event) {
91+
public void accept(final ExposureEvent event) {
7792
queue.offer(event);
7893
}
7994

8095
private static class ExposureSerializingHandler implements Runnable {
81-
private static final int FLUSH_THRESHOLD = 50;
82-
8396
private final MpscBlockingConsumerArrayQueue<ExposureEvent> queue;
8497
private final long ticksRequiredToFlush;
8598
private long lastTicks;
@@ -90,6 +103,7 @@ private static class ExposureSerializingHandler implements Runnable {
90103
private final Headers headers;
91104

92105
private final Map<String, String> context;
106+
private final Set<ExposureEvent> cache;
93107

94108
private final List<ExposureEvent> buffer = new ArrayList<>();
95109

@@ -101,6 +115,7 @@ public ExposureSerializingHandler(
101115
final Headers headers,
102116
final Map<String, String> context) {
103117
this.queue = queue;
118+
this.cache = Collections.newSetFromMap(new LRUCache<>(queue.capacity()));
104119
this.jsonAdapter = new Moshi.Builder().build().adapter(ExposuresRequest.class);
105120
this.httpClient = new OkHttpClient();
106121
this.submissionUrl = submissionUrl;
@@ -110,7 +125,7 @@ public ExposureSerializingHandler(
110125
this.lastTicks = System.nanoTime();
111126
this.ticksRequiredToFlush = timeUnit.toNanos(flushInterval);
112127

113-
log.debug("starting exposure serializer, url={}", submissionUrl);
128+
LOGGER.debug("starting exposure serializer, url={}", submissionUrl);
114129
}
115130

116131
@Override
@@ -120,25 +135,36 @@ public void run() {
120135
} catch (InterruptedException e) {
121136
Thread.currentThread().interrupt();
122137
}
123-
log.debug(
138+
LOGGER.debug(
124139
"exposure processor worker exited. submitting exposures stopped. unsubmitted exposures left: {}",
125-
!queuesAreEmpty());
140+
!queue.isEmpty());
126141
}
127142

128143
private void runDutyCycle() throws InterruptedException {
129144
final Thread thread = Thread.currentThread();
130145
while (!thread.isInterrupted()) {
131-
final ExposureEvent event = queue.poll(100, TimeUnit.MILLISECONDS);
132-
if (event != null) {
133-
buffer.add(event);
134-
consumeBatch();
146+
ExposureEvent event;
147+
while ((event = queue.poll(100, TimeUnit.MILLISECONDS)) != null) {
148+
if (addToBuffer(event)) {
149+
consumeBatch();
150+
break;
151+
}
135152
}
136153
flushIfNecessary();
137154
}
138155
}
139156

140157
private void consumeBatch() {
141-
queue.drain(buffer::add, queue.size());
158+
queue.drain(this::addToBuffer, queue.size());
159+
}
160+
161+
/** Adds an element to the buffer taking care of duplicated exposures thanks to the LRU cache */
162+
private boolean addToBuffer(ExposureEvent event) {
163+
if (cache.add(event)) {
164+
buffer.add(event);
165+
return true;
166+
}
167+
return false;
142168
}
143169

144170
protected void flushIfNecessary() {
@@ -147,27 +173,22 @@ protected void flushIfNecessary() {
147173
}
148174
if (shouldFlush()) {
149175
final ExposuresRequest exposures = new ExposuresRequest(this.context, this.buffer);
150-
final HttpRetryPolicy.Factory retryPolicyFactory =
151-
new HttpRetryPolicy.Factory(5, 100, 2.0, true);
152176
final String reqBod = jsonAdapter.toJson(exposures);
153177
final RequestBody requestBody =
154178
RequestBody.create(okhttp3.MediaType.parse("application/json"), reqBod);
155179
final Request request =
156180
new Request.Builder().headers(headers).url(submissionUrl).post(requestBody).build();
157181
try (okhttp3.Response response =
158-
OkHttpUtils.sendWithRetries(httpClient, retryPolicyFactory, request)) {
159-
182+
OkHttpUtils.sendWithRetries(httpClient, RETRY_POLICY, request)) {
160183
if (response.isSuccessful()) {
161-
log.debug("successfully flushed exposures request with {} evals", this.buffer.size());
184+
LOGGER.debug(
185+
"successfully flushed exposures request with {} evals", this.buffer.size());
162186
this.buffer.clear();
163187
} else {
164-
log.error(
165-
"Could not submit exposures (HTTP code {}) {}",
166-
response.code(),
167-
response.body() != null ? response.body().string() : "");
188+
LOGGER.error("Could not submit exposures (HTTP code {})", response.code());
168189
}
169190
} catch (Exception e) {
170-
log.error("Could not submit exposures", e);
191+
LOGGER.error("Could not submit exposures", e);
171192
}
172193
}
173194
}
@@ -181,9 +202,5 @@ private boolean shouldFlush() {
181202
}
182203
return false;
183204
}
184-
185-
protected boolean queuesAreEmpty() {
186-
return queue.isEmpty();
187-
}
188205
}
189206
}

0 commit comments

Comments
 (0)