Skip to content

Commit 706c1cd

Browse files
committed
perf: fix performance regression with cache since we added getConfigResult, as we were caching the GResultOf<Object>. This was causing extra calls to get the result after getting data from the cache. We now have a seperate cache for the normal flow and one for GResultOf<Object>.
1 parent ea3eb75 commit 706c1cd

File tree

2 files changed

+102
-29
lines changed

2 files changed

+102
-29
lines changed

gestalt-core/src/main/java/org/github/gestalt/config/GestaltCache.java

Lines changed: 101 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
@SuppressWarnings("OverloadMethodsDeclarationOrder")
2727
public class GestaltCache implements Gestalt, CoreReloadListener {
2828
private final Gestalt delegate;
29-
private final Map<Triple<String, TypeCapture<?>, Tags>, GResultOf<Object>> cache = Collections.synchronizedMap(new HashMap<>());
29+
private final Map<Triple<String, TypeCapture<?>, Tags>, Object> cache = Collections.synchronizedMap(new HashMap<>());
30+
private final Map<Triple<String, TypeCapture<?>, Tags>, GResultOf<Object>> cacheResultsOf = Collections.synchronizedMap(new HashMap<>());
3031
private final Tags defaultTags;
3132
private final ObservationService observationService;
3233
private final GestaltConfig gestaltConfig;
@@ -66,7 +67,7 @@ public <T> T getConfig(String path, Class<T> klass) throws GestaltException {
6667
Objects.requireNonNull(klass);
6768

6869
TypeCapture<T> typeCapture = TypeCapture.of(klass);
69-
return getConfigInternal(path, typeCapture, null).results();
70+
return getConfigInternal(path, typeCapture, null);
7071
}
7172

7273
@Override
@@ -76,15 +77,15 @@ public <T> T getConfig(String path, Class<T> klass, Tags tags) throws GestaltExc
7677
Objects.requireNonNull(tags);
7778

7879
TypeCapture<T> typeCapture = TypeCapture.of(klass);
79-
return getConfigInternal(path, typeCapture, tags).results();
80+
return getConfigInternal(path, typeCapture, tags);
8081
}
8182

8283
@Override
8384
public <T> T getConfig(String path, TypeCapture<T> klass) throws GestaltException {
8485
Objects.requireNonNull(path);
8586
Objects.requireNonNull(klass);
8687

87-
return getConfigInternal(path, klass, null).results();
88+
return getConfigInternal(path, klass, null);
8889
}
8990

9091
@Override
@@ -93,7 +94,7 @@ public <T> T getConfig(String path, TypeCapture<T> klass, Tags tags) throws Gest
9394
Objects.requireNonNull(klass);
9495
Objects.requireNonNull(tags);
9596

96-
return getConfigInternal(path, klass, tags).results();
97+
return getConfigInternal(path, klass, tags);
9798
}
9899

99100
@Override
@@ -102,30 +103,54 @@ public <T> GResultOf<T> getConfigResult(String path, TypeCapture<T> klass, Tags
102103
Objects.requireNonNull(klass);
103104
Objects.requireNonNull(tags);
104105

105-
return getConfigInternal(path, klass, tags);
106+
return getConfigInternalResult(path, klass, tags);
106107
}
107108

