Skip to content
Draft
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 lib/sdk/server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ ext.versions = [
"guava": "32.0.1-jre",
"jackson": "2.11.2",
"launchdarklyJavaSdkCommon": "2.6.0",
"launchdarklyJavaSdkInternal": "1.11.1",
"launchdarklyJavaSdkInternal": "1.13.0",
"launchdarklyLogging": "1.1.0",
"okhttp": "4.12.0", // specify this for the SDK build instead of relying on the transitive dependency from okhttp-eventsource
"okhttpEventsource": "5.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ public void recordEvaluationEvent(LDContext context, String flagKey, int flagVer
LDValue value, EvaluationReason reason, LDValue defaultValue, String prerequisiteOfFlagKey,
boolean requireFullEvent, Long debugEventsUntilDate, boolean excludeFromSummaries,
Long samplingRatio) {
recordEvaluationEvent(context, flagKey, flagVersion, variation, value, reason, defaultValue, prerequisiteOfFlagKey,
requireFullEvent, debugEventsUntilDate, excludeFromSummaries, samplingRatio, false);
}

@Override
public void recordEvaluationEvent(LDContext context, String flagKey, int flagVersion, int variation,
LDValue value, EvaluationReason reason, LDValue defaultValue, String prerequisiteOfFlagKey,
boolean requireFullEvent, Long debugEventsUntilDate, boolean excludeFromSummaries,
Long samplingRatio, boolean overrideAffected) {
eventProcessor.sendEvent(new Event.FeatureRequest(
System.currentTimeMillis(),
flagKey,
Expand All @@ -48,7 +57,8 @@ public void recordEvaluationEvent(LDContext context, String flagKey, int flagVer
debugEventsUntilDate,
false,
samplingRatio != null ? samplingRatio : 1,
excludeFromSummaries
excludeFromSummaries,
overrideAffected
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ default void recordEvaluation(DataModel.FeatureFlag flag, LDContext context, Eva
default void recordPrerequisiteEvaluation(DataModel.FeatureFlag flag, DataModel.FeatureFlag prereqOfFlag, LDContext context, EvalResult result) {
// default is no op
}
default void recordEvaluationError(DataModel.FeatureFlag flag, LDContext context, LDValue defaultValue, EvaluationReason.ErrorKind errorKind) {
default void recordEvaluationError(DataModel.FeatureFlag flag, LDContext context, LDValue defaultValue,
EvaluationReason.ErrorKind errorKind, boolean overrideAffected) {
// default is no op
}
default void recordEvaluationUnknownFlagError(String flagKey, LDContext context, LDValue defaultValue, EvaluationReason.ErrorKind errorKind) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ EvalResultAndFlag evaluate(String flagKey, LDContext context, LDValue defaultVal
!value.isNull() &&
value.getType() != requireType) {
logger.error("Feature flag \"{}\"; evaluation expected result as {}, but got {}", flagKey, defaultValue.getType(), value.getType());
recorder.recordEvaluationError(featureFlag, context, defaultValue, ErrorKind.WRONG_TYPE);
recorder.recordEvaluationError(featureFlag, context, defaultValue, ErrorKind.WRONG_TYPE,
result.isOverrideAffected());
// The type mismatch replaces the reason. The evaluation read the same definitions, so the
// new reason keeps the override-affected marking.
return new EvalResultAndFlag(
Expand All @@ -163,7 +164,8 @@ EvalResultAndFlag evaluate(String flagKey, LDContext context, LDValue defaultVal
if (featureFlag == null) {
recorder.recordEvaluationUnknownFlagError(flagKey, context, defaultValue, ErrorKind.EXCEPTION);
} else {
recorder.recordEvaluationError(featureFlag, context, defaultValue, ErrorKind.EXCEPTION);
// The evaluation did not complete, so nothing is known about which definitions it read.
recorder.recordEvaluationError(featureFlag, context, defaultValue, ErrorKind.EXCEPTION, false);
}
return new EvalResultAndFlag(EvalResult.of(defaultValue, NO_VARIATION, EvaluationReason.exception(e)), null);
}
Expand Down Expand Up @@ -255,6 +257,8 @@ private static DataModel.Segment getSegment(ReadOnlyStore store, String key) {
*/
private static EvaluationRecorder makeEvaluationRecorder(EventProcessor processor, boolean withReasons) {
return new EvaluationRecorder() {
// The marking passed to the event processor is the evaluation result's own record of the
// definitions it read. Event generation does not read the evaluation reason.
@Override
public void recordEvaluation(FeatureFlag flag, LDContext context, EvalResult result, LDValue defaultValue) {
processor.recordEvaluationEvent(
Expand All @@ -269,10 +273,13 @@ public void recordEvaluation(FeatureFlag flag, LDContext context, EvalResult res
flag.isTrackEvents() || result.isForceReasonTracking(),
flag.getDebugEventsUntilDate(),
flag.isExcludeFromSummaries(),
flag.getSamplingRatio()
flag.getSamplingRatio(),
result.isOverrideAffected()
);
}

// A prerequisite record carries the prerequisite's own marking, which its own subtree of
// reads set. It is not the marking of the evaluation that requested it.
@Override
public void recordPrerequisiteEvaluation(FeatureFlag flag, FeatureFlag prereqOfFlag, LDContext context, EvalResult result) {
processor.recordEvaluationEvent(
Expand All @@ -287,25 +294,28 @@ public void recordPrerequisiteEvaluation(FeatureFlag flag, FeatureFlag prereqOfF
flag.isTrackEvents() || result.isForceReasonTracking(),
flag.getDebugEventsUntilDate(),
flag.isExcludeFromSummaries(),
flag.getSamplingRatio()
flag.getSamplingRatio(),
result.isOverrideAffected()
);
}

@Override
public void recordEvaluationError(FeatureFlag flag, LDContext context, LDValue defaultValue, ErrorKind errorKind) {
public void recordEvaluationError(FeatureFlag flag, LDContext context, LDValue defaultValue, ErrorKind errorKind,
boolean overrideAffected) {
processor.recordEvaluationEvent(
context,
flag.getKey(),
flag.getVersion(),
NO_VARIATION,
defaultValue,
withReasons ? EvaluationReason.error(errorKind) : null,
withReasons ? EvaluationReason.error(errorKind).withOverrideAffected(overrideAffected) : null,
defaultValue,
null,
flag.isTrackEvents(),
flag.getDebugEventsUntilDate(),
flag.isExcludeFromSummaries(),
flag.getSamplingRatio()
flag.getSamplingRatio(),
overrideAffected
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ public void recordEvaluationEvent(LDContext context, String flagKey, int flagVer
// no-op
}

@Override
public void recordEvaluationEvent(LDContext context, String flagKey, int flagVersion, int variation, LDValue value,
EvaluationReason reason, LDValue defaultValue, String prerequisiteOfFlagKey,
boolean requireFullEvent, Long debugEventsUntilDate, boolean excludeFromSummaries,
Long samplingRatio, boolean overrideAffected) {
// no-op
}

@Override
public void recordIdentifyEvent(LDContext context) {
// no-op
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,54 @@ void recordEvaluationEvent(
boolean excludeFromSummaries,
Long samplingRatio
);

/**
* Records the action of evaluating a feature flag, with the override-affected marking.
* <p>
* An override-affected evaluation is one that read at least one definition from the SDK's
* override store: the flag itself, a prerequisite at any depth, or a segment consulted during
* matching. Such an evaluation produces no individual feature event and no debug event, whatever
* the flag's configuration requests, and it is counted in a separate summary counter that
* carries the marker. The SDK keys this behavior on the marking alone, not on the reason.
* <p>
* The default implementation ignores the marking and calls the method without it, so an
* existing custom implementation keeps working. Flag overrides are currently experimental and
* subject to change.
*
* @param context the evaluation context
* @param flagKey key of the feature flag that was evaluated
* @param flagVersion the version of the flag, or {@link #NO_VERSION} if the flag was not found
* @param variation the result variation index, or {@link EvaluationDetail#NO_VARIATION} if evaluation failed
* @param value the result value
* @param reason the evaluation reason, or null if the reason was not requested
* @param defaultValue the default value parameter for the evaluation
* @param prerequisiteOfFlagKey the key of the flag that this flag was evaluated as a prerequisite of,
* or null if this flag was evaluated for itself
* @param requireFullEvent true if full-fidelity analytics events should be sent for this flag
* @param debugEventsUntilDate if non-null, debug events are to be generated until this millisecond time
* @param excludeFromSummaries true if the event evaluation should not be included in summaries
* @param samplingRatio ratio used to control event sampling
* @param overrideAffected true if an override affected the evaluation
* @since 7.18.0
*/
default void recordEvaluationEvent(
LDContext context,
String flagKey,
int flagVersion,
int variation,
LDValue value,
EvaluationReason reason,
LDValue defaultValue,
String prerequisiteOfFlagKey,
boolean requireFullEvent,
Long debugEventsUntilDate,
boolean excludeFromSummaries,
Long samplingRatio,
boolean overrideAffected
) {
recordEvaluationEvent(context, flagKey, flagVersion, variation, value, reason, defaultValue,
prerequisiteOfFlagKey, requireFullEvent, debugEventsUntilDate, excludeFromSummaries, samplingRatio);
}

/**
* Registers an evaluation context, as when the SDK's {@code identify} method is called.
Expand Down
Loading
Loading