Skip to content

Commit b58e756

Browse files
authored
Update protos to include bloom filter (#4564)
1 parent 7848cc1 commit b58e756

File tree

6 files changed

+214
-47
lines changed

6 files changed

+214
-47
lines changed

firebase-abt/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
* [changed] Internal changes to improve experiment reporting.
4+
35
# 21.1.0
46
* [changed] Internal changes to ensure functionality alignment with other
57
SDK releases.

firebase-abt/src/main/java/com/google/firebase/abt/FirebaseABTesting.java

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@
3030
import java.util.ArrayList;
3131
import java.util.Collection;
3232
import java.util.Deque;
33-
import java.util.HashSet;
3433
import java.util.List;
3534
import java.util.Map;
36-
import java.util.Set;
3735

3836
/**
3937
* Manages Firebase A/B Testing Experiments.
@@ -144,11 +142,11 @@ public void removeAllExperiments() throws AbtException {
144142
}
145143

146144
/**
147-
* Gets the origin service's list of experiments in the app.
145+
* Gets the origin service's list of experiments in the app via the Analytics SDK.
148146
*
149147
* <p>Note: This is a blocking call and therefore should be called from a worker thread.
150148
*
151-
* @return the origin service's list of experiments in the app.
149+
* @return the origin service's list of experiments in the app as {@link AbtExperimentInfo}s.
152150
* @throws AbtException If there is no Analytics SDK.
153151
*/
154152
@WorkerThread
@@ -204,12 +202,10 @@ public void reportActiveExperiment(AbtExperimentInfo activeExperiment) throws Ab
204202
public void validateRunningExperiments(List<AbtExperimentInfo> runningExperiments)
205203
throws AbtException {
206204
throwAbtExceptionIfAnalyticsIsNull();
207-
Set<String> runningExperimentIds = new HashSet<>();
208-
for (AbtExperimentInfo runningExperiment : runningExperiments) {
209-
runningExperimentIds.add(runningExperiment.getExperimentId());
210-
}
205+
206+
// Get all experiments in Analytics and remove the ones that aren't running.
211207
List<ConditionalUserProperty> experimentsToRemove =
212-
getExperimentsToRemove(getAllExperimentsInAnalytics(), runningExperimentIds);
208+
getExperimentsToRemove(getAllExperiments(), runningExperiments);
213209
removeExperiments(experimentsToRemove);
214210
}
215211

@@ -245,34 +241,29 @@ private void replaceAllExperimentsWith(List<AbtExperimentInfo> replacementExperi
245241
return;
246242
}
247243

248-
Set<String> replacementExperimentIds = new HashSet<>();
249-
for (AbtExperimentInfo replacementExperiment : replacementExperiments) {
250-
replacementExperimentIds.add(replacementExperiment.getExperimentId());
251-
}
252-
253-
List<ConditionalUserProperty> experimentsInAnalytics = getAllExperimentsInAnalytics();
254-
Set<String> idsOfExperimentsInAnalytics = new HashSet<>();
255-
for (ConditionalUserProperty experimentInAnalytics : experimentsInAnalytics) {
256-
idsOfExperimentsInAnalytics.add(experimentInAnalytics.name);
257-
}
244+
// Get all experiments in Analytics.
245+
List<AbtExperimentInfo> experimentsInAnalytics = getAllExperiments();
258246

247+
// Remove experiments no longer assigned.
259248
List<ConditionalUserProperty> experimentsToRemove =
260-
getExperimentsToRemove(experimentsInAnalytics, replacementExperimentIds);
249+
getExperimentsToRemove(experimentsInAnalytics, replacementExperiments);
261250
removeExperiments(experimentsToRemove);
262251

252+
// Add newly assigned or updated (changed variant id).
263253
List<AbtExperimentInfo> experimentsToAdd =
264-
getExperimentsToAdd(replacementExperiments, idsOfExperimentsInAnalytics);
254+
getExperimentsToAdd(replacementExperiments, experimentsInAnalytics);
265255
addExperiments(experimentsToAdd);
266256
}
267257