108109
@SuppressWarnings("unchecked")
109-
private <T> GResultOf<T> getConfigInternal(String path, TypeCapture<T> klass, Tags tags) throws GestaltException {
110+
private <T> T getConfigInternal(String path, TypeCapture<T> klass, Tags tags) throws GestaltException {
110111

111112
Tags resolvedTags = tagMergingStrategy.mergeTags(tags, defaultTags);
112113
Triple<String, TypeCapture<?>, Tags> key = new Triple<>(path, klass, resolvedTags);
113-
if (cache.get(key) != null && cache.get(key).hasResults()) {
114+
if (cache.get(key) != null) {
114115
if (gestaltConfig.isObservationsEnabled() && observationService != null) {
115116
observationService.recordObservation("cache.hit", 1, Tags.of());
116117
}
117-
return (GResultOf<T>) cache.get(key);
118+
return (T) cache.get(key);
118119
} else {
119120
GResultOf<T> result = delegate.getConfigResult(path, klass, resolvedTags);
120121
updateCache(path, key, result);
122+
return result != null ? result.results(): null;
123+
}
124+
}
125+
126+
@SuppressWarnings("unchecked")
127+
private <T> GResultOf<T> getConfigInternalResult(String path, TypeCapture<T> klass, Tags tags) throws GestaltException {
128+
129+
Tags resolvedTags = tagMergingStrategy.mergeTags(tags, defaultTags);
130+
Triple<String, TypeCapture<?>, Tags> key = new Triple<>(path, klass, resolvedTags);
131+
if (cacheResultsOf.get(key) != null && cacheResultsOf.get(key).hasResults()) {
132+
if (gestaltConfig.isObservationsEnabled() && observationService != null) {
133+
observationService.recordObservation("cache.hit", 1, Tags.of());
134+
}
135+
return (GResultOf<T>) cacheResultsOf.get(key);
136+
} else {
137+
GResultOf<T> result = delegate.getConfigResult(path, klass, resolvedTags);
138+
updateCacheResults(path, key, result);
121139
return result;
122140
}
123141
}
124142

125143
@SuppressWarnings("unchecked")
126144
private <T> void updateCache(String path, Triple<String, TypeCapture<?>, Tags> key, GResultOf<T> result) {
127145
if (shouldCacheValue(path, result != null ? result.getMetadata() : Map.of())) {
128-
cache.put(key, (GResultOf<Object>) result);
146+
cache.put(key, result != null ? result.results() : null);
147+
}
148+
}
149+
150+
@SuppressWarnings("unchecked")
151+
private <T> void updateCacheResults(String path, Triple<String, TypeCapture<?>, Tags> key, GResultOf<T> result) {
152+
if (shouldCacheValue(path, result != null ? result.getMetadata() : Map.of())) {
153+
cacheResultsOf.put(key, (GResultOf<Object>) result);
129154
}
130155
}
131156

@@ -135,7 +160,7 @@ public <T> T getConfig(String path, T defaultVal, Class<T> klass) {
135160
Objects.requireNonNull(klass);
136161

137162
TypeCapture<T> typeCapture = TypeCapture.of(klass);
138-
return getConfigInternal(path, defaultVal, typeCapture, null).results();
163+
return getConfigInternal(path, defaultVal, typeCapture, null);
139164
}
140165

141166
@Override
@@ -145,7 +170,7 @@ public <T> T getConfig(String path, T defaultVal, Class<T> klass, Tags tags) {
145170
Objects.requireNonNull(tags);
146171

147172
TypeCapture<T> typeCapture = TypeCapture.of(klass);
148-
return getConfigInternal(path, defaultVal, typeCapture, tags).results();
173+
return getConfigInternal(path, defaultVal, typeCapture, tags);
149174
}
150175

151176
@Override
@@ -154,7 +179,7 @@ public <T> GResultOf<T> getConfigResult(String path, T defaultVal, TypeCapture<T
154179
Objects.requireNonNull(klass);
155180
Objects.requireNonNull(tags);
156181

157-
return getConfigInternal(path, defaultVal, klass, tags);
182+
return getConfigInternalResult(path, defaultVal, klass, tags);
158183
}
159184

160185

@@ -164,7 +189,7 @@ public <T> T getConfig(String path, T defaultVal, TypeCapture<T> klass) {
164189
Objects.requireNonNull(path);
165190
Objects.requireNonNull(klass);
166191

167-
return getConfigInternal(path, defaultVal, klass, null).results();
192+
return getConfigInternal(path, defaultVal, klass, null);
168193
}
169194

170195
@Override
@@ -173,15 +198,44 @@ public <T> T getConfig(String path, T defaultVal, TypeCapture<T> klass, Tags tag
173198
Objects.requireNonNull(klass);
174199
Objects.requireNonNull(tags);
175200

176-
return getConfigInternal(path, defaultVal, klass, tags).results();
201+
return getConfigInternal(path, defaultVal, klass, tags);
177202
}
178203

179204
@SuppressWarnings("unchecked")
180-
private <T> GResultOf<T> getConfigInternal(String path, T defaultVal, TypeCapture<T> klass, Tags tags) {
205+
private <T> T getConfigInternal(String path, T defaultVal, TypeCapture<T> klass, Tags tags) {
181206
Tags resolvedTags = tagMergingStrategy.mergeTags(tags, defaultTags);
182207
Triple<String, TypeCapture<?>, Tags> key = new Triple<>(path, klass, resolvedTags);
183208
if (cache.containsKey(key)) {
184-
GResultOf<T> result = (GResultOf<T>) cache.get(key);
209+
T result = (T) cache.get(key);
210+
if (result == null) {
211+
result = defaultVal;
212+
}
213+
214+
if (gestaltConfig.isObservationsEnabled() && observationService != null) {
215+
observationService.recordObservation("cache.hit", 1, Tags.of());
216+
}
217+
218+
return result;
219+
220+
} else {
221+
Optional<GResultOf<T>> result = delegate.getConfigOptionalResult(path, klass, resolvedTags);
222+
223+
updateCache(path, key, result.orElse(null));
224+
225+
if (result.isPresent() && result.get().hasResults()) {
226+
return result.get().results();
227+
} else {
228+
return defaultVal;
229+
}
230+
}
231+
}
232+
233+
@SuppressWarnings("unchecked")
234+
private <T> GResultOf<T> getConfigInternalResult(String path, T defaultVal, TypeCapture<T> klass, Tags tags) {
235+
Tags resolvedTags = tagMergingStrategy.mergeTags(tags, defaultTags);
236+
Triple<String, TypeCapture<?>, Tags> key = new Triple<>(path, klass, resolvedTags);
237+
if (cacheResultsOf.containsKey(key)) {
238+
GResultOf<T> result = (GResultOf<T>) cacheResultsOf.get(key);
185239
if (result == null || !result.hasResults()) {
186240
result = GResultOf.result(defaultVal, true);
187241
}
@@ -195,7 +249,7 @@ private <T> GResultOf<T> getConfigInternal(String path, T defaultVal, TypeCaptur
195249
} else {
196250
Optional<GResultOf<T>> result = delegate.getConfigOptionalResult(path, klass, resolvedTags);
197251

198-
updateCache(path, key, result.orElse(null));
252+
updateCacheResults(path, key, result.orElse(null));
199253

200254
if (result.isPresent() && result.get().hasResults()) {
201255
return result.get();
@@ -211,7 +265,7 @@ public <T> Optional<T> getConfigOptional(String path, Class<T> klass) {
211265
Objects.requireNonNull(klass);
212266

213267
TypeCapture<T> typeCapture = TypeCapture.of(klass);
214-
return getConfigOptionalInternal(path, typeCapture, null).map(GResultOf::results);
268+
return getConfigOptionalInternal(path, typeCapture, null);
215269
}
216270

217271
@Override
@@ -221,15 +275,15 @@ public <T> Optional<T> getConfigOptional(String path, Class<T> klass, Tags tags)
221275
Objects.requireNonNull(tags);
222276

223277
TypeCapture<T> typeCapture = TypeCapture.of(klass);
224-
return getConfigOptionalInternal(path, typeCapture, tags).map(GResultOf::results);
278+
return getConfigOptionalInternal(path, typeCapture, tags);
225279
}
226280

227281
@Override
228282
public <T> Optional<T> getConfigOptional(String path, TypeCapture<T> klass) {
229283
Objects.requireNonNull(path);
230284
Objects.requireNonNull(klass);
231285

232-
return getConfigOptionalInternal(path, klass, null).map(GResultOf::results);
286+
return getConfigOptionalInternal(path, klass, null);
233287
}
234288

235289
@Override
@@ -238,7 +292,7 @@ public <T> Optional<T> getConfigOptional(String path, TypeCapture<T> klass, Tags
238292
Objects.requireNonNull(klass);
239293
Objects.requireNonNull(tags);
240294

241-
return getConfigOptionalInternal(path, klass, tags).map(GResultOf::results);
295+
return getConfigOptionalInternal(path, klass, tags);
242296
}
243297

244298
@Override
@@ -247,11 +301,11 @@ public <T> Optional<GResultOf<T>> getConfigOptionalResult(String path, TypeCaptu
247301
Objects.requireNonNull(klass);
248302
Objects.requireNonNull(tags);
249303

250-
return getConfigOptionalInternal(path, klass, tags);
304+
return getConfigOptionalInternalResult(path, klass, tags);
251305
}
252306

253307
@SuppressWarnings("unchecked")
254-
public <T> Optional<GResultOf<T>> getConfigOptionalInternal(String path, TypeCapture<T> klass, Tags tags) {
308+
public <T> Optional<T> getConfigOptionalInternal(String path, TypeCapture<T> klass, Tags tags) {
255309

256310
Tags resolvedTags = tagMergingStrategy.mergeTags(tags, defaultTags);
257311
Triple<String, TypeCapture<?>, Tags> key = new Triple<>(path, klass, resolvedTags);
@@ -260,12 +314,31 @@ public <T> Optional<GResultOf<T>> getConfigOptionalInternal(String path, TypeCap
260314
observationService.recordObservation("cache.hit", 1, Tags.of());
261315
}
262316

263-
GResultOf<T> result = (GResultOf<T>) cache.get(key);
317+
T result = (T) cache.get(key);
264318
return Optional.ofNullable(result);
265319
} else {
266320
Optional<GResultOf<T>> resultOptional = delegate.getConfigOptionalResult(path, klass, resolvedTags);
267321
GResultOf<T> result = resultOptional.orElse(null);
268322
updateCache(path, key, result);
323+
return Optional.ofNullable(result != null ? result.results() : null);
324+
}
325+
}
326+
327+
public <T> Optional<GResultOf<T>> getConfigOptionalInternalResult(String path, TypeCapture<T> klass, Tags tags) {
328+
329+
Tags resolvedTags = tagMergingStrategy.mergeTags(tags, defaultTags);
330+
Triple<String, TypeCapture<?>, Tags> key = new Triple<>(path, klass, resolvedTags);
331+
if (cacheResultsOf.containsKey(key)) {
332+
if (gestaltConfig.isObservationsEnabled() && observationService != null) {
333+
observationService.recordObservation("cache.hit", 1, Tags.of());
334+
}
335+
336+
GResultOf<T> result = (GResultOf<T>) cacheResultsOf.get(key);
337+
return Optional.ofNullable(result);
338+
} else {
339+
Optional<GResultOf<T>> resultOptional = delegate.getConfigOptionalResult(path, klass, resolvedTags);
340+
GResultOf<T> result = resultOptional.orElse(null);
341+
updateCacheResults(path, key, result);
269342
return Optional.ofNullable(result);
270343
}
271344
}
@@ -274,9 +347,9 @@ private boolean shouldCacheValue(String path, Map<String, List<MetaDataValue<?>>
274347
boolean notIsSecret = nonCacheableSecrets.stream().noneMatch(it -> it.isSecret(path));
275348
boolean noCacheMetadata = metadata.containsKey(IsNoCacheMetadata.NO_CACHE) &&
276349
metadata.get(IsNoCacheMetadata.NO_CACHE).stream()
277-
.map(it -> it instanceof IsNoCacheMetadata && ((IsNoCacheMetadata) it).getMetadata())
278-
.filter(it -> it)
279-
.findFirst().orElse(false);
350+
.map(it -> it instanceof IsNoCacheMetadata && ((IsNoCacheMetadata) it).getMetadata())
351+
.filter(it -> it)
352+
.findFirst().orElse(false);
280353

281354
return notIsSecret && !noCacheMetadata;
282355
}

gestalt-core/src/test/java/org/github/gestalt/config/GestaltCacheTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ void getConfig() throws GestaltException {
7575
Assertions.assertEquals(100, port6.results());
7676
Assertions.assertEquals(100, port7.get().results());
7777

78-
Mockito.verify(mockGestalt, Mockito.times(1)).getConfigResult("db.port", TypeCapture.of(Integer.class), Tags.of());
78+
Mockito.verify(mockGestalt, Mockito.times(2)).getConfigResult("db.port", TypeCapture.of(Integer.class), Tags.of());
7979
}
8080

8181
@Test

0 commit comments

Comments
 (0)