Skip to content

Commit d6ec5f5

Browse files
authored
Feat/confirm allocations individually (#7)
* feat: send confirmation events ony for touched allocations * fix: tests and allocations bug * chore: updating changelog and readme * fix: checkstyle
1 parent c54870e commit d6ec5f5

14 files changed

+130
-63
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33

44
All updates that affect the released version of the Ascend Java Client will be detailed here. This project adheres to [Semantic Versioning](http://semver.org).
55

6+
## [0.7.1-beta] - 2019-10-02
7+
### Added
8+
- confirmations/contaminations are now only sent when the allocation is used
9+
- improved logging messages
10+
611
## [0.7.0-beta] - 2019-05-29
712
### Fixed
813
- Participant's now initialize with the client instead of getting reused like before.

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<dependency>
1616
<groupId>ai.evolv</groupId>
1717
<artifactId>ascend-sdk</artifactId>
18-
<version>0.7.0</version>
18+
<version>0.7.1</version>
1919
</dependency>
2020
```
2121

@@ -40,8 +40,7 @@
4040
```java
4141
ascendClient.confirm();
4242
```
43-
*Note: After the AscendClient has initialized, it is important to confirm the participant into the experiment. This action
44-
records the participant's allocation and sends the info back to Ascend.*
43+
*Note: This action records the participant's allocation and sends the info back to Ascend.*
4544

4645
### Value Retrieval
4746

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ plugins {
77

88
group = "ai.evolv"
99
archivesBaseName = "ascend-sdk"
10-
version = "0.7.0"
10+
version = "0.7.1"
1111

1212
sourceCompatibility = 1.8
1313

src/main/java/ai/evolv/Allocations.java

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,20 @@
1818

1919
class Allocations {
2020

21+
private static final String TOUCHED = "touched";
22+
private static final String CONFIRMED = "confirmed";
23+
private static final String CONTAMINATED = "contaminated";
24+
2125
private static final Logger LOGGER = LoggerFactory.getLogger(Allocations.class);
2226

2327
private final JsonArray allocations;
28+
private final AscendAllocationStore store;
2429

2530
private final Audience audience = new Audience();
2631

27-
Allocations(JsonArray allocations) {
32+
Allocations(JsonArray allocations, AscendAllocationStore store) {
2833
this.allocations = allocations;
34+
this.store = store;
2935
}
3036

3137
<T> T getValueFromAllocations(String key, Class<T> cls, AscendParticipant participant)
@@ -40,10 +46,17 @@ <T> T getValueFromAllocations(String key, Class<T> cls, AscendParticipant partic
4046
if (!audience.filter(participant.getUserAttributes(), allocation)) {
4147
try {
4248
JsonElement element = getElementFromGenome(allocation.get("genome"), keyParts);
43-
return new Gson().fromJson(element, cls);
49+
T value = new Gson().fromJson(element, cls);
50+
if (value != null) {
51+
LOGGER.debug(String.format("Found value for key '%s' in experiment %s",
52+
key, allocation.get("eid").getAsString()));
53+
markTouched(allocation);
54+
store.put(participant.getUserId(), allocations);
55+
}
56+
return value;
4457
} catch (AscendKeyError e) {
45-
LOGGER.debug(String.format("Unable to find key %s in experiment %s.",
46-
keyParts.toString(), allocation.get("eid").getAsString()), e);
58+
LOGGER.debug(String.format("Unable to find key '%s' in experiment %s.",
59+
key, allocation.get("eid").getAsString()));
4760
continue;
4861
}
4962
}
@@ -123,4 +136,34 @@ Set<String> getActiveExperiments() {
123136
}
124137
return activeExperiments;
125138
}
139+
140+
static JsonObject markTouched(JsonObject allocation) {
141+
allocation.addProperty(TOUCHED, true);
142+
return allocation;
143+
}
144+
145+
static boolean isTouched(JsonObject allocation) {
146+
return allocation.has(TOUCHED) &&
147+
allocation.get(TOUCHED).getAsBoolean();
148+
}
149+
150+
static JsonObject markConfirmed(JsonObject allocation) {
151+
allocation.addProperty(CONFIRMED, true);
152+
return allocation;
153+
}
154+
155+
static boolean isConfirmed(JsonObject allocation) {
156+
return allocation.has(CONFIRMED) &&
157+
allocation.get(CONFIRMED).getAsBoolean();
158+
}
159+
160+
static JsonObject markContaminated(JsonObject allocation) {
161+
allocation.addProperty(CONTAMINATED, true);
162+
return allocation;
163+
}
164+
165+
static boolean isContaminated(JsonObject allocation) {
166+
return allocation.has(CONTAMINATED) &&
167+
allocation.get(CONTAMINATED).getAsBoolean();
168+
}
126169
}

src/main/java/ai/evolv/Allocator.java

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ enum AllocationStatus {
3838
this.participant = participant;
3939
this.httpClient = config.getHttpClient();
4040
this.allocationStatus = AllocationStatus.FETCHING;
41-
this.eventEmitter = new EventEmitter(config, participant);
41+
this.eventEmitter = new EventEmitter(config, participant, this.store);
4242

4343
}
4444

@@ -86,15 +86,8 @@ CompletableFuture<JsonArray> fetchAllocations() {
8686
store.put(participant.getUserId(), allocations);
8787
allocationStatus = AllocationStatus.RETRIEVED;
8888

89-
if (confirmationSandbagged) {
90-
eventEmitter.confirm(allocations);
91-
}
92-
93-
if (contaminationSandbagged) {
94-
eventEmitter.contaminate(allocations);
95-
}
96-
97-
executionQueue.executeAllWithValuesFromAllocations(allocations);
89+
executionQueue.executeAllWithValuesFromAllocations(allocations,
90+
eventEmitter, confirmationSandbagged, contaminationSandbagged);
9891

9992
return allocations;
10093
}).exceptionally(e -> {
@@ -107,16 +100,9 @@ JsonArray resolveAllocationFailure() {
107100
JsonArray previousAllocations = store.get(participant.getUserId());
108101
if (allocationsNotEmpty(previousAllocations)) {
109102
LOGGER.debug("Falling back to participant's previous allocation.");
110-
if (confirmationSandbagged) {
111-
eventEmitter.confirm(previousAllocations);
112-
}
113-
114-
if (contaminationSandbagged) {
115-
eventEmitter.contaminate(previousAllocations);
116-
}
117-
118103
allocationStatus = AllocationStatus.RETRIEVED;
119-
executionQueue.executeAllWithValuesFromAllocations(previousAllocations);
104+
executionQueue.executeAllWithValuesFromAllocations(previousAllocations,
105+
eventEmitter, confirmationSandbagged, contaminationSandbagged);
120106
} else {
121107
LOGGER.debug("Falling back to the supplied defaults.");
122108
allocationStatus = AllocationStatus.FAILED;

src/main/java/ai/evolv/AscendClientFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ private static AscendClient createClient(AscendConfig config, AscendParticipant
5353
CompletableFuture<JsonArray> futureAllocations = allocator.fetchAllocations();
5454

5555
return new AscendClientImpl(config,
56-
new EventEmitter(config, participant),
56+
new EventEmitter(config, participant, store),
5757
futureAllocations,
5858
allocator,
5959
reconciliationNeeded,

src/main/java/ai/evolv/AscendClientImpl.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ public <T> T get(String key, T defaultValue) {
5050
}
5151

5252
GenericClass<T> cls = new GenericClass(defaultValue.getClass());
53-
T value = new Allocations(allocations).getValueFromAllocations(key, cls.getMyType(),
54-
participant);
53+
T value = new Allocations(allocations, store).getValueFromAllocations(key,
54+
cls.getMyType(), participant);
5555

5656
if (value == null) {
57-
throw new AscendKeyError("Got null when retrieving key from allocations.");
57+
throw new AscendKeyError("Got null when retrieving key " +
58+
"from allocations.");
5859
}
5960

6061
return value;
@@ -71,7 +72,7 @@ public <T> T get(String key, T defaultValue) {
7172

7273
@Override
7374
public <T> void subscribe(String key, T defaultValue, AscendAction<T> function) {
74-
Execution execution = new Execution<>(key, defaultValue, function, participant);
75+
Execution execution = new Execution<>(key, defaultValue, function, participant, store);
7576
if (previousAllocations) {
7677
try {
7778
JsonArray allocations = store.get(participant.getUserId());

src/main/java/ai/evolv/DefaultAllocationStore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public class DefaultAllocationStore implements AscendAllocationStore {
66

77
private LruCache cache;
88

9-
DefaultAllocationStore(int size) {
9+
public DefaultAllocationStore(int size) {
1010
this.cache = new LruCache(size);
1111
}
1212

src/main/java/ai/evolv/EventEmitter.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ class EventEmitter {
2020
private final HttpClient httpClient;
2121
private final AscendConfig config;
2222
private final AscendParticipant participant;
23+
private final AscendAllocationStore store;
2324

2425
private final Audience audience = new Audience();
2526

26-
EventEmitter(AscendConfig config, AscendParticipant participant) {
27+
EventEmitter(AscendConfig config, AscendParticipant participant, AscendAllocationStore store) {
2728
this.httpClient = config.getHttpClient();
2829
this.config = config;
2930
this.participant = participant;
31+
this.store = store;
3032
}
3133

3234
void emit(String key) {
@@ -49,17 +51,29 @@ void contaminate(JsonArray allocations) {
4951

5052
void sendAllocationEvents(String key, JsonArray allocations) {
5153
for (JsonElement a : allocations) {
52-
if (!audience.filter(participant.getUserAttributes(), a.getAsJsonObject())) {
53-
JsonObject allocation = a.getAsJsonObject();
54+
JsonObject allocation = a.getAsJsonObject();
55+
if (!audience.filter(participant.getUserAttributes(), allocation)
56+
&& Allocations.isTouched(allocation)
57+
&& !Allocations.isConfirmed(allocation)
58+
&& !Allocations.isContaminated(allocation)) {
5459
String experimentId = allocation.get("eid").getAsString();
5560
String candidateId = allocation.get("cid").getAsString();
5661

5762
String url = getEventUrl(key, experimentId, candidateId);
5863
makeEventRequest(url);
64+
65+
if (key.equals(CONFIRM_KEY)) {
66+
Allocations.markConfirmed(allocation);
67+
} else if (key.equals(CONTAMINATE_KEY)) {
68+
Allocations.markContaminated(allocation);
69+
}
70+
5971
continue;
6072
}
61-
LOGGER.debug(String.format("%s event filtered.", key));
73+
LOGGER.debug(String.format("%s event filtered for experiment %s.", key,
74+
allocation.get("eid").getAsString()));
6275
}
76+
store.put(this.participant.getUserId(), allocations);
6377
}
6478

6579
String getEventUrl(String type, Double score) {

src/main/java/ai/evolv/Execution.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ class Execution<T> {
1414
private final T defaultValue;
1515
private final AscendAction function;
1616
private final AscendParticipant participant;
17+
private final AscendAllocationStore store;
1718

1819
private Set<String> alreadyExecuted = new HashSet<>();
1920

20-
Execution(String key, T defaultValue, AscendAction<T> function, AscendParticipant participant) {
21+
Execution(String key, T defaultValue, AscendAction<T> function, AscendParticipant participant,
22+
AscendAllocationStore store) {
2123
this.key = key;
2224
this.defaultValue = defaultValue;
2325
this.function = function;
2426
this.participant = participant;
27+
this.store = store;
2528
}
2629

2730
String getKey() {
@@ -30,7 +33,7 @@ String getKey() {
3033

3134
void executeWithAllocation(JsonArray rawAllocations) throws AscendKeyError {
3235
GenericClass<T> cls = new GenericClass(defaultValue.getClass());
33-
Allocations allocations = new Allocations(rawAllocations);
36+
Allocations allocations = new Allocations(rawAllocations, store);
3437
T value = allocations.getValueFromAllocations(key, cls.getMyType(), participant);
3538

3639
if (value == null) {

0 commit comments

Comments
 (0)