268258
/** Returns this origin's experiments in Analytics that are no longer assigned to this App. */
269259
private ArrayList<ConditionalUserProperty> getExperimentsToRemove(
270-
List<ConditionalUserProperty> experimentsInAnalytics, Set<String> replacementExperimentIds) {
260+
List<AbtExperimentInfo> experimentsInAnalytics,
261+
List<AbtExperimentInfo> replacementExperiments) {
271262

272263
ArrayList<ConditionalUserProperty> experimentsToRemove = new ArrayList<>();
273-
for (ConditionalUserProperty experimentInAnalytics : experimentsInAnalytics) {
274-
if (!replacementExperimentIds.contains(experimentInAnalytics.name)) {
275-
experimentsToRemove.add(experimentInAnalytics);
264+
for (AbtExperimentInfo experimentInAnalytics : experimentsInAnalytics) {
265+
if (!experimentsListContainsExperiment(replacementExperiments, experimentInAnalytics)) {
266+
experimentsToRemove.add(experimentInAnalytics.toConditionalUserProperty(originService));
276267
}
277268
}
278269
return experimentsToRemove;
@@ -283,17 +274,33 @@ private ArrayList<ConditionalUserProperty> getExperimentsToRemove(
283274
* to this origin's list of experiments in Analytics.
284275
*/
285276
private ArrayList<AbtExperimentInfo> getExperimentsToAdd(
286-
List<AbtExperimentInfo> replacementExperiments, Set<String> idsOfExperimentsInAnalytics) {
277+
List<AbtExperimentInfo> replacementExperiments,
278+
List<AbtExperimentInfo> experimentInfoFromAnalytics) {
287279

288280
ArrayList<AbtExperimentInfo> experimentsToAdd = new ArrayList<>();
289281
for (AbtExperimentInfo replacementExperiment : replacementExperiments) {
290-
if (!idsOfExperimentsInAnalytics.contains(replacementExperiment.getExperimentId())) {
282+
if (!experimentsListContainsExperiment(experimentInfoFromAnalytics, replacementExperiment)) {
291283
experimentsToAdd.add(replacementExperiment);
292284
}
293285
}
294286
return experimentsToAdd;
295287
}
296288

289+
private boolean experimentsListContainsExperiment(
290+
List<AbtExperimentInfo> experiments, AbtExperimentInfo experiment) {
291+
String experimentId = experiment.getExperimentId();
292+
String variantId = experiment.getVariantId();
293+
294+
for (AbtExperimentInfo experimentInfo : experiments) {
295+
if (experimentInfo.getExperimentId().equals(experimentId)
296+
&& experimentInfo.getVariantId().equals(variantId)) {
297+
return true;
298+
}
299+
}
300+
301+
return false;
302+
}
303+
297304
/** Adds the given experiments to the origin's list in Analytics. */
298305
private void addExperiments(List<AbtExperimentInfo> experimentsToAdd) {
299306

@@ -369,7 +376,7 @@ private int getMaxUserPropertiesInAnalytics() {
369376
* Returns a list of all this origin's experiments in this App's Analytics SDK.
370377
*
371378
* <p>The list is sorted chronologically by the experiment start time, with the oldest experiment
372-
* at index 0.
379+
* at index 0. Experiments are stored as {@link ConditionalUserProperty}s in Analytics.
373380
*/
374381
@WorkerThread
375382
private List<ConditionalUserProperty> getAllExperimentsInAnalytics() {

firebase-abt/src/test/java/com/google/firebase/abt/FirebaseABTestingTest.java

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,27 @@ public class FirebaseABTestingTest {
5454
private static final String TEST_EXPERIMENT_1_ID = "1";
5555
private static final String TEST_EXPERIMENT_2_ID = "2";
5656

57+
private static final String TEST_VARIANT_ID_A = "VARIANT_A";
58+
private static final String TEST_VARIANT_ID_B = "VARIANT_B";
59+
5760
private static final AbtExperimentInfo TEST_ABT_EXPERIMENT_1 =
5861
createExperimentInfo(
5962
TEST_EXPERIMENT_1_ID,
63+
TEST_VARIANT_ID_A,
6064
/*triggerEventName=*/ "",
6165
/*experimentStartTimeInEpochMillis=*/ 1000L);
62-
private static final AbtExperimentInfo TEST_ABT_EXPERIMENT_2 =
66+
private static final AbtExperimentInfo TEST_ABT_EXPERIMENT_2_VARIANT_A =
67+
createExperimentInfo(
68+
TEST_EXPERIMENT_2_ID,
69+
TEST_VARIANT_ID_A,
70+
"trigger_event_2",
71+
/*experimentStartTimeInEpochMillis=*/ 2000L);
72+
private static final AbtExperimentInfo TEST_ABT_EXPERIMENT_2_VARIANT_B =
6373
createExperimentInfo(
64-
TEST_EXPERIMENT_2_ID, "trigger_event_2", /*experimentStartTimeInEpochMillis=*/ 2000L);
74+
TEST_EXPERIMENT_2_ID,
75+
TEST_VARIANT_ID_B,
76+
"trigger_event_2",
77+
/*experimentStartTimeInEpochMillis=*/ 2000L);
6578

6679
private static final int MAX_ALLOWED_EXPERIMENTS_IN_ANALYTICS = 100;
6780

@@ -91,7 +104,7 @@ public void replaceAllExperiments_noExperimentsInAnalytics_experimentsCorrectlyS
91104

92105
firebaseAbt.replaceAllExperiments(
93106
Lists.newArrayList(
94-
TEST_ABT_EXPERIMENT_1.toStringMap(), TEST_ABT_EXPERIMENT_2.toStringMap()));
107+
TEST_ABT_EXPERIMENT_1.toStringMap(), TEST_ABT_EXPERIMENT_2_VARIANT_A.toStringMap()));
95108

96109
ArgumentCaptor<ConditionalUserProperty> analyticsExperimentArgumentCaptor =
97110
ArgumentCaptor.forClass(ConditionalUserProperty.class);
@@ -107,7 +120,8 @@ public void replaceAllExperiments_noExperimentsInAnalytics_experimentsCorrectlyS
107120

108121
// Validates that TEST_ABT_EXPERIMENT_1 and TEST_ABT_EXPERIMENT_2 have been set in Analytics.
109122
assertThat(analyticsExperiment1.toStringMap()).isEqualTo(TEST_ABT_EXPERIMENT_1.toStringMap());
110-
assertThat(analyticsExperiment2.toStringMap()).isEqualTo(TEST_ABT_EXPERIMENT_2.toStringMap());
123+
assertThat(analyticsExperiment2.toStringMap())
124+
.isEqualTo(TEST_ABT_EXPERIMENT_2_VARIANT_A.toStringMap());
111125
}
112126

113127
@Test
@@ -117,10 +131,11 @@ public void replaceAllExperiments_existExperimentsInAnalytics_experimentsCorrect
117131
.thenReturn(
118132
Lists.newArrayList(
119133
TEST_ABT_EXPERIMENT_1.toConditionalUserProperty(ORIGIN_SERVICE),
120-
TEST_ABT_EXPERIMENT_2.toConditionalUserProperty(ORIGIN_SERVICE)));
134+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toConditionalUserProperty(ORIGIN_SERVICE)));
121135

122-
AbtExperimentInfo newExperiment3 = createExperimentInfo("3", "", 1000L);
123-
AbtExperimentInfo newExperiment4 = createExperimentInfo("4", "trigger_event_4", 1000L);
136+
AbtExperimentInfo newExperiment3 = createExperimentInfo("3", TEST_VARIANT_ID_A, "", 1000L);
137+
AbtExperimentInfo newExperiment4 =
138+
createExperimentInfo("4", TEST_VARIANT_ID_A, "trigger_event_4", 1000L);
124139

125140
// Simulates the case where experiment 1 is assigned (as before), experiment 2 is no longer
126141
// assigned; experiment 3 and experiment 4 are newly assigned.
@@ -146,6 +161,47 @@ public void replaceAllExperiments_existExperimentsInAnalytics_experimentsCorrect
146161
.isEqualTo(newExperiment4.toStringMap());
147162
}
148163

164+
@Test
165+
public void
166+
replaceAllExperiments_existExperimentsInAnalyticsWithDifferentVariants_experimentsCorrectlySetInAnalytics()
167+
throws Exception {
168+
when(mockAnalyticsConnector.getConditionalUserProperties(ORIGIN_SERVICE, ""))
169+
.thenReturn(
170+
Lists.newArrayList(
171+
TEST_ABT_EXPERIMENT_1.toConditionalUserProperty(ORIGIN_SERVICE),
172+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toConditionalUserProperty(ORIGIN_SERVICE)));
173+
174+
AbtExperimentInfo newExperiment3 = createExperimentInfo("3", "b", "", 1000L);
175+
AbtExperimentInfo newExperiment4 = createExperimentInfo("4", "a", "trigger_event_4", 1000L);
176+
177+
// Simulates the case where experiments 1 and 2 are removed,
178+
// experiment 2 is re-set with a new variant, and experiments 3 and 4 are newly added.
179+
firebaseAbt.replaceAllExperiments(
180+
Lists.newArrayList(
181+
TEST_ABT_EXPERIMENT_2_VARIANT_B.toStringMap(),
182+
newExperiment3.toStringMap(),
183+
newExperiment4.toStringMap()));
184+
185+
// Validates that experiment 1 is cleared, experiment 2 is updated,
186+
// and experiment 3 and experiment 4 are set in Analytics.
187+
ArgumentCaptor<ConditionalUserProperty> analyticsExperimentArgumentCaptor =
188+
ArgumentCaptor.forClass(ConditionalUserProperty.class);
189+
verify(mockAnalyticsConnector, times(1))
190+
.clearConditionalUserProperty(TEST_EXPERIMENT_1_ID, null, null);
191+
verify(mockAnalyticsConnector, times(1))
192+
.clearConditionalUserProperty(TEST_EXPERIMENT_2_ID, null, null);
193+
verify(mockAnalyticsConnector, times(3))
194+
.setConditionalUserProperty(analyticsExperimentArgumentCaptor.capture());
195+
196+
List<ConditionalUserProperty> actualValues = analyticsExperimentArgumentCaptor.getAllValues();
197+
assertThat(AbtExperimentInfo.fromConditionalUserProperty(actualValues.get(0)).toStringMap())
198+
.isEqualTo(TEST_ABT_EXPERIMENT_2_VARIANT_B.toStringMap());
199+
assertThat(AbtExperimentInfo.fromConditionalUserProperty(actualValues.get(1)).toStringMap())
200+
.isEqualTo(newExperiment3.toStringMap());
201+
assertThat(AbtExperimentInfo.fromConditionalUserProperty(actualValues.get(2)).toStringMap())
202+
.isEqualTo(newExperiment4.toStringMap());
203+
}
204+
149205
@Test
150206
public void replaceAllExperiments_totalExperimentsExceedsAnalyticsLimit_oldExperimentsDiscarded()
151207
throws Exception {
@@ -155,17 +211,18 @@ public void replaceAllExperiments_totalExperimentsExceedsAnalyticsLimit_oldExper
155211
.thenReturn(
156212
Lists.newArrayList(
157213
TEST_ABT_EXPERIMENT_1.toConditionalUserProperty(ORIGIN_SERVICE),
158-
TEST_ABT_EXPERIMENT_2.toConditionalUserProperty(ORIGIN_SERVICE)));
214+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toConditionalUserProperty(ORIGIN_SERVICE)));
159215

160-
AbtExperimentInfo newExperiment3 = createExperimentInfo("3", "", 1000L);
161-
AbtExperimentInfo newExperiment4 = createExperimentInfo("4", "trigger_event_4", 1000L);
216+
AbtExperimentInfo newExperiment3 = createExperimentInfo("3", TEST_VARIANT_ID_A, "", 1000L);
217+
AbtExperimentInfo newExperiment4 =
218+
createExperimentInfo("4", TEST_VARIANT_ID_A, "trigger_event_4", 1000L);
162219

163220
// Simulates the case where experiment 1 and 2 are assigned (as before), experiment 3 and
164221
// experiment 4 are newly assigned.
165222
firebaseAbt.replaceAllExperiments(
166223
Lists.newArrayList(
167224
TEST_ABT_EXPERIMENT_1.toStringMap(),
168-
TEST_ABT_EXPERIMENT_2.toStringMap(),
225+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toStringMap(),
169226
newExperiment3.toStringMap(),
170227
newExperiment4.toStringMap()));
171228

@@ -231,7 +288,7 @@ public void removeAllExperiments_existExperimentsInAnalytics_experimentsClearedF
231288
.thenReturn(
232289
Lists.newArrayList(
233290
TEST_ABT_EXPERIMENT_1.toConditionalUserProperty(ORIGIN_SERVICE),
234-
TEST_ABT_EXPERIMENT_2.toConditionalUserProperty(ORIGIN_SERVICE)));
291+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toConditionalUserProperty(ORIGIN_SERVICE)));
235292

