Litmus prometheus integration
This commit is contained in:
@@ -75,6 +75,13 @@
|
|||||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
<version>2.13.0</version>
|
<version>2.13.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||||
|
<version>1.8.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package com.navi.medici.client;
|
|||||||
|
|
||||||
import com.navi.medici.clickstream.ClickStreamPayload;
|
import com.navi.medici.clickstream.ClickStreamPayload;
|
||||||
import com.navi.medici.config.LitmusConfig;
|
import com.navi.medici.config.LitmusConfig;
|
||||||
|
import com.navi.medici.response.LitmusExperimentResponse;
|
||||||
import com.navi.medici.util.JacksonUtils;
|
import com.navi.medici.util.JacksonUtils;
|
||||||
|
import io.micrometer.core.instrument.Counter;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
@@ -24,7 +26,7 @@ public class ClickStreamClient {
|
|||||||
this.client = new OkHttpClient();
|
this.client = new OkHttpClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void publish(ClickStreamPayload<T> payload) {
|
public <T> void publish(String experimentName, ClickStreamPayload<T> payload) {
|
||||||
String requestBody = JacksonUtils.objectToString(payload);
|
String requestBody = JacksonUtils.objectToString(payload);
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(this.litmusConfig.getClickStreamAPI())
|
.url(this.litmusConfig.getClickStreamAPI())
|
||||||
@@ -32,6 +34,13 @@ public class ClickStreamClient {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
try(Response response = client.newCall(request).execute()) {
|
try(Response response = client.newCall(request).execute()) {
|
||||||
|
Counter.builder("litmus_client_click_stream_event_ingestion")
|
||||||
|
.tag("vertical", litmusConfig.getVertical())
|
||||||
|
.tag("status", String.valueOf(response.code()))
|
||||||
|
.tag("experiment_name", experimentName)
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
if (response.code() < 300) {
|
if (response.code() < 300) {
|
||||||
var responseBody = response.body();
|
var responseBody = response.body();
|
||||||
assert responseBody != null;
|
assert responseBody != null;
|
||||||
@@ -41,6 +50,14 @@ public class ClickStreamClient {
|
|||||||
log.error("clickstream ingestion received non-2xx. status: {}", response.code());
|
log.error("clickstream ingestion received non-2xx. status: {}", response.code());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Counter.builder("litmus_client_click_stream_event_ingestion")
|
||||||
|
.tag("vertical", litmusConfig.getVertical())
|
||||||
|
.tag("experiment_name", experimentName)
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.tag("exception", e.getMessage())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
|
|
||||||
log.error("clickstream event ingestion failed. ", e);
|
log.error("clickstream event ingestion failed. ", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ import com.navi.medici.response.LitmusExperimentResponse;
|
|||||||
|
|
||||||
public interface ExperimentFetcher {
|
public interface ExperimentFetcher {
|
||||||
|
|
||||||
LitmusExperimentResponse fetchExperiments() throws LitmusException;
|
LitmusExperimentResponse fetchExperiments(String vertical, Long pollingTime) throws LitmusException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.navi.medici.response.LitmusExperimentCollection;
|
|||||||
import com.navi.medici.response.LitmusExperimentResponse;
|
import com.navi.medici.response.LitmusExperimentResponse;
|
||||||
import com.navi.medici.response.LitmusResponse;
|
import com.navi.medici.response.LitmusResponse;
|
||||||
import com.navi.medici.util.JacksonUtils;
|
import com.navi.medici.util.JacksonUtils;
|
||||||
|
import io.micrometer.core.instrument.Counter;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
@@ -14,10 +15,10 @@ import okhttp3.Response;
|
|||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public class HttpExperimentFetcher implements ExperimentFetcher {
|
public class HttpExperimentFetcher implements ExperimentFetcher {
|
||||||
|
|
||||||
private final OkHttpClient client;
|
private final OkHttpClient client;
|
||||||
private final URL experimentUrl;
|
private final URL experimentUrl;
|
||||||
private final URL segmentIdUrl;
|
private final URL segmentIdUrl;
|
||||||
|
private final LitmusConfig litmusConfig;
|
||||||
|
|
||||||
public HttpExperimentFetcher(LitmusConfig litmusConfig) {
|
public HttpExperimentFetcher(LitmusConfig litmusConfig) {
|
||||||
this.client = new OkHttpClient();
|
this.client = new OkHttpClient();
|
||||||
@@ -25,29 +26,64 @@ public class HttpExperimentFetcher implements ExperimentFetcher {
|
|||||||
litmusConfig.getLitmusURLs()
|
litmusConfig.getLitmusURLs()
|
||||||
.getLitmusExperimentsURL(litmusConfig.getProjectName(), litmusConfig.getNamePrefix());
|
.getLitmusExperimentsURL(litmusConfig.getProjectName(), litmusConfig.getNamePrefix());
|
||||||
this.segmentIdUrl = litmusConfig.getLitmusURLs().getSegmentIdURL();
|
this.segmentIdUrl = litmusConfig.getLitmusURLs().getSegmentIdURL();
|
||||||
|
this.litmusConfig = litmusConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LitmusExperimentResponse fetchExperiments() throws LitmusException {
|
public LitmusExperimentResponse fetchExperiments(String vertical, Long pollingTime) throws LitmusException {
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(this.experimentUrl)
|
.url(String.format("%s?vertical=%s&polling_time=%s", this.experimentUrl, vertical, pollingTime))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
Counter.builder("litmus_client_fetch_experiment_polling")
|
||||||
|
.tag("vertical", vertical)
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
|
|
||||||
if (response.code() < 300) {
|
if (response.code() < 300) {
|
||||||
var responseBody = response.body();
|
var responseBody = response.body();
|
||||||
assert responseBody != null;
|
assert responseBody != null;
|
||||||
|
|
||||||
LitmusExperimentCollection experiments = JacksonUtils.stringToObject(responseBody.string(), LitmusExperimentCollection.class);
|
LitmusExperimentCollection experiments = JacksonUtils.stringToObject(responseBody.string(), LitmusExperimentCollection.class);
|
||||||
|
|
||||||
|
Counter.builder("litmus_client_fetch_experiment_polling_total_request")
|
||||||
|
.tag("vertical", vertical)
|
||||||
|
.tag("status", String.valueOf(response.code()))
|
||||||
|
.tag("experiment_state", LitmusExperimentResponse.Status.CHANGED.name())
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
return new LitmusExperimentResponse(LitmusExperimentResponse.Status.CHANGED, experiments);
|
return new LitmusExperimentResponse(LitmusExperimentResponse.Status.CHANGED, experiments);
|
||||||
} else if(response.code() == 304) {
|
} else if (response.code() == 304) {
|
||||||
|
Counter.builder("litmus_client_fetch_experiment_polling_request_status")
|
||||||
|
.tag("vertical", vertical)
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.tag("status", String.valueOf(response.code()))
|
||||||
|
.tag("experiment_state", LitmusExperimentResponse.Status.NOT_CHANGED.name())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
|
|
||||||
return new LitmusExperimentResponse(LitmusExperimentResponse.Status.NOT_CHANGED, response.code());
|
return new LitmusExperimentResponse(LitmusExperimentResponse.Status.NOT_CHANGED, response.code());
|
||||||
} else {
|
} else {
|
||||||
|
Counter.builder("litmus_client_fetch_experiment_polling_request_status")
|
||||||
|
.tag("vertical", vertical)
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.tag("status", String.valueOf(response.code()))
|
||||||
|
.tag("experiment_state", LitmusExperimentResponse.Status.NOT_CHANGED.name())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
|
|
||||||
return new LitmusExperimentResponse(LitmusExperimentResponse.Status.UNAVAILABLE, response.code());
|
return new LitmusExperimentResponse(LitmusExperimentResponse.Status.UNAVAILABLE, response.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Counter.builder("litmus_client_fetch_experiment_polling_request_failed")
|
||||||
|
.tag("vertical", vertical)
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
throw new LitmusException("fetch experiments failed.", e);
|
throw new LitmusException("fetch experiments failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,12 +93,26 @@ public class HttpExperimentFetcher implements ExperimentFetcher {
|
|||||||
.url(String.format("%s?segment_name=%s&id=%s", this.segmentIdUrl, segmentName, id))
|
.url(String.format("%s?segment_name=%s&id=%s", this.segmentIdUrl, segmentName, id))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try(Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
var responseBody = response.body();
|
var responseBody = response.body();
|
||||||
assert responseBody != null;
|
assert responseBody != null;
|
||||||
|
|
||||||
|
Counter.builder("litmus_client_segment_id_exist_request")
|
||||||
|
.tag("vertical", litmusConfig.getVertical())
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.tag("status", String.valueOf(response.code()))
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
return JacksonUtils.stringToObject(responseBody.string(), LitmusResponse.class);
|
return JacksonUtils.stringToObject(responseBody.string(), LitmusResponse.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Counter.builder("litmus_client_segment_id_exist_request_failed")
|
||||||
|
.tag("vertical", litmusConfig.getVertical())
|
||||||
|
.tag("segment_name", segmentName)
|
||||||
|
.tag("app_name", litmusConfig.getAppName())
|
||||||
|
.tag("exception", e.getMessage())
|
||||||
|
.register(this.litmusConfig.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
|
|
||||||
throw new LitmusException("segment name to id matching exists.", e);
|
throw new LitmusException("segment name to id matching exists.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.navi.medici.scheduler.LitmusScheduledExecutorImpl;
|
|||||||
import com.navi.medici.strategy.Strategy;
|
import com.navi.medici.strategy.Strategy;
|
||||||
import com.navi.medici.strategy.UnknownStrategy;
|
import com.navi.medici.strategy.UnknownStrategy;
|
||||||
import com.navi.medici.util.LitmusURLs;
|
import com.navi.medici.util.LitmusURLs;
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@@ -37,6 +38,8 @@ public class LitmusConfig {
|
|||||||
private final Strategy fallbackStrategy;
|
private final Strategy fallbackStrategy;
|
||||||
private final LitmusExperimentBootstrapProvider litmusExperimentBootstrapProvider;
|
private final LitmusExperimentBootstrapProvider litmusExperimentBootstrapProvider;
|
||||||
private final String clickStreamAPI;
|
private final String clickStreamAPI;
|
||||||
|
private final String vertical;
|
||||||
|
private final MeterRegistry meterRegistry;
|
||||||
|
|
||||||
|
|
||||||
private LitmusConfig(
|
private LitmusConfig(
|
||||||
@@ -52,7 +55,9 @@ public class LitmusConfig {
|
|||||||
LitmusScheduledExecutor litmusScheduledExecutor,
|
LitmusScheduledExecutor litmusScheduledExecutor,
|
||||||
Strategy fallbackStrategy,
|
Strategy fallbackStrategy,
|
||||||
LitmusExperimentBootstrapProvider litmusBootstrapProvider,
|
LitmusExperimentBootstrapProvider litmusBootstrapProvider,
|
||||||
String clickStreamAPI) {
|
String clickStreamAPI,
|
||||||
|
String vertical,
|
||||||
|
MeterRegistry meterRegistry) {
|
||||||
|
|
||||||
if (appName == null) {
|
if (appName == null) {
|
||||||
throw new IllegalStateException("You are required to specify the litmus appName");
|
throw new IllegalStateException("You are required to specify the litmus appName");
|
||||||
@@ -70,6 +75,19 @@ public class LitmusConfig {
|
|||||||
throw new IllegalStateException("You are required to specify a scheduler");
|
throw new IllegalStateException("You are required to specify a scheduler");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clickStreamAPI == null) {
|
||||||
|
throw new IllegalStateException("You are required to specify a scheduler");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vertical == null) {
|
||||||
|
throw new IllegalStateException("You are required to specify vertical");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meterRegistry == null) {
|
||||||
|
throw new IllegalStateException("You are required to specify meter registry");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.fallbackStrategy = Objects.requireNonNullElseGet(fallbackStrategy, UnknownStrategy::new);
|
this.fallbackStrategy = Objects.requireNonNullElseGet(fallbackStrategy, UnknownStrategy::new);
|
||||||
|
|
||||||
this.litmusAPI = litmusAPI;
|
this.litmusAPI = litmusAPI;
|
||||||
@@ -85,6 +103,8 @@ public class LitmusConfig {
|
|||||||
this.litmusScheduledExecutor = litmusScheduledExecutor;
|
this.litmusScheduledExecutor = litmusScheduledExecutor;
|
||||||
this.litmusExperimentBootstrapProvider = litmusBootstrapProvider;
|
this.litmusExperimentBootstrapProvider = litmusBootstrapProvider;
|
||||||
this.clickStreamAPI = clickStreamAPI;
|
this.clickStreamAPI = clickStreamAPI;
|
||||||
|
this.vertical = vertical;
|
||||||
|
this.meterRegistry = meterRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
@@ -150,6 +170,12 @@ public class LitmusConfig {
|
|||||||
return clickStreamAPI;
|
return clickStreamAPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getVertical() {return vertical;}
|
||||||
|
|
||||||
|
public MeterRegistry getMeterRegistry() {
|
||||||
|
return meterRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private URI litmusAPI;
|
private URI litmusAPI;
|
||||||
@@ -165,6 +191,8 @@ public class LitmusConfig {
|
|||||||
private @Nullable Strategy fallbackStrategy;
|
private @Nullable Strategy fallbackStrategy;
|
||||||
private @Nullable LitmusExperimentBootstrapProvider litmusExperimentBootstrapProvider;
|
private @Nullable LitmusExperimentBootstrapProvider litmusExperimentBootstrapProvider;
|
||||||
private @Nullable String clickStreamAPI;
|
private @Nullable String clickStreamAPI;
|
||||||
|
private String vertical;
|
||||||
|
private MeterRegistry meterRegistry;
|
||||||
|
|
||||||
private static String getHostname() {
|
private static String getHostname() {
|
||||||
String hostName = System.getProperty("hostname");
|
String hostName = System.getProperty("hostname");
|
||||||
@@ -252,6 +280,17 @@ public class LitmusConfig {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder vertical(String vertical) {
|
||||||
|
this.vertical = vertical;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder meterRegistry(MeterRegistry meterRegistry) {
|
||||||
|
this.meterRegistry = meterRegistry;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getBackupFile() {
|
private String getBackupFile() {
|
||||||
if (backupFile != null) {
|
if (backupFile != null) {
|
||||||
return backupFile;
|
return backupFile;
|
||||||
@@ -277,7 +316,9 @@ public class LitmusConfig {
|
|||||||
.orElseGet(LitmusScheduledExecutorImpl::getInstance),
|
.orElseGet(LitmusScheduledExecutorImpl::getInstance),
|
||||||
fallbackStrategy,
|
fallbackStrategy,
|
||||||
litmusExperimentBootstrapProvider,
|
litmusExperimentBootstrapProvider,
|
||||||
clickStreamAPI);
|
clickStreamAPI,
|
||||||
|
vertical,
|
||||||
|
meterRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultSdkVersion() {
|
public String getDefaultSdkVersion() {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.navi.medici.request.v1.LitmusExperiment;
|
|||||||
import com.navi.medici.util.JacksonUtils;
|
import com.navi.medici.util.JacksonUtils;
|
||||||
import com.navi.medici.variants.Variant;
|
import com.navi.medici.variants.Variant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class EventDispatcher {
|
public class EventDispatcher {
|
||||||
private final ClickStreamClient clickStreamClient;
|
private final ClickStreamClient clickStreamClient;
|
||||||
@@ -47,11 +48,11 @@ public class EventDispatcher {
|
|||||||
.customerReferenceId(litmusContext.getUserId().orElse(""))
|
.customerReferenceId(litmusContext.getUserId().orElse(""))
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
clickStreamClient.publish(clickStreamPayload);
|
CompletableFuture.supplyAsync(() -> {
|
||||||
|
clickStreamClient.publish(litmusExperiment.getName(), clickStreamPayload);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clickstreamIngestion() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import com.navi.medici.strategy.UserWithIdStrategy;
|
|||||||
import com.navi.medici.util.JacksonUtils;
|
import com.navi.medici.util.JacksonUtils;
|
||||||
import com.navi.medici.util.VariantUtil;
|
import com.navi.medici.util.VariantUtil;
|
||||||
import com.navi.medici.variants.Variant;
|
import com.navi.medici.variants.Variant;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.Counter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -33,9 +35,12 @@ import lombok.extern.log4j.Log4j2;
|
|||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public class DefaultLitmus implements Litmus {
|
public class DefaultLitmus implements Litmus {
|
||||||
|
|
||||||
|
|
||||||
public static final UnknownStrategy UNKNOWN_STRATEGY = new UnknownStrategy();
|
public static final UnknownStrategy UNKNOWN_STRATEGY = new UnknownStrategy();
|
||||||
public static final Variant DISABLED_VARIANT = new Variant("disabled", null, false, null);
|
public static final Variant DISABLED_VARIANT = new Variant("disabled", null, false, null);
|
||||||
|
|
||||||
|
|
||||||
private final ExperimentRepository experimentRepository;
|
private final ExperimentRepository experimentRepository;
|
||||||
private final Map<String, Strategy> strategyMap;
|
private final Map<String, Strategy> strategyMap;
|
||||||
private final LitmusContextProvider contextProvider;
|
private final LitmusContextProvider contextProvider;
|
||||||
@@ -149,6 +154,12 @@ public class DefaultLitmus implements Litmus {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Counter.builder("litmus_client_experiment_check_enabled")
|
||||||
|
.tag("experiment_name", experimentName)
|
||||||
|
.tag("result", String.valueOf(enabled))
|
||||||
|
.register(this.config.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
|
|
||||||
this.eventDispatcher.publish(context, litmusExperiment, enabled, null);
|
this.eventDispatcher.publish(context, litmusExperiment, enabled, null);
|
||||||
log.info("experiment_name: {}, result: {}", experimentName, enabled);
|
log.info("experiment_name: {}, result: {}", experimentName, enabled);
|
||||||
return enabled;
|
return enabled;
|
||||||
@@ -204,6 +215,14 @@ public class DefaultLitmus implements Litmus {
|
|||||||
|
|
||||||
this.eventDispatcher.publish(context, litmusExperiment, enabled, variant);
|
this.eventDispatcher.publish(context, litmusExperiment, enabled, variant);
|
||||||
log.info("experiment_name: {}, result: {}", experimentName, enabled);
|
log.info("experiment_name: {}, result: {}", experimentName, enabled);
|
||||||
|
|
||||||
|
Counter.builder("litmus_client_experiment_variant")
|
||||||
|
.tag("experiment_name", experimentName)
|
||||||
|
.tag("result", String.valueOf(enabled))
|
||||||
|
.tag("variant", variant.getName())
|
||||||
|
.register(this.config.getMeterRegistry())
|
||||||
|
.increment();
|
||||||
|
|
||||||
return variant;
|
return variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,4 +236,7 @@ public class DefaultLitmus implements Litmus {
|
|||||||
return getVariant(experimentName, contextProvider.getContext(), defaultValue);
|
return getVariant(experimentName, contextProvider.getContext(), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,18 +51,18 @@ public class LitmusExperimentRepository implements ExperimentRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (litmusConfig.isSynchronousFetchOnInitialisation()) {
|
if (litmusConfig.isSynchronousFetchOnInitialisation()) {
|
||||||
updateExperiments().run();
|
updateExperiments(litmusConfig.getVertical(), litmusConfig.getFetchLitmusExperimentsInterval()).run();
|
||||||
}
|
}
|
||||||
this.inMemoryCache = new InMemoryCache(this.experimentCollection);
|
this.inMemoryCache = new InMemoryCache(this.experimentCollection);
|
||||||
|
|
||||||
executor.setInterval(updateExperiments(), 0, litmusConfig.getFetchLitmusExperimentsInterval());
|
executor.setInterval(updateExperiments(litmusConfig.getVertical(), litmusConfig.getFetchLitmusExperimentsInterval()), 0, litmusConfig.getFetchLitmusExperimentsInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Runnable updateExperiments() {
|
private Runnable updateExperiments(String vertical, Long pollingTime) {
|
||||||
return () -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
LitmusExperimentResponse response = experimentFetcher.fetchExperiments();
|
LitmusExperimentResponse response = experimentFetcher.fetchExperiments(vertical, pollingTime);
|
||||||
if (response.getStatus() == LitmusExperimentResponse.Status.CHANGED) {
|
if (response != null && LitmusExperimentResponse.Status.CHANGED == response.getStatus()) {
|
||||||
experimentCollection = response.getExperimentCollection();
|
experimentCollection = response.getExperimentCollection();
|
||||||
experimentBackupHandler.write(response.getExperimentCollection());
|
experimentBackupHandler.write(response.getExperimentCollection());
|
||||||
this.inMemoryCache = new InMemoryCache(experimentCollection);
|
this.inMemoryCache = new InMemoryCache(experimentCollection);
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
package com.navi.medici.strategy;
|
package com.navi.medici.strategy;
|
||||||
|
|
||||||
|
|
||||||
|
import static com.navi.medici.strategy.FlexibleRolloutStrategy.GROUP_ID;
|
||||||
|
import static com.navi.medici.strategy.FlexibleRolloutStrategy.PERCENTAGE;
|
||||||
|
|
||||||
import com.navi.medici.client.HttpExperimentFetcher;
|
import com.navi.medici.client.HttpExperimentFetcher;
|
||||||
import com.navi.medici.context.LitmusContext;
|
import com.navi.medici.context.LitmusContext;
|
||||||
|
import com.navi.medici.util.StrategyUtils;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class DeviceWithIdStrategy implements Strategy {
|
public class DeviceWithIdStrategy implements Strategy {
|
||||||
|
|
||||||
@@ -28,10 +34,30 @@ public class DeviceWithIdStrategy implements Strategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled(Map<String, String> parameters, LitmusContext litmusContext) {
|
public boolean isEnabled(Map<String, String> parameters, LitmusContext litmusContext) {
|
||||||
|
if (StringUtils.isBlank(parameters.get(PERCENTAGE))) {
|
||||||
|
return deviceWithIdSegmentCheck(parameters, litmusContext);
|
||||||
|
} else {
|
||||||
|
return segmentCheckWithRollout(parameters, litmusContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deviceWithIdSegmentCheck(Map<String, String> parameters, LitmusContext litmusContext) {
|
||||||
|
var deviceId = litmusContext.getDeviceId().orElse(null);
|
||||||
var segmentName = parameters.get(SEGMENT);
|
var segmentName = parameters.get(SEGMENT);
|
||||||
var result = experimentFetcher.segmentIdExists(segmentName, litmusContext.getDeviceId().orElse(null));
|
var result = experimentFetcher.segmentIdExists(segmentName, deviceId);
|
||||||
|
|
||||||
return result.getData() != null && (Boolean) result.getData();
|
return result.getData() != null && (Boolean) result.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean segmentCheckWithRollout(Map<String, String> parameters, LitmusContext litmusContext) {
|
||||||
|
var deviceId = litmusContext.getDeviceId().orElse(null);
|
||||||
|
var percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE));
|
||||||
|
var groupId = parameters.getOrDefault(GROUP_ID, "");
|
||||||
|
|
||||||
|
var norm = StrategyUtils.getNormalizedNumber(deviceId, groupId);
|
||||||
|
|
||||||
|
return Optional.of(deviceWithIdSegmentCheck(parameters, litmusContext))
|
||||||
|
.map(r -> r && percentage > 0 && norm <= percentage)
|
||||||
|
.orElse(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ public class FlexibleRolloutStrategy implements Strategy {
|
|||||||
protected static final String PERCENTAGE = "rollout";
|
protected static final String PERCENTAGE = "rollout";
|
||||||
protected static final String GROUP_ID = "groupId";
|
protected static final String GROUP_ID = "groupId";
|
||||||
|
|
||||||
private final Supplier<String> randomGenerator;
|
|
||||||
|
|
||||||
public FlexibleRolloutStrategy() {
|
|
||||||
this.randomGenerator = () -> Math.random() * 100 + "";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "flexibleRollout";
|
return "flexibleRollout";
|
||||||
@@ -32,32 +25,12 @@ public class FlexibleRolloutStrategy implements Strategy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<String> resolveStickiness(String stickiness, LitmusContext context) {
|
|
||||||
switch (stickiness) {
|
|
||||||
case "userId":
|
|
||||||
return context.getUserId();
|
|
||||||
case "sessionId":
|
|
||||||
return context.getSessionId();
|
|
||||||
case "deviceId":
|
|
||||||
return context.getDeviceId();
|
|
||||||
case "appVersionCode":
|
|
||||||
return context.getAppVersionCode();
|
|
||||||
case "osType":
|
|
||||||
return context.getOsType();
|
|
||||||
case "random":
|
|
||||||
return Optional.of(randomGenerator.get());
|
|
||||||
case "default":
|
|
||||||
return Optional.of(context.getUserId()
|
|
||||||
.orElse(context.getSessionId().orElse(this.randomGenerator.get())));
|
|
||||||
default:
|
|
||||||
return context.getByName(stickiness);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled(Map<String, String> parameters, LitmusContext litmusContext) {
|
public boolean isEnabled(Map<String, String> parameters, LitmusContext litmusContext) {
|
||||||
final String stickiness = getStickiness(parameters);
|
final String stickiness = getStickiness(parameters);
|
||||||
final Optional<String> stickinessId = resolveStickiness(stickiness, litmusContext);
|
Optional<String> stickinessId = StrategyUtils.resolveStickiness(stickiness, litmusContext);
|
||||||
final int percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE));
|
final int percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE));
|
||||||
final String groupId = parameters.getOrDefault(GROUP_ID, "");
|
final String groupId = parameters.getOrDefault(GROUP_ID, "");
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
package com.navi.medici.strategy;
|
package com.navi.medici.strategy;
|
||||||
|
|
||||||
|
|
||||||
|
import static com.navi.medici.strategy.FlexibleRolloutStrategy.GROUP_ID;
|
||||||
|
import static com.navi.medici.strategy.FlexibleRolloutStrategy.PERCENTAGE;
|
||||||
|
|
||||||
import com.navi.medici.client.HttpExperimentFetcher;
|
import com.navi.medici.client.HttpExperimentFetcher;
|
||||||
import com.navi.medici.context.LitmusContext;
|
import com.navi.medici.context.LitmusContext;
|
||||||
|
import com.navi.medici.util.StrategyUtils;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class UserWithIdStrategy implements Strategy {
|
public class UserWithIdStrategy implements Strategy {
|
||||||
|
|
||||||
@@ -28,10 +34,31 @@ public class UserWithIdStrategy implements Strategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled(Map<String, String> parameters, LitmusContext litmusContext) {
|
public boolean isEnabled(Map<String, String> parameters, LitmusContext litmusContext) {
|
||||||
|
if (StringUtils.isBlank(parameters.get(PERCENTAGE))) {
|
||||||
|
return userWithIdSegmentCheck(parameters, litmusContext);
|
||||||
|
} else {
|
||||||
|
return segmentCheckWithRollout(parameters, litmusContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean userWithIdSegmentCheck(Map<String, String> parameters, LitmusContext litmusContext) {
|
||||||
|
var userId = litmusContext.getUserId().orElse(null);
|
||||||
var segmentName = parameters.get(SEGMENT);
|
var segmentName = parameters.get(SEGMENT);
|
||||||
var result = experimentFetcher.segmentIdExists(segmentName, litmusContext.getUserId().orElse(null));
|
var result = experimentFetcher.segmentIdExists(segmentName, userId);
|
||||||
|
|
||||||
return result.getData() != null && (Boolean) result.getData();
|
return result.getData() != null && (Boolean) result.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean segmentCheckWithRollout(Map<String, String> parameters, LitmusContext litmusContext) {
|
||||||
|
var userId = litmusContext.getUserId().orElse(null);
|
||||||
|
var percentage = StrategyUtils.getPercentage(parameters.get(PERCENTAGE));
|
||||||
|
var groupId = parameters.getOrDefault(GROUP_ID, "");
|
||||||
|
|
||||||
|
var norm = StrategyUtils.getNormalizedNumber(userId, groupId);
|
||||||
|
|
||||||
|
return Optional.of(userWithIdSegmentCheck(parameters, litmusContext))
|
||||||
|
.map(r -> r && percentage > 0 && norm <= percentage)
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.navi.medici.util;
|
package com.navi.medici.util;
|
||||||
|
|
||||||
import com.navi.medici.annotation.Nullable;
|
import com.navi.medici.annotation.Nullable;
|
||||||
|
import com.navi.medici.context.LitmusContext;
|
||||||
import com.sangupta.murmur.Murmur3;
|
import com.sangupta.murmur.Murmur3;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class StrategyUtils {
|
public class StrategyUtils {
|
||||||
|
|
||||||
@@ -47,4 +50,27 @@ public class StrategyUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Optional<String> resolveStickiness(String stickiness, LitmusContext context) {
|
||||||
|
Supplier<String> randomGenerator = () -> Math.random() * 100 + "";
|
||||||
|
switch (stickiness) {
|
||||||
|
case "userId":
|
||||||
|
return context.getUserId();
|
||||||
|
case "sessionId":
|
||||||
|
return context.getSessionId();
|
||||||
|
case "deviceId":
|
||||||
|
return context.getDeviceId();
|
||||||
|
case "appVersionCode":
|
||||||
|
return context.getAppVersionCode();
|
||||||
|
case "osType":
|
||||||
|
return context.getOsType();
|
||||||
|
case "random":
|
||||||
|
return Optional.of(randomGenerator.get());
|
||||||
|
case "default":
|
||||||
|
return Optional.of(context.getUserId()
|
||||||
|
.orElse(context.getSessionId().orElse(randomGenerator.get())));
|
||||||
|
default:
|
||||||
|
return context.getByName(stickiness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,16 @@ import io.micrometer.core.annotation.Timed;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -37,6 +40,19 @@ public class ExperimentController {
|
|||||||
return experimentService.fetchAllExperiments();
|
return experimentService.fetchAllExperiments();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/vertical")
|
||||||
|
@Timed(value = "litmus.fetch.vertical.experiment", percentiles = {0.95, 0.99})
|
||||||
|
public ResponseEntity<LitmusExperimentCollection> fetchExperimentsForAVertical(@RequestParam("vertical") String vertical,
|
||||||
|
@RequestParam("polling_time") Long pollingTime) {
|
||||||
|
|
||||||
|
var collection = experimentService.fetchAllExperimentsForVerticals(vertical, pollingTime);
|
||||||
|
if (collection.getLitmusExperiments().isEmpty()) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_MODIFIED).body(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok(collection);
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping(value = "/attach/variants/{experiment_id}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
@PutMapping(value = "/attach/variants/{experiment_id}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
@Timed(value = "litmus.attach.variants", percentiles = {0.95, 0.99})
|
@Timed(value = "litmus.attach.variants", percentiles = {0.95, 0.99})
|
||||||
public void attachVariants(@PathVariable("experiment_id") String experimentId,
|
public void attachVariants(@PathVariable("experiment_id") String experimentId,
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import com.navi.medici.variants.VariantDefinition;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ExperimentService {
|
public interface ExperimentService {
|
||||||
|
|
||||||
void create(LitmusExperiment litmusExperimentRequest);
|
void create(LitmusExperiment litmusExperimentRequest);
|
||||||
|
|
||||||
LitmusExperimentCollection fetchAllExperiments();
|
LitmusExperimentCollection fetchAllExperiments();
|
||||||
|
|
||||||
void attachVariants(String experimentId, List<VariantDefinition> variantDefinitions);
|
void attachVariants(String experimentId, List<VariantDefinition> variantDefinitions);
|
||||||
|
|
||||||
|
LitmusExperimentCollection fetchAllExperimentsForVerticals(String vertical, Long pollingTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.navi.medici.service.experiment;
|
package com.navi.medici.service.experiment;
|
||||||
|
|
||||||
|
import static com.navi.medici.enums.ExperimentType.KILL_SWITCH;
|
||||||
|
import static com.navi.medici.enums.ExperimentType.RELEASE;
|
||||||
|
|
||||||
import com.navi.medici.entity.ExperimentEntity;
|
import com.navi.medici.entity.ExperimentEntity;
|
||||||
import com.navi.medici.exceptions.BadRequestException;
|
import com.navi.medici.exceptions.BadRequestException;
|
||||||
import com.navi.medici.query.experiment.IExperimentQuery;
|
import com.navi.medici.query.experiment.IExperimentQuery;
|
||||||
@@ -41,9 +44,16 @@ public record ExperimentServiceImpl(IExperimentQuery experimentQuery,
|
|||||||
@Override
|
@Override
|
||||||
public LitmusExperimentCollection fetchAllExperiments() {
|
public LitmusExperimentCollection fetchAllExperiments() {
|
||||||
List<ExperimentEntity> experimentEntities = experimentQuery.findByEnabled(true);
|
List<ExperimentEntity> experimentEntities = experimentQuery.findByEnabled(true);
|
||||||
|
|
||||||
List<LitmusExperiment> litmusExperiments = experimentEntities.stream()
|
List<LitmusExperiment> litmusExperiments = experimentEntities.stream()
|
||||||
.map(this::build)
|
.map(this::build)
|
||||||
.collect(Collectors.toList());
|
.peek(le -> {
|
||||||
|
//This is to reduce sending strategy string over network
|
||||||
|
if (RELEASE.equals(le.getType()) || KILL_SWITCH.equals(le.getType())) {
|
||||||
|
le.setStrategies(null);
|
||||||
|
}
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
return LitmusExperimentCollection.builder()
|
return LitmusExperimentCollection.builder()
|
||||||
.litmusExperiments(litmusExperiments)
|
.litmusExperiments(litmusExperiments)
|
||||||
.build();
|
.build();
|
||||||
@@ -63,6 +73,19 @@ public record ExperimentServiceImpl(IExperimentQuery experimentQuery,
|
|||||||
experimentQuery.save(experiment);
|
experimentQuery.save(experiment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LitmusExperimentCollection fetchAllExperimentsForVerticals(String vertical, Long pollingTime) {
|
||||||
|
var experimentEntities = experimentQuery.findByVertical(vertical, pollingTime);
|
||||||
|
|
||||||
|
List<LitmusExperiment> litmusExperiments = experimentEntities.stream()
|
||||||
|
.map(this::build)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return LitmusExperimentCollection.builder()
|
||||||
|
.litmusExperiments(litmusExperiments)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private LitmusExperiment build(ExperimentEntity experimentEntity) {
|
private LitmusExperiment build(ExperimentEntity experimentEntity) {
|
||||||
return LitmusExperiment.builder()
|
return LitmusExperiment.builder()
|
||||||
.experimentId(experimentEntity.getExperimentId())
|
.experimentId(experimentEntity.getExperimentId())
|
||||||
@@ -75,6 +98,7 @@ public record ExperimentServiceImpl(IExperimentQuery experimentQuery,
|
|||||||
.type(experimentEntity.getType())
|
.type(experimentEntity.getType())
|
||||||
.startTime(experimentEntity.getStartTime())
|
.startTime(experimentEntity.getStartTime())
|
||||||
.endTime(experimentEntity.getEndTime())
|
.endTime(experimentEntity.getEndTime())
|
||||||
|
.vertical(experimentEntity.getVertical())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spring.jpa.hibernate.ddl-auto=none
|
|||||||
#spring.jpa.properties.hibernate.use_sql_comments=true
|
#spring.jpa.properties.hibernate.use_sql_comments=true
|
||||||
#spring.jpa.properties.hibernate.format_sql=true
|
#spring.jpa.properties.hibernate.format_sql=true
|
||||||
|
|
||||||
management.server.port=4001
|
management.server.port=4000
|
||||||
management.endpoints.web.exposure.include=prometheus,health,info,metric,heapdump,threaddump
|
management.endpoints.web.exposure.include=prometheus,health,info,metric,heapdump,threaddump
|
||||||
server.tomcat.mbeanregistry.enabled=true
|
server.tomcat.mbeanregistry.enabled=true
|
||||||
spring.jmx.enabled=true
|
spring.jmx.enabled=true
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ public class ExperimentEntity {
|
|||||||
@Column(name = "end_time")
|
@Column(name = "end_time")
|
||||||
LocalDateTime endTime;
|
LocalDateTime endTime;
|
||||||
|
|
||||||
|
@Column(name = "vertical")
|
||||||
|
String vertical;
|
||||||
|
|
||||||
@Version
|
@Version
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.navi.medici.query.experiment;
|
|||||||
|
|
||||||
import com.navi.medici.entity.ExperimentEntity;
|
import com.navi.medici.entity.ExperimentEntity;
|
||||||
import com.navi.medici.repository.ExperimentRepository;
|
import com.navi.medici.repository.ExperimentRepository;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -10,6 +11,7 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ExperimentQueryImpl implements IExperimentQuery {
|
public class ExperimentQueryImpl implements IExperimentQuery {
|
||||||
|
|
||||||
private final ExperimentRepository experimentRepository;
|
private final ExperimentRepository experimentRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -27,6 +29,13 @@ public class ExperimentQueryImpl implements IExperimentQuery {
|
|||||||
return experimentRepository.findByExperimentId(experimentId);
|
return experimentRepository.findByExperimentId(experimentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ExperimentEntity> findByVertical(String vertical, Long pollingTime) {
|
||||||
|
var lastPollingTime = LocalDateTime.now().minusSeconds(pollingTime);
|
||||||
|
|
||||||
|
return experimentRepository.findByVerticalAndUpdatedTime(vertical, lastPollingTime, LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(ExperimentEntity experiment) {
|
public void save(ExperimentEntity experiment) {
|
||||||
experimentRepository.save(experiment);
|
experimentRepository.save(experiment);
|
||||||
|
|||||||
@@ -11,5 +11,7 @@ public interface IExperimentQuery {
|
|||||||
|
|
||||||
Optional<ExperimentEntity> findByExperimentId(String experimentId);
|
Optional<ExperimentEntity> findByExperimentId(String experimentId);
|
||||||
|
|
||||||
|
List<ExperimentEntity> findByVertical(String vertical, Long pollingTime);
|
||||||
|
|
||||||
void save(ExperimentEntity experiment);
|
void save(ExperimentEntity experiment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
package com.navi.medici.repository;
|
package com.navi.medici.repository;
|
||||||
|
|
||||||
import com.navi.medici.entity.ExperimentEntity;
|
import com.navi.medici.entity.ExperimentEntity;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface ExperimentRepository extends CrudRepository<ExperimentEntity, Long> {
|
public interface ExperimentRepository extends CrudRepository<ExperimentEntity, Long> {
|
||||||
|
|
||||||
List<ExperimentEntity> findByEnabled(Boolean enabled);
|
List<ExperimentEntity> findByEnabled(Boolean enabled);
|
||||||
|
|
||||||
Optional<ExperimentEntity> findByName(String name);
|
Optional<ExperimentEntity> findByName(String name);
|
||||||
|
|
||||||
Optional<ExperimentEntity> findByExperimentId(String experimentId);
|
Optional<ExperimentEntity> findByExperimentId(String experimentId);
|
||||||
|
|
||||||
|
@Query(value = "select * from experiments e where e.vertical = :vertical and e.updated_at >= :lastPollingTime and e.updated_at <= :currentTime ",
|
||||||
|
nativeQuery = true)
|
||||||
|
List<ExperimentEntity> findByVerticalAndUpdatedTime(@Param("vertical") String vertical,
|
||||||
|
@Param("lastPollingTime") LocalDateTime lastPollingTime,
|
||||||
|
@Param("currentTime") LocalDateTime currentTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
--liquibase formatted sql
|
||||||
|
|
||||||
|
--changeset author:chandresh id:202204011642
|
||||||
|
|
||||||
|
ALTER TABLE experiments
|
||||||
|
ADD COLUMN vertical VARCHAR;
|
||||||
@@ -27,7 +27,18 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -36,6 +47,18 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||||
|
<version>1.8.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk16</artifactId>
|
||||||
|
<version>140</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.navi.medici.container;
|
|||||||
import com.navi.medici.config.LitmusConfig;
|
import com.navi.medici.config.LitmusConfig;
|
||||||
import com.navi.medici.litmus.DefaultLitmus;
|
import com.navi.medici.litmus.DefaultLitmus;
|
||||||
import com.navi.medici.litmus.Litmus;
|
import com.navi.medici.litmus.Litmus;
|
||||||
|
import io.micrometer.core.instrument.MeterRegistry;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -10,13 +11,15 @@ import org.springframework.stereotype.Component;
|
|||||||
public class MockContainer {
|
public class MockContainer {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Litmus litmus() {
|
public Litmus litmus(MeterRegistry meterRegistry) {
|
||||||
var litmusConfig = LitmusConfig.builder()
|
var litmusConfig = LitmusConfig.builder()
|
||||||
.litmusAPI("http://localhost:12000/litmus-core/v1")
|
.litmusAPI("http://localhost:12000/litmus-core/v1")
|
||||||
.appName("litmus-mock")
|
.appName("litmus-mock")
|
||||||
.instanceId("test-instance")
|
.instanceId("test-instance")
|
||||||
.litmusContextProvider(new CustomLitmusContextProvider())
|
.litmusContextProvider(new CustomLitmusContextProvider())
|
||||||
.clickStreamAPI("https://dev-janus.np.navi-tech.in/events/json")
|
.clickStreamAPI("https://dev-janus.np.navi-tech.in/events/json")
|
||||||
|
.vertical("PL")
|
||||||
|
.meterRegistry(meterRegistry)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Litmus litmus = new DefaultLitmus(litmusConfig);
|
Litmus litmus = new DefaultLitmus(litmusConfig);
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
server.port=11000
|
server.port=11000
|
||||||
|
|
||||||
|
management.server.port=4001
|
||||||
|
management.endpoints.web.exposure.include=prometheus,health,info,metric,heapdump,threaddump
|
||||||
@@ -31,6 +31,8 @@
|
|||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public record LitmusProxyContainer(LitmusProxyConfig litmusProxyConfig, LitmusCo
|
|||||||
.instanceId("test-instance")
|
.instanceId("test-instance")
|
||||||
.litmusContextProvider(litmusContextProvider)
|
.litmusContextProvider(litmusContextProvider)
|
||||||
.clickStreamAPI(litmusProxyConfig.getClickStreamEndpoint())
|
.clickStreamAPI(litmusProxyConfig.getClickStreamEndpoint())
|
||||||
|
.vertical("PL")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Litmus litmus = new DefaultLitmus(litmusConfig);
|
Litmus litmus = new DefaultLitmus(litmusConfig);
|
||||||
|
|||||||
Reference in New Issue
Block a user