Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace caffeine cache with reference to full map #2214

Merged
merged 45 commits into from
Aug 6, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
3b66cc1
Split up caffeine cache
Jun 24, 2021
72348b6
Fix unit tests
Jun 24, 2021
bc29c9c
Update cache to LoadingCache
Jun 24, 2021
652d42a
Use isEnabled
Jun 24, 2021
df17bbe
Clean up test module
Jun 24, 2021
8cb4138
Clean up more imports
Jun 24, 2021
a020b92
Update ttls and deploy states call
Jun 25, 2021
7a77ce2
Rename config values
Jun 25, 2021
80bb901
Missed a value
Jun 25, 2021
1e95103
update logging
Jun 25, 2021
7b1ca90
rename to ApiCache
Jun 29, 2021
e8af8b1
Update deploy api cache load
Jun 30, 2021
ee556cd
Split up cache enabling config values
Jun 30, 2021
50e1dee
Add debug for cache loading
Jun 30, 2021
69ebebc
Use a set as key
Jul 1, 2021
f7174d3
Use cache loader for async reloading
Jul 16, 2021
488b35c
copypasta error config value
Jul 16, 2021
dd24913
Use AtomiceReference map instead of LoadingCache
Jul 23, 2021
5947b22
Remove caffeine dep
Jul 23, 2021
6a0732b
update zk call
Jul 26, 2021
9373f5e
another log line and return typo
Jul 26, 2021
1517b59
More debug lines
Jul 26, 2021
ebaa6ed
Add try/catch
Jul 26, 2021
b2a1f25
Removed a heavy debug line
Jul 26, 2021
e3e3ea2
Be more selective with debug info
Jul 27, 2021
f2a2e22
Trying to get around first run exception
Jul 27, 2021
5b2069b
Update timing for scheduled reloader
Jul 27, 2021
bfbcf36
Reloader keeps starting before curator framework
Jul 27, 2021
642ebe1
Single threaded executor, lifecycle manage reloading
Jul 28, 2021
c23fcaf
Add managers to test Lifecycle
Jul 28, 2021
76aaa10
Load map ref when started
Jul 28, 2021
14a9ab9
use request cache for other calls
Jul 28, 2021
9e7555b
Update caching to 5 seconds
Jul 29, 2021
efcd68b
Add skipCache flag
Jul 29, 2021
5b97f8b
Add flag to deploy manager side
Jul 30, 2021
254d6e4
make skipApiCache a setter and deprecate
Jul 30, 2021
12d6162
More debug and fallback to ZK
Jul 30, 2021
862c22e
Finesse logging
Jul 30, 2021
3514f4b
ApiCache disabled for leader & missed params
Aug 3, 2021
9cafb6c
Try implement leader listener
Aug 3, 2021
2223fdb
Logging around starting and stopping reloader on leader update
Aug 4, 2021
9cdd205
give ApiCache the leaderlatch instead
Aug 4, 2021
73875d6
debug logs for leader clean up
Aug 4, 2021
6f2849b
More debug logs
Aug 4, 2021
e7bc9d9
Make all get* logs trace
Aug 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package com.hubspot.singularity;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.base.Function;
import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.hubspot.dropwizard.guicier.DropwizardAwareModule;
import com.hubspot.mesos.client.SingularityMesosClientModule;
import com.hubspot.mesos.client.UserAndPassword;
Expand All @@ -29,14 +26,10 @@
import com.hubspot.singularity.resources.SingularityOpenApiResource;
import com.hubspot.singularity.resources.SingularityResourceModule;
import com.hubspot.singularity.scheduler.SingularitySchedulerModule;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

public class SingularityServiceModule
extends DropwizardAwareModule<SingularityConfiguration> {
public static final String REQUESTS_CAFFEINE_CACHE =
"singularity.service.resources.request";
private final Function<SingularityConfiguration, Module> dbModuleProvider;
private Optional<Class<? extends LoadBalancerClient>> lbClientClass = Optional.empty();

Expand Down Expand Up @@ -135,16 +128,4 @@ public IndexViewConfiguration provideIndexViewConfiguration(
.contains(SingularityAuthenticatorClass.WEBHOOK)
);
}