236293
firebaseAbt.removeAllExperiments();
237294

@@ -266,15 +323,15 @@ public void getAllExperiments_existExperimentsInAnalytics_returnAllExperiments()
266323
.thenReturn(
267324
Lists.newArrayList(
268325
TEST_ABT_EXPERIMENT_1.toConditionalUserProperty(ORIGIN_SERVICE),
269-
TEST_ABT_EXPERIMENT_2.toConditionalUserProperty(ORIGIN_SERVICE)));
326+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toConditionalUserProperty(ORIGIN_SERVICE)));
270327

271328
List<AbtExperimentInfo> abtExperimentInfoList = firebaseAbt.getAllExperiments();
272329

273330
assertThat(abtExperimentInfoList).hasSize(2);
274331
assertThat(abtExperimentInfoList.get(0).toStringMap())
275332
.isEqualTo(TEST_ABT_EXPERIMENT_1.toStringMap());
276333
assertThat(abtExperimentInfoList.get(1).toStringMap())
277-
.isEqualTo(TEST_ABT_EXPERIMENT_2.toStringMap());
334+
.isEqualTo(TEST_ABT_EXPERIMENT_2_VARIANT_A.toStringMap());
278335
}
279336

280337
@Test
@@ -298,7 +355,7 @@ public void getAllExperiments_analyticsSdkUnavailable_throwsAbtException() {
298355
.thenReturn(
299356
Lists.newArrayList(
300357
TEST_ABT_EXPERIMENT_1.toConditionalUserProperty(ORIGIN_SERVICE),
301-
TEST_ABT_EXPERIMENT_2.toConditionalUserProperty(ORIGIN_SERVICE)));
358+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toConditionalUserProperty(ORIGIN_SERVICE)));
302359

