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
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ public void summarizeEvent(
int variation,
LDValue value,
LDValue defaultValue,
LDContext context
LDContext context,
boolean overrideAffected
) {
summarizer.summarizeEvent(timestamp, flagKey, flagVersion, variation, value, defaultValue, context);
summarizer.summarizeEvent(timestamp, flagKey, flagVersion, variation, value, defaultValue, context,
overrideAffected);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,10 @@ private void processEvent(Event e, EventBuffer outbox) {
if(!fe.isExcludeFromSummaries()) {
outbox.addToSummary(fe);
}
addFullEvent = fe.isTrackEvents();
if (shouldDebugEvent(fe)) {
// An override-affected evaluation appears only in the summary counters. It produces no
// individual feature event and no debug event, whatever the flag's configuration requests.
addFullEvent = fe.isTrackEvents() && !fe.isOverrideAffected();
if (!fe.isOverrideAffected() && shouldDebugEvent(fe)) {
debugEvent = fe.toDebugEvent();
}
} else {
Expand Down Expand Up @@ -702,7 +704,8 @@ void addToSummary(Event.FeatureRequest e) {
e.getVariation(),
e.getValue(),
e.getDefaultVal(),
e.getContext()
e.getContext(),
e.isOverrideAffected()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ public static final class FeatureRequest extends Event {
private final boolean debug;
private final long samplingRatio;
private final boolean excludeFromSummaries;
private final boolean overrideAffected;

/**
* Constructs a feature request event.
Expand All @@ -171,12 +172,14 @@ public static final class FeatureRequest extends Event {
* @param trackEvents true if full event tracking is turned on for this flag
* @param debugEventsUntilDate if non-null, the time until which event debugging should be enabled
* @param debug true if this is a debugging event
* @param excludeFromSummaries true to exclude the event from summaries
* @param samplingRatio the sampling ratio for the event
* @param excludeFromSummaries true to exclude the event from summaries
* @param overrideAffected true if an override affected the evaluation; see {@link #isOverrideAffected()}
*/
public FeatureRequest(long timestamp, String key, LDContext context, int version, int variation, LDValue value,
LDValue defaultVal, EvaluationReason reason, String prereqOf, boolean trackEvents,
Long debugEventsUntilDate, boolean debug, long samplingRatio, boolean excludeFromSummaries) {
Long debugEventsUntilDate, boolean debug, long samplingRatio, boolean excludeFromSummaries,
boolean overrideAffected) {
super(timestamp, context);
this.key = key;
this.version = version;
Expand All @@ -190,6 +193,32 @@ public FeatureRequest(long timestamp, String key, LDContext context, int version
this.debug = debug;
this.excludeFromSummaries = excludeFromSummaries;
this.samplingRatio = samplingRatio;
this.overrideAffected = overrideAffected;
}

/**
* Constructs a feature request event that no override affected.
*
* @param timestamp the timestamp in milliseconds
* @param key the flag key
* @param context the context associated with the event
* @param version the flag version, or -1 if the flag was not found
* @param variation the result variation, or -1 if there was an error
* @param value the result value
* @param defaultVal the default value passed by the application
* @param reason the evaluation reason, if it is to be included in the event
* @param prereqOf if this flag was evaluated as a prerequisite, this is the key of the flag that referenced it
* @param trackEvents true if full event tracking is turned on for this flag
* @param debugEventsUntilDate if non-null, the time until which event debugging should be enabled
* @param debug true if this is a debugging event
* @param samplingRatio the sampling ratio for the event
* @param excludeFromSummaries true to exclude the event from summaries
*/
public FeatureRequest(long timestamp, String key, LDContext context, int version, int variation, LDValue value,
LDValue defaultVal, EvaluationReason reason, String prereqOf, boolean trackEvents,
Long debugEventsUntilDate, boolean debug, long samplingRatio, boolean excludeFromSummaries) {
this(timestamp, key, context, version, variation, value, defaultVal, reason, prereqOf, trackEvents,
debugEventsUntilDate, debug, samplingRatio, excludeFromSummaries, false);
}

/**
Expand Down Expand Up @@ -311,6 +340,23 @@ public boolean isExcludeFromSummaries() {
return excludeFromSummaries;
}

/**
* True if an override affected this evaluation. The override can be direct or transitive. It is
* direct when the evaluated flag came from the SDK's override store. It is transitive when a
* prerequisite flag at any depth, or a segment read during the evaluation, came from that store.
* <p>
* The event processor keys on this value alone. It produces no individual feature event and no
* debug event for such an evaluation, and it counts the evaluation in a separate summary counter
* that carries the override-affected marker.
* <p>
* Flag overrides are currently experimental and subject to change.
*
* @return true if an override affected the evaluation
*/
public boolean isOverrideAffected() {
return overrideAffected;
}

@Override
public long getSamplingRatio() {
return samplingRatio;
Expand All @@ -324,7 +370,7 @@ public long getSamplingRatio() {
public FeatureRequest toDebugEvent() {
return new FeatureRequest(getCreationDate(), getKey(), getContext(), getVersion(),
getVariation(), getValue(), getDefaultVal(), getReason(), getPrereqOf(),
false, null, true, samplingRatio, excludeFromSummaries);
false, null, true, samplingRatio, excludeFromSummaries, overrideAffected);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,28 +280,11 @@ private void writeSummaryEvent(EventSummarizer.EventSummary summary, JsonWriter
jw.name("counters");
jw.beginArray();

for (int i = 0; i < flagInfo.versionsAndVariations.size(); i++) {
int version = flagInfo.versionsAndVariations.keyAt(i);
SimpleIntKeyedMap<CounterValue> variations = flagInfo.versionsAndVariations.valueAt(i);
for (int j = 0; j < variations.size(); j++) {
int variation = variations.keyAt(j);
CounterValue counter = variations.valueAt(j);

jw.beginObject();

if (variation >= 0) {
jw.name("variation").value(variation);
}
if (version >= 0) {
jw.name("version").value(version);
} else {
jw.name("unknown").value(true);
}
writeLDValue("value", counter.flagValue, jw);
jw.name("count").value(counter.count);

jw.endObject();
}
writeCounters(jw, flagInfo.versionsAndVariations, false);
SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> overrideAffectedCounters =
flagInfo.overrideAffectedVersionsAndVariationsIfAny();
if (overrideAffectedCounters != null) {
writeCounters(jw, overrideAffectedCounters, true);
}

jw.endArray(); // end of "counters" array
Expand All @@ -312,6 +295,37 @@ private void writeSummaryEvent(EventSummarizer.EventSummary summary, JsonWriter
jw.endObject(); // end of summary event object
}

private void writeCounters(JsonWriter jw, SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> versionsAndVariations,
boolean overrideAffected) throws IOException {
for (int i = 0; i < versionsAndVariations.size(); i++) {
int version = versionsAndVariations.keyAt(i);
SimpleIntKeyedMap<CounterValue> variations = versionsAndVariations.valueAt(i);
for (int j = 0; j < variations.size(); j++) {
int variation = variations.keyAt(j);
CounterValue counter = variations.valueAt(j);

jw.beginObject();

if (variation >= 0) {
jw.name("variation").value(variation);
}
if (version >= 0) {
jw.name("version").value(version);
} else {
jw.name("unknown").value(true);
}
// The override-affected marker is present only when true, like the unknown marker.
if (overrideAffected) {
jw.name("overrideAffected").value(true);
}
writeLDValue("value", counter.flagValue, jw);
jw.name("count").value(counter.count);

jw.endObject();
}
}
}

private void writeKindAndCreationDate(JsonWriter jw, String kind, long creationDate) throws IOException {
jw.name("kind").value(kind);
jw.name("creationDate").value(creationDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,33 @@ void summarizeEvent(
LDValue defaultValue,
LDContext context
) {
eventsState.incrementCounter(flagKey, flagVersion, variation, value, defaultValue, context);
summarizeEvent(timestamp, flagKey, flagVersion, variation, value, defaultValue, context, false);
}

/**
* Adds information about an evaluation to our counters. An override-affected evaluation is
* counted in a separate counter from other evaluations of the same flag, version, and variation.
*
* @param timestamp the millisecond timestamp
* @param flagKey the flag key
* @param flagVersion the flag version, or -1 if the flag is unknown
* @param variation the result variation, or -1 if none
* @param value the result value
* @param defaultValue the application default value
* @param context the evaluation context
* @param overrideAffected true if an override affected the evaluation
*/
void summarizeEvent(
long timestamp,
String flagKey,
int flagVersion,
int variation,
LDValue value,
LDValue defaultValue,
LDContext context,
boolean overrideAffected
) {
eventsState.incrementCounter(flagKey, flagVersion, variation, value, defaultValue, context, overrideAffected);
eventsState.noteTimestamp(timestamp);
}

Expand Down Expand Up @@ -120,7 +146,8 @@ void incrementCounter(
int variation,
LDValue flagValue,
LDValue defaultVal,
LDContext context
LDContext context,
boolean overrideAffected
) {
FlagInfo flagInfo = counters.get(flagKey);
if (flagInfo == null) {
Expand All @@ -131,10 +158,12 @@ void incrementCounter(
flagInfo.contextKinds.add(context.getIndividualContext(i).getKind().toString());
}

SimpleIntKeyedMap<CounterValue> variations = flagInfo.versionsAndVariations.get(flagVersion);
SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> versionsAndVariations =
overrideAffected ? flagInfo.overrideAffectedVersionsAndVariations() : flagInfo.versionsAndVariations;
SimpleIntKeyedMap<CounterValue> variations = versionsAndVariations.get(flagVersion);
if (variations == null) {
variations = new SimpleIntKeyedMap<>();
flagInfo.versionsAndVariations.put(flagVersion, variations);
versionsAndVariations.put(flagVersion, variations);
}

CounterValue value = variations.get(variation);
Expand Down Expand Up @@ -175,21 +204,55 @@ public int hashCode() {

static final class FlagInfo {
final LDValue defaultVal;
// Counters for evaluations that no override affected, keyed by version and then by variation.
final SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> versionsAndVariations;
// Counters for override-affected evaluations, with the same shape. Most flags never have any,
// so the map is created on first use.
private SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> overrideAffectedVersionsAndVariations;
final Set<String> contextKinds;

FlagInfo(LDValue defaultVal, SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> versionsAndVariations,
Set<String> contextKinds) {
this(defaultVal, versionsAndVariations, null, contextKinds);
}

FlagInfo(LDValue defaultVal, SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> versionsAndVariations,
SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> overrideAffectedVersionsAndVariations,
Set<String> contextKinds) {
this.defaultVal = defaultVal;
this.versionsAndVariations = versionsAndVariations;
this.overrideAffectedVersionsAndVariations = overrideAffectedVersionsAndVariations;
this.contextKinds = contextKinds;
}

/**
* Returns the counters for override-affected evaluations, creating the map on first use.
*
* @return the counters, never null
*/
SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> overrideAffectedVersionsAndVariations() {
if (overrideAffectedVersionsAndVariations == null) {
overrideAffectedVersionsAndVariations = new SimpleIntKeyedMap<>();
}
return overrideAffectedVersionsAndVariations;
}

/**
* Returns the counters for override-affected evaluations, or null if there are none.
*
* @return the counters or null
*/
SimpleIntKeyedMap<SimpleIntKeyedMap<CounterValue>> overrideAffectedVersionsAndVariationsIfAny() {
return overrideAffectedVersionsAndVariations == null || overrideAffectedVersionsAndVariations.size() == 0
? null : overrideAffectedVersionsAndVariations;
}

@Override
public boolean equals(Object other) { // used only in tests
if (other instanceof FlagInfo) {
FlagInfo o = (FlagInfo)other;
return o.defaultVal.equals(this.defaultVal) && o.versionsAndVariations.equals(this.versionsAndVariations) &&
Objects.equals(o.overrideAffectedVersionsAndVariationsIfAny(), this.overrideAffectedVersionsAndVariationsIfAny()) &&
o.contextKinds.equals(this.contextKinds);
}
return false;
Expand All @@ -202,8 +265,10 @@ public int hashCode() { // used only in tests

@Override
public String toString() { // used only in tests
return "(default=" + defaultVal + ", counters=" + versionsAndVariations + ", contextKinds=" +
String.join(",", contextKinds) + ")";
return "(default=" + defaultVal + ", counters=" + versionsAndVariations +
(overrideAffectedVersionsAndVariationsIfAny() == null ? "" :
", overrideAffectedCounters=" + overrideAffectedVersionsAndVariations) +
", contextKinds=" + String.join(",", contextKinds) + ")";
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
public interface EventSummarizerInterface {
/**
* Adds information about an evaluation to the summary.
* Adds information about an evaluation that no override affected to the summary.
*
* @param timestamp the millisecond timestamp
* @param flagKey the flag key
Expand All @@ -25,14 +25,42 @@ public interface EventSummarizerInterface {
* @param defaultValue the application default value
* @param context the evaluation context
*/
void summarizeEvent(
default void summarizeEvent(
long timestamp,
String flagKey,
int flagVersion,
int variation,
LDValue value,
LDValue defaultValue,
LDContext context
) {
summarizeEvent(timestamp, flagKey, flagVersion, variation, value, defaultValue, context, false);
}

/**
* Adds information about an evaluation to the summary.
* <p>
* Override-affected evaluations are counted separately from other evaluations of the same flag,
* version, and variation, so the marker takes part in the counter key.
*
* @param timestamp the millisecond timestamp
* @param flagKey the flag key
* @param flagVersion the flag version, or -1 if the flag is unknown
* @param variation the result variation, or -1 if none
* @param value the result value
* @param defaultValue the application default value
* @param context the evaluation context
* @param overrideAffected true if an override affected the evaluation
*/
void summarizeEvent(
long timestamp,
String flagKey,
int flagVersion,
int variation,
LDValue value,
LDValue defaultValue,
LDContext context,
boolean overrideAffected
);

/**
Expand Down
Loading
Loading