@Provides
@Singleton
@Named(REQUESTS_CAFFEINE_CACHE)
public Cache<String, List<SingularityRequestParent>> getRequestsCaffeineCache() {
SingularityConfiguration configuration = getConfiguration();

return Caffeine
.newBuilder()
.expireAfterWrite(configuration.getCaffeineCacheTtl(), TimeUnit.SECONDS)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,9 @@ public class SingularityConfiguration extends Configuration {
// Enable caffeine cache on heavily requested endpoint
private boolean useCaffeineCache = false;

// Caffeine cache ttl
private int caffeineCacheTtl = 1;
// Caffeine cache TTLs
private int deployCaffeineCacheTtl = 1;
private int requestCaffeineCacheTtl = 1;

public long getAskDriverToKillTasksAgainAfterMillis() {
return askDriverToKillTasksAgainAfterMillis;
Expand Down Expand Up @@ -2084,11 +2085,19 @@ public void setUseCaffeineCache(boolean useCaffeineCache) {
this.useCaffeineCache = useCaffeineCache;
}

public int getCaffeineCacheTtl() {
return caffeineCacheTtl;
public int getDeployCaffeineCacheTtl() {
return deployCaffeineCacheTtl;
}

public void setCaffeineCacheTtl(int caffeineCacheTtl) {
this.caffeineCacheTtl = caffeineCacheTtl;
public void setDeployCaffeineCacheTtl(int deployCaffeineCacheTtl) {
Copy link
Contributor

@pschoenfelder pschoenfelder Jun 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit — can we remove caffeine from the config terminology here? Users don't need to be aware of cache choice/impl, and we could change caches in the future if we wanted to without any awkwardness

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also updated the useCaffeineCache to useZKFastCache

this.deployCaffeineCacheTtl = deployCaffeineCacheTtl;
}

public int getRequestCaffeineCacheTtl() {
return requestCaffeineCacheTtl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last nit — can we append the time unit to this so users don't have to search code for it?

}

public void setRequestCaffeineCacheTtl(int requestCaffeineCacheTtl) {
this.requestCaffeineCacheTtl = requestCaffeineCacheTtl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.hubspot.singularity.event.SingularityEventListener;
import com.hubspot.singularity.scheduler.SingularityLeaderCache;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -70,6 +71,8 @@ public class DeployManager extends CuratorAsyncManager {
private static final String DEPLOY_STATISTICS_KEY = "STATISTICS";
private static final String DEPLOY_RESULT_KEY = "RESULT_STATE";

private final ManagerCache<String, SingularityRequestDeployState> deployCache;

@Inject
public DeployManager(
CuratorFramework curator,
Expand Down Expand Up @@ -99,6 +102,25 @@ public DeployManager(
this.updateRequestTranscoder = updateRequestTranscoder;
this.deploysCache = deploysCache;
this.leaderCache = leaderCache;
this.deployCache =
new ManagerCache<>(
configuration.useCaffeineCache(),
configuration.getDeployCaffeineCacheTtl(),
requestId -> {
List<SingularityRequestDeployState> deployStates = getAsync(
"getRequestDeployStatesByRequestIds",
Collections.singletonList(getRequestDeployStatePath(requestId)),
requestDeployStateTranscoder
);

SingularityRequestDeployState deployState = null;
if (!deployStates.isEmpty()) {
deployState = deployStates.get(0);
}

return deployState;
}
);
}

public List<SingularityDeployKey> getDeployIdsFor(String requestId) {
Expand All @@ -124,6 +146,15 @@ public Map<String, SingularityRequestDeployState> getRequestDeployStatesByReques
return leaderCache.getRequestDeployStateByRequestId(requestIds);
}

Map<String, SingularityRequestDeployState> deployStatesByRequestIds;

if (deployCache.isEnabled()) {
deployStatesByRequestIds = deployCache.getAll(requestIds);
if (!deployStatesByRequestIds.isEmpty()) {
return deployStatesByRequestIds;
}
}

return fetchDeployStatesByRequestIds(requestIds);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.hubspot.singularity.data;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.inject.Inject;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ManagerCache<K, V> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the ZkCache class. It's basically the same thing as this and might be better suited (at least for the deployManager part of things)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ZkCache just uses a Guava cache and we were hoping to get benefits from the LoadingCache created by CaffeineCache for debouncing requests because we were also getting timeouts at the DeployManager's request, do you still think that I should update the DeployManager's usage?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, thought the Zk one was more similar. Can keep the separate one then

private static final Logger LOG = LoggerFactory.getLogger(ManagerCache.class);

public final boolean isEnabled;
private final LoadingCache<K, V> cache;

@Inject
public ManagerCache(boolean isEnabled, int cacheTtl, Function<? super K, V> loader) {
this.isEnabled = isEnabled;
cache =
Caffeine
.newBuilder()
.expireAfterWrite(cacheTtl, TimeUnit.SECONDS)
.build(loader::apply);
}

public V get(K key) {
V values = cache.get(key);
if (values != null) {
LOG.trace("Grabbed values for {} from cache", key);
} else {
LOG.trace("{} not in cache, setting", key);
}

return values;
}

public Map<K, V> getAll(@Nonnull Iterable<? extends K> keys) {
Map<K, V> values = cache.getAll(keys);
if (!values.isEmpty()) {
LOG.trace("Grabbed mapped values for {} from cache", keys);
}

return values;
}

public boolean isEnabled() {
return isEnabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public class RequestManager extends CuratorAsyncManager {
);

private final Map<Class<? extends SingularityExpiringRequestActionParent<? extends SingularityExpiringRequestParent>>, Transcoder<? extends SingularityExpiringRequestActionParent<? extends SingularityExpiringRequestParent>>> expiringTranscoderMap;
private final ManagerCache<String, List<SingularityRequestWithState>> requestsCache;

@Inject
public RequestManager(
Expand Down Expand Up @@ -133,6 +134,12 @@ public RequestManager(

this.leaderCache = leaderCache;
this.webCache = webCache;
this.requestsCache =
new ManagerCache<>(
configuration.useCaffeineCache(),
configuration.getRequestCaffeineCacheTtl(),
key -> this.fetchRequests()
);
}

private String getRequestPath(String requestId) {
Expand Down Expand Up @@ -632,11 +639,20 @@ public List<SingularityRequestWithState> getRequests(boolean useWebCache) {
if (useWebCache && webCache.useCachedRequests()) {
return webCache.getRequests();
}

if (requestsCache.isEnabled()) {
List<SingularityRequestWithState> requests = requestsCache.get("all");
if (requests != null) {
return requests;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should just not use the web cache here if this is the case. Seems overkill to have the leaderCache -> web cache -> zk cache all here. Lots of duplicate memory usage to store it all 3 times

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, to have ease in revert, the removal of web cache will be in a separate PR


List<SingularityRequestWithState> requests = fetchRequests();

if (useWebCache) {
webCache.cacheRequests(requests);
}

return requests;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
import static com.hubspot.singularity.WebExceptions.checkNotNullBadRequest;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.hubspot.jackson.jaxrs.PropertyFiltering;
import com.hubspot.singularity.AgentPlacement;
import com.hubspot.singularity.CrashLoopInfo;
Expand All @@ -35,7 +33,6 @@
import com.hubspot.singularity.SingularityRequestHistory.RequestHistoryType;
import com.hubspot.singularity.SingularityRequestParent;
import com.hubspot.singularity.SingularityRequestWithState;
import com.hubspot.singularity.SingularityServiceModule;
import com.hubspot.singularity.SingularityShellCommand;
import com.hubspot.singularity.SingularityTaskCleanup;
import com.hubspot.singularity.SingularityTaskHealthcheckResult;
Expand Down Expand Up @@ -89,7 +86,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
Expand Down Expand Up @@ -124,7 +120,6 @@ public class RequestResource extends AbstractRequestResource {
private final SingularityConfiguration configuration;
private final SingularityExceptionNotifier exceptionNotifier;
private final SingularityAgentAndRackManager agentAndRackManager;
private final Cache<String, List<SingularityRequestParent>> requestsCache;

@Inject
public RequestResource(
Expand All @@ -142,10 +137,7 @@ public RequestResource(
@Singularity ObjectMapper objectMapper,
SingularityConfiguration configuration,
SingularityExceptionNotifier exceptionNotifier,
SingularityAgentAndRackManager agentAndRackManager,
@Named(
SingularityServiceModule.REQUESTS_CAFFEINE_CACHE
) Cache<String, List<SingularityRequestParent>> requestsCache
SingularityAgentAndRackManager agentAndRackManager
) {
super(
requestManager,
Expand All @@ -165,7 +157,6 @@ public RequestResource(
this.configuration = configuration;
this.exceptionNotifier = exceptionNotifier;
this.agentAndRackManager = agentAndRackManager;
this.requestsCache = requestsCache;
}

private void submitRequest(
Expand Down Expand Up @@ -1472,24 +1463,7 @@ public List<SingularityRequestParent> getRequests(
"requestType"
) List<RequestType> requestTypes
) {
String key = getRequestCacheKey(
user.getId(),
filterRelevantForUser,
includeFullRequestData,
limit,
requestTypes
);
if (!useWebCache(useWebCache) && configuration.useCaffeineCache()) {
List<SingularityRequestParent> cachedRequests = requestsCache.getIfPresent(key);

if (cachedRequests != null) {
LOG.trace("Grabbed getRequests value for {} from cache", key);

return cachedRequests;
}
}

List<SingularityRequestParent> requests = requestHelper.fillDataForRequestsAndFilter(
return requestHelper.fillDataForRequestsAndFilter(
filterAutorized(
requestManager.getRequests(useWebCache(useWebCache)),
SingularityAuthorizationScope.READ,
Expand All @@ -1501,42 +1475,6 @@ public List<SingularityRequestParent> getRequests(
Optional.ofNullable(limit),
requestTypes
);

if (!useWebCache(useWebCache) && configuration.useCaffeineCache()) {
requestsCache.put(key, requests);

LOG.trace("Setting getRequests value for {} in cache", key);
}

return requests;
}

private String getRequestCacheKey(
String id,
Boolean filterRelevantForUser,
Boolean includeFullRequestData,
Integer limit,
List<RequestType> requestTypes
) {
StringBuilder key = new StringBuilder(id);

if (filterRelevantForUser != null) {
key.append("_filter_").append(filterRelevantForUser);
}
if (includeFullRequestData != null) {
key.append("_fullRequestData_").append(includeFullRequestData);
}
if (limit != null) {
key.append("_limit_").append(limit);
}
if (!requestTypes.isEmpty()) {
key.append("_types_");
for (RequestType type : requestTypes) {
key.append("_").append(type.name());
}
}

return key.toString();
}

private boolean valueOrFalse(Boolean input) {
Expand Down
Loading