303360
// Update to just one experiment running
304361
firebaseAbt.validateRunningExperiments(Lists.newArrayList(TEST_ABT_EXPERIMENT_1));
@@ -315,11 +372,11 @@ public void validateRunningExperiments_noinactiveExperimentsInAnalytics_cleansUp
315372
.thenReturn(
316373
Lists.newArrayList(
317374
TEST_ABT_EXPERIMENT_1.toConditionalUserProperty(ORIGIN_SERVICE),
318-
TEST_ABT_EXPERIMENT_2.toConditionalUserProperty(ORIGIN_SERVICE)));
375+
TEST_ABT_EXPERIMENT_2_VARIANT_A.toConditionalUserProperty(ORIGIN_SERVICE)));
319376

320377
// Update still says the same two experiments are running
321378
firebaseAbt.validateRunningExperiments(
322-
Lists.newArrayList(TEST_ABT_EXPERIMENT_1, TEST_ABT_EXPERIMENT_2));
379+
Lists.newArrayList(TEST_ABT_EXPERIMENT_1, TEST_ABT_EXPERIMENT_2_VARIANT_A));
323380

324381
// Verify nothing cleared
325382
verify(mockAnalyticsConnector, never()).clearConditionalUserProperty(any(), any(), any());
@@ -346,11 +403,14 @@ public void reportActiveExperiment_setsNullTriggerCondition() throws Exception {
346403
}
347404

348405
private static AbtExperimentInfo createExperimentInfo(
349-
String experimentId, String triggerEventName, long experimentStartTimeInEpochMillis) {
406+
String experimentId,
407+
String variantId,
408+
String triggerEventName,
409+
long experimentStartTimeInEpochMillis) {
350410

351411
return new AbtExperimentInfo(
352412
experimentId,
353-
VARIANT_ID_VALUE,
413+
variantId,
354414
triggerEventName,
355415
new Date(experimentStartTimeInEpochMillis),
356416
TRIGGER_TIMEOUT_IN_MILLIS_VALUE,

0 commit comments

Comments
 (0)