From 50204ef6e33df194de79e8027349e52f307b07f1 Mon Sep 17 00:00:00 2001 From: deleteLater Date: Wed, 4 Nov 2026 18:51:10 +0800 Subject: [PATCH 1/2] Remove sendToExperiment from evaluation events --- .../java/co/featbit/server/DataModel.java | 40 ++----------------- .../java/co/featbit/server/Evaluator.java | 15 ++----- .../java/co/featbit/server/EvaluatorImp.java | 31 -------------- .../java/co/featbit/server/InsightTypes.java | 1 - .../co/featbit/server/EvaluationTest.java | 10 ++++- 5 files changed, 16 insertions(+), 81 deletions(-) diff --git a/src/main/java/co/featbit/server/DataModel.java b/src/main/java/co/featbit/server/DataModel.java index d73a715..d1e26de 100644 --- a/src/main/java/co/featbit/server/DataModel.java +++ b/src/main/java/co/featbit/server/DataModel.java @@ -262,7 +262,6 @@ static class FeatureFlag implements DataStorageTypes.Item, JsonHelper.AfterJsonP final Date updatedAt; private Long timestamp; private final boolean isArchived; - private final boolean exptIncludeAllTargets; // Missing values from older servers keep evaluation event collection enabled. private Boolean insightsEnabled; private final boolean isEnabled; @@ -276,11 +275,10 @@ static class FeatureFlag implements DataStorageTypes.Item, JsonHelper.AfterJsonP private final String disabledVariationId; private transient Map variationMap; - FeatureFlag(String id, Date updatedAt, boolean isArchived, boolean exptIncludeAllTargets, boolean isEnabled, String name, String key, String variationType, List variations, List targetUsers, List rules, Fallthrough fallthrough, String disabledVariationId) { + FeatureFlag(String id, Date updatedAt, boolean isArchived, boolean isEnabled, String name, String key, String variationType, List variations, List targetUsers, List rules, Fallthrough fallthrough, String disabledVariationId) { this.id = id; this.updatedAt = updatedAt; this.isArchived = isArchived; - this.exptIncludeAllTargets = exptIncludeAllTargets; this.isEnabled = isEnabled; this.name = name; this.key = key; @@ -316,10 +314,6 @@ public Integer getType() { return FB_FEATURE_FLAG; } - public boolean exptIncludeAllTargets() { - return exptIncludeAllTargets; - } - public boolean isInsightsEnabled() { return !Boolean.FALSE.equals(insightsEnabled); } @@ -444,25 +438,18 @@ public void afterDeserialization() { } static final class TargetRule { - private final boolean includedInExpt; - private final String dispatchKey; private final List conditions; private final List variations; - TargetRule(boolean includedInExpt, String dispatchKey, List conditions, List variations) { - this.includedInExpt = includedInExpt; + TargetRule(String dispatchKey, List conditions, List variations) { this.dispatchKey = dispatchKey; this.conditions = conditions; this.variations = variations; } - public boolean includedInExpt() { - return includedInExpt; - } - public List getConditions() { return conditions == null ? Collections.emptyList() : conditions; } @@ -539,12 +526,9 @@ static final class RolloutVariation { private final double[] rollout; - private final double exptRollout; - - RolloutVariation(String id, double[] rollout, double exptRollout) { + RolloutVariation(String id, double[] rollout) { this.id = id; this.rollout = rollout; - this.exptRollout = exptRollout; } public String getId() { @@ -554,34 +538,18 @@ public String getId() { public double[] getRollout() { return rollout; } - - public double getExptRollout() { - return exptRollout; - } - - public double splittingPercentage() { - if (rollout != null) return rollout[1] - rollout[0]; - return 0D; - } } static final class Fallthrough { - private final boolean includedInExpt; - private final String dispatchKey; private final List variations; - Fallthrough(boolean includedInExpt, String dispatchKey, List variations) { - this.includedInExpt = includedInExpt; + Fallthrough(String dispatchKey, List variations) { this.dispatchKey = dispatchKey; this.variations = variations; } - public boolean includedInExpt() { - return includedInExpt; - } - public List getVariations() { return variations == null ? Collections.emptyList() : variations; } diff --git a/src/main/java/co/featbit/server/Evaluator.java b/src/main/java/co/featbit/server/Evaluator.java index b1b9f38..1d985f1 100644 --- a/src/main/java/co/featbit/server/Evaluator.java +++ b/src/main/java/co/featbit/server/Evaluator.java @@ -13,7 +13,6 @@ abstract class Evaluator implements EvaluationReason { protected static final Logger logger = Loggers.EVALUATION; - protected static final String EXPT_KEY_PREFIX = "expt"; protected static final String NO_EVAL_RES = "NE"; protected static final String DEFAULT_JSON_VALUE = "DJV"; protected static final String FLAG_KEY_UNKNOWN = "flag key unknown"; @@ -69,40 +68,36 @@ static class EvalResult { private final String index; private final String value; private final String reason; - private final boolean sendToExperiment; private final String keyName; private final String name; - EvalResult(String flagType, String value, String index, String reason, boolean sendToExperiment, String keyName, String name) { + EvalResult(String flagType, String value, String index, String reason, String keyName, String name) { this.flagType = flagType; this.value = value; this.index = index; this.reason = reason; - this.sendToExperiment = sendToExperiment; this.keyName = keyName; this.name = name; } public static EvalResult error(String reason, String keyName, String name) { - return new EvalResult(null, null, NO_EVAL_RES, reason, false, keyName, name); + return new EvalResult(null, null, NO_EVAL_RES, reason, keyName, name); } public static EvalResult error(String defaultValue, String reason, String keyName, String name) { - return new EvalResult(null, defaultValue, NO_EVAL_RES, reason, false, keyName, name); + return new EvalResult(null, defaultValue, NO_EVAL_RES, reason, keyName, name); } public static EvalResult of(String flagType, DataModel.Variation option, String reason, - boolean sendToExperiment, String keyName, String name) { return new EvalResult(flagType, option.getValue(), option.getId(), reason, - sendToExperiment, keyName, name); } @@ -123,10 +118,6 @@ public String getReason() { return reason; } - public boolean isSendToExperiment() { - return sendToExperiment; - } - public String getKeyName() { return keyName; } diff --git a/src/main/java/co/featbit/server/EvaluatorImp.java b/src/main/java/co/featbit/server/EvaluatorImp.java index aa4afbe..87f47d9 100644 --- a/src/main/java/co/featbit/server/EvaluatorImp.java +++ b/src/main/java/co/featbit/server/EvaluatorImp.java @@ -63,7 +63,6 @@ private EvalResult matchFeatureFlagDisabledUserVariation(DataModel.FeatureFlag f return EvalResult.of(flag.getVariationType(), flag.getVariation(flag.getDisabledVariationId()), REASON_FLAG_OFF, - false, flag.getKey(), flag.getName()); } return null; @@ -76,7 +75,6 @@ private EvalResult matchTargetedUserVariation(DataModel.FeatureFlag featureFlag, .map(target -> EvalResult.of(featureFlag.getVariationType(), featureFlag.getVariation(target.getVariationId()), REASON_TARGET_MATCH, - featureFlag.exptIncludeAllTargets(), featureFlag.getKey(), featureFlag.getName())) .orElse(null); @@ -89,7 +87,6 @@ private EvalResult matchConditionedUserVariation(DataModel.FeatureFlag featureFl targetRule.getVariations(), user, REASON_RULE_MATCH, - targetRule.includedInExpt(), targetRule.getDispatchKey()); } @@ -244,7 +241,6 @@ private EvalResult matchFallThroughUserVariation(DataModel.FeatureFlag featureFl fallthrough.getVariations(), user, REASON_FALLTHROUGH, - fallthrough.includedInExpt(), fallthrough.getDispatchKey()); } @@ -252,7 +248,6 @@ private EvalResult getRollOutVariationOption(DataModel.FeatureFlag featureFlag, Collection rollouts, FBUser user, String reason, - Boolean ruleIncludedInExperiment, String dispatchKey) { dispatchKey = StringUtils.isEmpty(dispatchKey) ? "keyid" : dispatchKey; String userAttr = user.getProperty(dispatchKey); @@ -264,34 +259,8 @@ private EvalResult getRollOutVariationOption(DataModel.FeatureFlag featureFlag, .map(rollout -> EvalResult.of(featureFlag.getVariationType(), featureFlag.getVariation(rollout.getId()), reason, - isSendToExperiment(dispatchKeyValue, rollout, featureFlag.exptIncludeAllTargets(), ruleIncludedInExperiment), featureFlag.getKey(), featureFlag.getName())) .orElse(null); } - - private boolean isSendToExperiment(String dispatchKeyValue, - DataModel.RolloutVariation rollout, - Boolean exptIncludeAllRules, - Boolean ruleIncludedInExperiment) { - if (exptIncludeAllRules) { - return true; - } - if (ruleIncludedInExperiment) { - double sendToExperimentPercentage = rollout.getExptRollout(); - double splittingPercentage = rollout.splittingPercentage(); - if (sendToExperimentPercentage == 0D || splittingPercentage == 0D) { - return false; - } - double upperBound = sendToExperimentPercentage / splittingPercentage; - if (upperBound > 1D) { - upperBound = 1D; - } - String newDispatchKeyValue = String.join("", EXPT_KEY_PREFIX, dispatchKeyValue); - return VariationSplittingAlgorithm.ifKeyBelongsPercentage(newDispatchKeyValue, new double[]{0D, upperBound}); - } - return false; - } - - } diff --git a/src/main/java/co/featbit/server/InsightTypes.java b/src/main/java/co/featbit/server/InsightTypes.java index 726bcbd..e41d4c7 100644 --- a/src/main/java/co/featbit/server/InsightTypes.java +++ b/src/main/java/co/featbit/server/InsightTypes.java @@ -219,7 +219,6 @@ public JsonElement serialize(FlagEvent flagEvent, Type type, JsonSerializationCo for (FlagEventVariation variation : flagEvent.userVariations) { JsonObject var = new JsonObject(); var.addProperty("featureFlagKey", variation.getFeatureFlagKeyName()); - var.addProperty("sendToExperiment", variation.getVariation().isSendToExperiment()); var.addProperty("timestamp", variation.getTimestamp()); JsonObject v = new JsonObject(); v.addProperty("id", variation.getVariation().getIndex()); diff --git a/src/test/java/co/featbit/server/EvaluationTest.java b/src/test/java/co/featbit/server/EvaluationTest.java index fdf52ae..c00eaca 100644 --- a/src/test/java/co/featbit/server/EvaluationTest.java +++ b/src/test/java/co/featbit/server/EvaluationTest.java @@ -1,8 +1,10 @@ package co.featbit.server; +import co.featbit.commons.json.JsonHelper; import co.featbit.commons.model.FBUser; import co.featbit.server.exterior.DataStorage; import co.featbit.server.exterior.DataStorageTypes; +import com.google.gson.JsonObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -10,6 +12,7 @@ import static co.featbit.server.exterior.DataStorageTypes.FEATURES; import static co.featbit.server.exterior.DataStorageTypes.SEGMENTS; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; class EvaluationTest extends ComponentBaseTest { @@ -65,11 +68,16 @@ void testEvaluationWhenDisabledFlag() { } @Test - void testEvaluationWhenMatchTargetUser() { + void testEvaluationWhenMatchTargetUser() throws Exception { InsightTypes.Event event = InsightTypes.FlagEvent.of(user2); Evaluator.EvalResult res = evaluator.evaluate(flag, user2, event); assertEquals("teamB", res.getValue()); assertEquals(REASON_TARGET_MATCH, res.getReason()); + + JsonObject payload = JsonHelper.deserialize(JsonHelper.serialize(event), JsonObject.class); + JsonObject entry = payload.getAsJsonArray("variations").get(0).getAsJsonObject(); + assertFalse(entry.has("sendToExperiment")); + assertEquals("teamB", entry.getAsJsonObject("variation").get("value").getAsString()); } @Test From 116590fc5c00dde61567c2c5d63c88cc137ba92b Mon Sep 17 00:00:00 2001 From: deleteLater Date: Wed, 4 Nov 2026 18:54:51 +0800 Subject: [PATCH 2/2] Restore original evaluation test --- src/test/java/co/featbit/server/EvaluationTest.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/test/java/co/featbit/server/EvaluationTest.java b/src/test/java/co/featbit/server/EvaluationTest.java index c00eaca..fdf52ae 100644 --- a/src/test/java/co/featbit/server/EvaluationTest.java +++ b/src/test/java/co/featbit/server/EvaluationTest.java @@ -1,10 +1,8 @@ package co.featbit.server; -import co.featbit.commons.json.JsonHelper; import co.featbit.commons.model.FBUser; import co.featbit.server.exterior.DataStorage; import co.featbit.server.exterior.DataStorageTypes; -import com.google.gson.JsonObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -12,7 +10,6 @@ import static co.featbit.server.exterior.DataStorageTypes.FEATURES; import static co.featbit.server.exterior.DataStorageTypes.SEGMENTS; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; class EvaluationTest extends ComponentBaseTest { @@ -68,16 +65,11 @@ void testEvaluationWhenDisabledFlag() { } @Test - void testEvaluationWhenMatchTargetUser() throws Exception { + void testEvaluationWhenMatchTargetUser() { InsightTypes.Event event = InsightTypes.FlagEvent.of(user2); Evaluator.EvalResult res = evaluator.evaluate(flag, user2, event); assertEquals("teamB", res.getValue()); assertEquals(REASON_TARGET_MATCH, res.getReason()); - - JsonObject payload = JsonHelper.deserialize(JsonHelper.serialize(event), JsonObject.class); - JsonObject entry = payload.getAsJsonArray("variations").get(0).getAsJsonObject(); - assertFalse(entry.has("sendToExperiment")); - assertEquals("teamB", entry.getAsJsonObject("variation").get("value").getAsString()); } @Test