Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pull_requests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
version: [8,11,17]
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
Expand Down
14 changes: 10 additions & 4 deletions src/main/java/io/getunleash/metric/DefaultHttpMetricsSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,32 @@ public DefaultHttpMetricsSender(UnleashConfig unleashConfig) {
.create();
}

public void registerClient(ClientRegistration registration) {
public int registerClient(ClientRegistration registration) {
if (!unleashConfig.isDisableMetrics()) {
try {
post(clientRegistrationURL, registration);
int statusCode = post(clientRegistrationURL, registration);
eventDispatcher.dispatch(registration);
return statusCode;
} catch (UnleashException ex) {
eventDispatcher.dispatch(ex);
return -1;
}
}
return -1;
}

public void sendMetrics(ClientMetrics metrics) {
public int sendMetrics(ClientMetrics metrics) {
if (!unleashConfig.isDisableMetrics()) {
try {
post(clientMetricsURL, metrics);
int statusCode = post(clientMetricsURL, metrics);
eventDispatcher.dispatch(metrics);
return statusCode;
} catch (UnleashException ex) {
eventDispatcher.dispatch(ex);
return -1;
}
}
return -1;
}

private int post(URL url, Object o) throws UnleashException {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/getunleash/metric/MetricSender.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.getunleash.metric;

public interface MetricSender {
void registerClient(ClientRegistration registration);
int registerClient(ClientRegistration registration);

void sendMetrics(ClientMetrics metrics);
int sendMetrics(ClientMetrics metrics);
}
9 changes: 6 additions & 3 deletions src/main/java/io/getunleash/metric/OkHttpMetricsSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,21 @@ public OkHttpMetricsSender(UnleashConfig config) {
}

@Override
public void registerClient(ClientRegistration registration) {
public int registerClient(ClientRegistration registration) {
if (!config.isDisableMetrics()) {
try {
post(clientRegistrationUrl, registration);
int statusCode = post(clientRegistrationUrl, registration);
eventDispatcher.dispatch(registration);
return statusCode;
} catch (UnleashException ex) {
eventDispatcher.dispatch(ex);
}
}
return -1;
}

@Override
public void sendMetrics(ClientMetrics metrics) {
public int sendMetrics(ClientMetrics metrics) {
if (!config.isDisableMetrics()) {
try {
post(clientMetricsUrl, metrics);
Expand All @@ -81,6 +83,7 @@ public void sendMetrics(ClientMetrics metrics) {
eventDispatcher.dispatch(ex);
}
}
return -1;
}

private int post(HttpUrl url, Object o) {
Expand Down
39 changes: 34 additions & 5 deletions src/main/java/io/getunleash/metric/UnleashMetricServiceImpl.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package io.getunleash.metric;

import io.getunleash.util.Throttler;
import io.getunleash.util.UnleashConfig;
import io.getunleash.util.UnleashScheduledExecutor;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UnleashMetricServiceImpl implements UnleashMetricService {
private static final Logger LOGGER = LoggerFactory.getLogger(UnleashMetricServiceImpl.class);
private final LocalDateTime started;
private final UnleashConfig unleashConfig;

Expand All @@ -15,6 +19,8 @@ public class UnleashMetricServiceImpl implements UnleashMetricService {
// mutable
private volatile MetricsBucket currentMetricsBucket;

private final Throttler throttler;

public UnleashMetricServiceImpl(
UnleashConfig unleashConfig, UnleashScheduledExecutor executor) {
this(unleashConfig, unleashConfig.getMetricSenderFactory().apply(unleashConfig), executor);
Expand All @@ -28,6 +34,11 @@ public UnleashMetricServiceImpl(
this.started = LocalDateTime.now(ZoneId.of("UTC"));
this.unleashConfig = unleashConfig;
this.metricSender = metricSender;
this.throttler =
new Throttler(
(int) unleashConfig.getSendMetricsInterval(),
300,
unleashConfig.getUnleashURLs().getClientMetricsURL());
long metricsInterval = unleashConfig.getSendMetricsInterval();
executor.setInterval(sendMetrics(), metricsInterval, metricsInterval);
}
Expand All @@ -51,11 +62,29 @@ public void countVariant(String toggleName, String variantName) {

private Runnable sendMetrics() {
return () -> {
MetricsBucket metricsBucket = this.currentMetricsBucket;
this.currentMetricsBucket = new MetricsBucket();
metricsBucket.end();
ClientMetrics metrics = new ClientMetrics(unleashConfig, metricsBucket);
metricSender.sendMetrics(metrics);
if (throttler.performAction()) {
MetricsBucket metricsBucket = this.currentMetricsBucket;
this.currentMetricsBucket = new MetricsBucket();
metricsBucket.end();
ClientMetrics metrics = new ClientMetrics(unleashConfig, metricsBucket);
int statusCode = metricSender.sendMetrics(metrics);
if (statusCode >= 200 && statusCode < 400) {
throttler.decrementFailureCountAndResetSkips();
}
if (statusCode >= 400) {
throttler.handleHttpErrorCodes(statusCode);
}
} else {
throttler.skipped();
}
};
}

protected int getSkips() {
return this.throttler.getSkips();
}

protected int getFailures() {
return this.throttler.getFailures();
}
}
91 changes: 64 additions & 27 deletions src/main/java/io/getunleash/repository/FeatureRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
import io.getunleash.event.EventDispatcher;
import io.getunleash.event.UnleashReady;
import io.getunleash.lang.Nullable;
import io.getunleash.util.Throttler;
import io.getunleash.util.UnleashConfig;
import io.getunleash.util.UnleashScheduledExecutor;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FeatureRepository implements IFeatureRepository {

private static final Logger LOGGER = LoggerFactory.getLogger(FeatureRepository.class);
private final UnleashConfig unleashConfig;
private final BackupHandler<FeatureCollection> featureBackupHandler;
private final FeatureBootstrapHandler featureBootstrapHandler;
private final FeatureFetcher featureFetcher;
private final EventDispatcher eventDispatcher;

private final Throttler throttler;

private FeatureCollection featureCollection;
private boolean ready;

Expand All @@ -36,7 +41,11 @@ public FeatureRepository(
this.featureFetcher = unleashConfig.getUnleashFeatureFetcherFactory().apply(unleashConfig);
this.featureBootstrapHandler = new FeatureBootstrapHandler(unleashConfig);
this.eventDispatcher = new EventDispatcher(unleashConfig);

this.throttler =
new Throttler(
(int) unleashConfig.getFetchTogglesInterval(),
300,
unleashConfig.getUnleashURLs().getFetchTogglesURL());
this.initCollections(unleashConfig.getScheduledExecutor());
}

Expand All @@ -46,12 +55,16 @@ protected FeatureRepository(
EventDispatcher eventDispatcher,
FeatureFetcher featureFetcher,
FeatureBootstrapHandler featureBootstrapHandler) {

this.unleashConfig = unleashConfig;
this.featureBackupHandler = featureBackupHandler;
this.featureFetcher = featureFetcher;
this.featureBootstrapHandler = featureBootstrapHandler;
this.eventDispatcher = eventDispatcher;
this.throttler =
new Throttler(
(int) unleashConfig.getFetchTogglesInterval(),
300,
unleashConfig.getUnleashURLs().getFetchTogglesURL());
this.initCollections(unleashConfig.getScheduledExecutor());
}

Expand All @@ -66,6 +79,11 @@ protected FeatureRepository(
this.featureFetcher = featureFetcher;
this.featureBootstrapHandler = featureBootstrapHandler;
this.eventDispatcher = new EventDispatcher(unleashConfig);
this.throttler =
new Throttler(
(int) unleashConfig.getFetchTogglesInterval(),
300,
unleashConfig.getUnleashURLs().getFetchTogglesURL());
this.initCollections(executor);
}

Expand All @@ -90,33 +108,44 @@ private void initCollections(UnleashScheduledExecutor executor) {
}
}

private Integer calculateMaxSkips(int fetchTogglesInterval) {
return Integer.max(20, 300 / Integer.max(fetchTogglesInterval, 1));
}

private Runnable updateFeatures(@Nullable final Consumer<UnleashException> handler) {
return () -> {
try {
ClientFeaturesResponse response = featureFetcher.fetchFeatures();
eventDispatcher.dispatch(response);
if (response.getStatus() == ClientFeaturesResponse.Status.CHANGED) {
SegmentCollection segmentCollection = response.getSegmentCollection();
featureCollection =
new FeatureCollection(
response.getToggleCollection(),
segmentCollection != null
? segmentCollection
: new SegmentCollection(Collections.emptyList()));

featureBackupHandler.write(featureCollection);
}

if (!ready) {
eventDispatcher.dispatch(new UnleashReady());
ready = true;
}
} catch (UnleashException e) {
if (handler != null) {
handler.accept(e);
} else {
throw e;
if (throttler.performAction()) {
try {
ClientFeaturesResponse response = featureFetcher.fetchFeatures();
eventDispatcher.dispatch(response);
if (response.getStatus() == ClientFeaturesResponse.Status.CHANGED) {
SegmentCollection segmentCollection = response.getSegmentCollection();
featureCollection =
new FeatureCollection(
response.getToggleCollection(),
segmentCollection != null
? segmentCollection
: new SegmentCollection(Collections.emptyList()));

featureBackupHandler.write(featureCollection);
} else if (response.getStatus() == ClientFeaturesResponse.Status.UNAVAILABLE) {
throttler.handleHttpErrorCodes(response.getHttpStatusCode());
return;
}
throttler.decrementFailureCountAndResetSkips();
if (!ready) {
eventDispatcher.dispatch(new UnleashReady());
ready = true;
}
} catch (UnleashException e) {
if (handler != null) {
handler.accept(e);
} else {
throw e;
}
}
} else {
throttler.skipped(); // We didn't do anything this iteration, just reduce the count
}
};
}
Expand All @@ -137,4 +166,12 @@ public List<String> getFeatureNames() {
public Segment getSegment(Integer id) {
return featureCollection.getSegmentCollection().getSegment(id);
}

public Integer getFailures() {
return this.throttler.getFailures();
}

public Integer getSkips() {
return this.throttler.getSkips();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class FeatureToggleResponse implements UnleashEvent {
public enum Status {
NOT_CHANGED,
CHANGED,
UNAVAILABLE
UNAVAILABLE,
}

private final Status status;
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/getunleash/util/ConstraintMerger.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public static List<Constraint> mergeConstraints(
Optional.ofNullable(strategy.getConstraints())
.orElseGet(Collections::emptyList),
Optional.ofNullable(strategy.getSegments())
.orElseGet(Collections::emptyList).stream()
.orElseGet(Collections::emptyList)
.stream()
.map(repository::getSegment)
.map(s -> s == null ? DENY_SEGMENT : s)
.map(Segment::getConstraints)
Expand Down
Loading