Skip to content

Commit b08b14d

Browse files
committed
benchmark the sdk interface
1 parent 35f7576 commit b08b14d

6 files changed

Lines changed: 76629 additions & 67 deletions

File tree

‎pom.xml‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@
301301
<failsOnError>true</failsOnError>
302302
<linkXRef>true</linkXRef>
303303
<violationSeverity>warning</violationSeverity>
304+
<sourceDirectories>
305+
<sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
306+
<sourceDirectory>${project.basedir}/src/jmh/java</sourceDirectory>
307+
</sourceDirectories>
304308
</configuration>
305309
<executions>
306310
<execution>
@@ -420,6 +424,20 @@
420424
</sources>
421425
</configuration>
422426
</execution>
427+
<execution>
428+
<id>add-jmh-resources</id>
429+
<phase>generate-resources</phase>
430+
<goals>
431+
<goal>add-resource</goal>
432+
</goals>
433+
<configuration>
434+
<resources>
435+
<resource>
436+
<directory>${project.basedir}/src/jmh/resources</directory>
437+
</resource>
438+
</resources>
439+
</configuration>
440+
</execution>
423441
</executions>
424442
</plugin>
425443
<plugin>
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package com.flagsmith;
2+
3+
import com.flagsmith.config.FlagsmithConfig;
4+
import com.flagsmith.exceptions.FlagsmithClientError;
5+
import com.flagsmith.flagengine.EvaluationContext;
6+
import com.flagsmith.models.Segment;
7+
import com.flagsmith.models.environments.EnvironmentModel;
8+
import java.io.IOException;
9+
import java.io.InputStream;
10+
import java.util.List;
11+
import java.util.Map;
12+
import okhttp3.Interceptor;
13+
import okhttp3.MediaType;
14+
import okhttp3.Protocol;
15+
import okhttp3.Response;
16+
import okhttp3.ResponseBody;
17+
import org.openjdk.jmh.annotations.Benchmark;
18+
import org.openjdk.jmh.annotations.Param;
19+
import org.openjdk.jmh.annotations.Scope;
20+
import org.openjdk.jmh.annotations.Setup;
21+
import org.openjdk.jmh.annotations.State;
22+
import org.openjdk.jmh.annotations.TearDown;
23+
24+
/**
25+
* Benchmarks a locally evaluating SDK client against environment documents tiered by size.
26+
*/
27+
@State(Scope.Benchmark)
28+
public class SdkBenchmark {
29+
private static final MediaType JSON = MediaType.get("application/json");
30+
private static final String IDENTITY_OVERRIDES_IDENTIFIER = "identity-override-1";
31+
private static final String IDENTIFIER = "user-1";
32+
private static final Map<String, Object> TRAITS = Map.of(
33+
"plan", "plan-7",
34+
"age", 30,
35+
"country", "GB",
36+
"version", "2.5.1",
37+
"email", "jane@example.com",
38+
"user_id", 12,
39+
"beta", true,
40+
"score", 95,
41+
"tier", "pro",
42+
"device", "iPhone 15");
43+
44+
@Param({"small", "medium", "large"})
45+
public String environment;
46+
47+
private FlagsmithClient client;
48+
49+
/**
50+
* Build a locally evaluating client serving the environment document from memory.
51+
*/
52+
@Setup
53+
public void buildClient() throws IOException, FlagsmithClientError {
54+
byte[] environmentDocument;
55+
try (InputStream stream = getClass().getResourceAsStream(
56+
"/environments/" + environment + ".json")) {
57+
environmentDocument = stream.readAllBytes();
58+
}
59+
60+
client = FlagsmithClient.newBuilder()
61+
.setApiKey("ser.benchmark")
62+
.withConfiguration(FlagsmithConfig.newBuilder()
63+
.baseUri("http://flagsmith.benchmark/api/v1/")
64+
.addHttpInterceptor(serveEnvironmentDocument(environmentDocument))
65+
.withLocalEvaluation(true)
66+
.withEnvironmentRefreshIntervalSeconds(86400) // Refreshed by benchmarks only
67+
.build())
68+
.build();
69+
70+
int segmentCount = MapperFactory.getMapper()
71+
.readValue(environmentDocument, EnvironmentModel.class)
72+
.getProject().getSegments().size();
73+
verifyScenarios(segmentCount);
74+
}
75+
76+
@TearDown
77+
public void closeClient() {
78+
client.close();
79+
}
80+
81+
/**
82+
* Fetch, parse and map the environment document, replacing the client's cached copy.
83+
*/
84+
@Benchmark
85+
public EvaluationContext updateEnvironment() {
86+
client.updateEnvironment();
87+
return client.getEvaluationContext();
88+
}
89+
90+
/**
91+
* Get a feature value from the environment flags.
92+
*/
93+
@Benchmark
94+
public Object getEnvironmentFlags() throws FlagsmithClientError {
95+
return client.getEnvironmentFlags().getFeatureValue("feature_1");
96+
}
97+
98+
/**
99+
* Check a feature for an identity with identity overrides.
100+
*/
101+
@Benchmark
102+
public boolean getIdentityFlagsWithIdentityOverrides() throws FlagsmithClientError {
103+
return client.getIdentityFlags(IDENTITY_OVERRIDES_IDENTIFIER).isFeatureEnabled("feature_8");
104+
}
105+
106+
/**
107+
* Get a feature value for an identity without traits.
108+
*/
109+
@Benchmark
110+
public Object getIdentityFlagsNoTraits() throws FlagsmithClientError {
111+
return client.getIdentityFlags(IDENTIFIER).getFeatureValue("feature_1");
112+
}
113+
114+
/**
115+
* Get a feature value for an identity with traits.
116+
*/
117+
@Benchmark
118+
public Object getIdentityFlagsWithTraits() throws FlagsmithClientError {
119+
return client.getIdentityFlags(IDENTIFIER, TRAITS).getFeatureValue("feature_1");
120+
}
121+
122+
/**
123+
* List the segments of an identity with traits.
124+
*/
125+
@Benchmark
126+
public List<Segment> getIdentitySegmentsWithTraits() throws FlagsmithClientError {
127+
return client.getIdentitySegments(IDENTIFIER, TRAITS);
128+
}
129+
130+
private void verifyScenarios(int segmentCount) throws FlagsmithClientError {
131+
Object overriddenValue = client.getIdentityFlags(IDENTITY_OVERRIDES_IDENTIFIER)
132+
.getFeatureValue("feature_8");
133+
if (!"identity-1".equals(overriddenValue)) {
134+
throw new IllegalStateException("Identity overrides not applied: " + overriddenValue);
135+
}
136+
137+
int noTraitsSegments = client.getIdentitySegments(IDENTIFIER).size();
138+
int withTraitsSegments = client.getIdentitySegments(IDENTIFIER, TRAITS).size();
139+
if (withTraitsSegments < segmentCount / 2 || noTraitsSegments >= withTraitsSegments) {
140+
throw new IllegalStateException("Identity in " + noTraitsSegments + " segments without traits"
141+
+ " and " + withTraitsSegments + " with traits, of " + segmentCount);
142+
}
143+
}
144+
145+
private static Interceptor serveEnvironmentDocument(byte[] environmentDocument) {
146+
return chain -> new Response.Builder()
147+
.request(chain.request())
148+
.protocol(Protocol.HTTP_1_1)
149+
.code(200)
150+
.message("OK")
151+
.body(ResponseBody.create(environmentDocument, JSON))
152+
.build();
153+
}
154+
}

‎src/jmh/java/com/flagsmith/flagengine/EngineBenchmark.java‎

Lines changed: 0 additions & 67 deletions
This file was deleted.

0 commit comments

Comments
 (0)