-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathEppoClient.java
More file actions
223 lines (197 loc) · 7.5 KB
/
EppoClient.java
File metadata and controls
223 lines (197 loc) · 7.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package cloud.eppo;
import cloud.eppo.api.Configuration;
import cloud.eppo.api.IAssignmentCache;
import cloud.eppo.cache.ExpiringInMemoryAssignmentCache;
import cloud.eppo.cache.LRUInMemoryAssignmentCache;
import cloud.eppo.http.EppoConfigurationClient;
import cloud.eppo.logging.AssignmentLogger;
import cloud.eppo.logging.BanditLogger;
import cloud.eppo.parser.ConfigurationParser;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class used to ingest and use the flag and bandit configurations retrieved from Eppo This class
* uses the Singleton pattern. First the singleton must be initialized via it's Builder's
* buildAndInit() method. Then call getInstance() to access the singleton and call methods to get
* assignments and bandit actions.
*/
public class EppoClient extends BaseEppoClient<JsonNode> {
private static final Logger log = LoggerFactory.getLogger(EppoClient.class);
private static final boolean DEFAULT_IS_GRACEFUL_MODE = true;
private static final boolean DEFAULT_FORCE_REINITIALIZE = false;
private static final long DEFAULT_POLLING_INTERVAL_MS = 30 * 1000;
private static final long DEFAULT_JITTER_INTERVAL_RATIO = 10;
private static EppoClient instance;
public static EppoClient getInstance() {
if (instance == null) {
throw new IllegalStateException("Eppo SDK has not been initialized");
}
return instance;
}
private EppoClient(
String sdkKey,
String sdkName,
String sdkVersion,
@Nullable String baseUrl,
@Nullable AssignmentLogger assignmentLogger,
@Nullable BanditLogger banditLogger,
boolean isGracefulMode,
@Nullable IAssignmentCache assignmentCache,
@Nullable IAssignmentCache banditAssignmentCache,
ConfigurationParser<JsonNode> configurationParser,
EppoConfigurationClient configurationClient) {
super(
sdkKey,
sdkName,
sdkVersion,
baseUrl,
assignmentLogger,
banditLogger,
null,
isGracefulMode,
false,
true,
null,
assignmentCache,
banditAssignmentCache,
configurationParser,
configurationClient);
}
/**
* Creates a new EppoClient Builder object with the specified SDK Key.
*
* @param sdkKey (see <a href="https://docs.geteppo.com/sdks/sdk-keys/">SDK Keys</a>)
*/
public static Builder builder(@NotNull String sdkKey) {
return new Builder(sdkKey);
}
/** Builder pattern to initialize the EppoClient singleton */
public static class Builder {
private final String sdkKey;
private AssignmentLogger assignmentLogger;
private BanditLogger banditLogger;
private boolean isGracefulMode = DEFAULT_IS_GRACEFUL_MODE;
private boolean forceReinitialize = DEFAULT_FORCE_REINITIALIZE;
private long pollingIntervalMs = DEFAULT_POLLING_INTERVAL_MS;
private String apiBaseUrl = null;
@Nullable private Consumer<Configuration> configChangeCallback;
// Assignment and bandit caching on by default. To disable, call
// `builder.assignmentCache(null).banditAssignmentCache(null);`
private IAssignmentCache assignmentCache = new LRUInMemoryAssignmentCache(100);
private IAssignmentCache banditAssignmentCache =
new ExpiringInMemoryAssignmentCache(10, TimeUnit.MINUTES);
private Builder(@NotNull String sdkKey) {
this.sdkKey = sdkKey;
}
/**
* Assignment logger to use to record when variations were assigned. This is needed if you want
* Eppo to analyze experiments controlled by flags.
*/
public Builder assignmentLogger(AssignmentLogger assignmentLogger) {
this.assignmentLogger = assignmentLogger;
return this;
}
/**
* Bandit logger to use to record when a bandit has assigned an action. This is needed if you
* are using contextual multi-armed bandits.
*/
public Builder banditLogger(BanditLogger banditLogger) {
this.banditLogger = banditLogger;
return this;
}
/**
* Sets the initial graceful mode of the client. When on (which is the default), flag evaluation
* errors will be caught, and the default value returned. When off, the errors will be rethrown.
*/
public Builder isGracefulMode(boolean isGracefulMode) {
this.isGracefulMode = isGracefulMode;
return this;
}
/**
* Sets whether the singleton client should be recreated if one already has been. fetch an
* updated configuration. If true, a new client will be instantiated and a new fetch for
* configurations will be performed. If false (which is the default), initialization will be
* ignored and the previously initialized client will be used.
*/
public Builder forceReinitialize(boolean forceReinitialize) {
this.forceReinitialize = forceReinitialize;
return this;
}
/**
* Sets how often the client should check for updated configurations, in milliseconds. The
* default is 30,000 (poll every 30 seconds).
*/
public Builder pollingIntervalMs(long pollingIntervalMs) {
this.pollingIntervalMs = pollingIntervalMs;
return this;
}
/**
* Overrides the base URL from where the SDK fetches configurations. This typically should not
* be explicitly set so that the default API URL is used.
*/
public Builder apiBaseUrl(String apiBaseUrl) {
this.apiBaseUrl = apiBaseUrl;
return this;
}
public Builder assignmentCache(IAssignmentCache assignmentCache) {
this.assignmentCache = assignmentCache;
return this;
}
public Builder banditAssignmentCache(IAssignmentCache banditAssignmentCache) {
this.banditAssignmentCache = banditAssignmentCache;
return this;
}
/**
* Registers a callback for when a new configuration is applied to the `EppoClient` instance.
*/
public Builder onConfigurationChange(Consumer<Configuration> configChangeCallback) {
this.configChangeCallback = configChangeCallback;
return this;
}
public EppoClient buildAndInit() {
AppDetails appDetails = AppDetails.getInstance();
String sdkName = appDetails.getName();
String sdkVersion = appDetails.getVersion();
if (instance != null) {
// Stop any active polling.
instance.stopPolling();
if (forceReinitialize) {
log.warn(
"Eppo SDK is already initialized, reinitializing since forceReinitialize is true");
} else {
log.warn(
"Eppo SDK is already initialized, skipping reinitialization since forceReinitialize is false");
return instance;
}
}
instance =
new EppoClient(
sdkKey,
sdkName,
sdkVersion,
apiBaseUrl,
assignmentLogger,
banditLogger,
isGracefulMode,
assignmentCache,
banditAssignmentCache,
new JacksonConfigurationParser(),
new OkHttpEppoClient());
if (configChangeCallback != null) {
instance.onConfigurationChange(configChangeCallback);
}
// Fetch first configuration
instance.loadConfiguration();
// start polling, if enabled.
if (pollingIntervalMs > 0) {
instance.startPolling(pollingIntervalMs, pollingIntervalMs / DEFAULT_JITTER_INTERVAL_RATIO);
}
return instance;
}
}
}