All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.launchdarkly.sdk.server.EvaluatorPreprocessing Maven / Gradle / Ivy

package com.launchdarkly.sdk.server;

import com.google.common.collect.ImmutableSet;
import com.launchdarkly.sdk.EvaluationReason;
import com.launchdarkly.sdk.LDValue;
import com.launchdarkly.sdk.server.DataModel.Clause;
import com.launchdarkly.sdk.server.DataModel.FeatureFlag;
import com.launchdarkly.sdk.server.DataModel.Operator;
import com.launchdarkly.sdk.server.DataModel.Prerequisite;
import com.launchdarkly.sdk.server.DataModel.Rule;
import com.launchdarkly.sdk.server.DataModel.Segment;
import com.launchdarkly.sdk.server.DataModel.SegmentRule;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;

/**
 * These methods precompute data that may help to reduce the overhead of feature flag evaluations. They
 * are called from the afterDeserialized() methods of FeatureFlag and Segment, after those objects have
 * been deserialized from JSON but before they have been made available to any other code (so these
 * methods do not need to be thread-safe).
 * 

* If for some reason these methods have not been called before an evaluation happens, the evaluation * logic must still be able to work without the precomputed data. */ abstract class EvaluatorPreprocessing { private EvaluatorPreprocessing() {} static final class ClauseExtra { final Set valuesSet; final List valuesExtra; ClauseExtra(Set valuesSet, List valuesExtra) { this.valuesSet = valuesSet; this.valuesExtra = valuesExtra; } static final class ValueExtra { final ZonedDateTime parsedDate; final Pattern parsedRegex; final SemanticVersion parsedSemVer; ValueExtra(ZonedDateTime parsedDate, Pattern parsedRegex, SemanticVersion parsedSemVer) { this.parsedDate = parsedDate; this.parsedRegex = parsedRegex; this.parsedSemVer = parsedSemVer; } } } static void preprocessFlag(FeatureFlag f) { for (Prerequisite p: f.getPrerequisites()) { EvaluatorPreprocessing.preprocessPrerequisite(p); } List rules = f.getRules(); int n = rules.size(); for (int i = 0; i < n; i++) { preprocessFlagRule(rules.get(i), i); } } static void preprocessSegment(Segment s) { List rules = s.getRules(); int n = rules.size(); for (int i = 0; i < n; i++) { preprocessSegmentRule(rules.get(i), i); } } static void preprocessPrerequisite(Prerequisite p) { // Precompute an immutable EvaluationReason instance that will be used if the prerequisite fails. p.setPrerequisiteFailedReason(EvaluationReason.prerequisiteFailed(p.getKey())); } static void preprocessFlagRule(Rule r, int ruleIndex) { // Precompute an immutable EvaluationReason instance that will be used if a user matches this rule. r.setRuleMatchReason(EvaluationReason.ruleMatch(ruleIndex, r.getId())); for (Clause c: r.getClauses()) { preprocessClause(c); } } static void preprocessSegmentRule(SegmentRule r, int ruleIndex) { for (Clause c: r.getClauses()) { preprocessClause(c); } } static void preprocessClause(Clause c) { Operator op = c.getOp(); if (op == null) { return; } switch (op) { case in: // This is a special case where the clause is testing for an exact match against any of the // clause values. Converting the value list to a Set allows us to do a fast lookup instead of // a linear search. We do not do this for other operators (or if there are fewer than two // values) because the slight extra overhead of a Set is not worthwhile in those case. List values = c.getValues(); if (values.size() > 1) { c.setPreprocessed(new ClauseExtra(ImmutableSet.copyOf(values), null)); } break; case matches: c.setPreprocessed(preprocessClauseValues(c.getValues(), v -> new ClauseExtra.ValueExtra(null, EvaluatorTypeConversion.valueToRegex(v), null) )); break; case after: case before: c.setPreprocessed(preprocessClauseValues(c.getValues(), v -> new ClauseExtra.ValueExtra(EvaluatorTypeConversion.valueToDateTime(v), null, null) )); break; case semVerEqual: case semVerGreaterThan: case semVerLessThan: c.setPreprocessed(preprocessClauseValues(c.getValues(), v -> new ClauseExtra.ValueExtra(null, null, EvaluatorTypeConversion.valueToSemVer(v)) )); break; default: break; } } private static ClauseExtra preprocessClauseValues( List values, Function f ) { List valuesExtra = new ArrayList<>(values.size()); for (LDValue v: values) { valuesExtra.add(f.apply(v)); } return new ClauseExtra(null, valuesExtra); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy