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 38 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
Expand Up @@ -313,6 +313,8 @@ public class SingularityClient {

private final Retryer<HttpResponse> httpResponseRetryer;

private boolean skipApiCache = false;

@Inject
@Deprecated
public SingularityClient(
Expand Down Expand Up @@ -408,6 +410,11 @@ public SingularityClient(
.build();
}

@Deprecated
public void setSkipApiCache(boolean skipApiCache) {
this.skipApiCache = skipApiCache;
}
Comment on lines +413 to +416
Copy link
Member

Choose a reason for hiding this comment

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

for structure/usability of the client. Is there a reason you have this at the class level instead of just being an arg on the relevant method(s)?

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 looked through the request call usages in Orion and talked to Suruu and we decided it would be easier on Deploy's side to have a class level argument rather than updating all usages. It also makes clean up easier for when we've solved the underlying CuratorFramework issue


private String getApiBase(String host) {
return String.format(BASE_API_FORMAT, ssl ? "https" : "http", host, contextPath);
}
Expand Down Expand Up @@ -896,6 +903,16 @@ public Optional<SingularityRequestParent> getSingularityRequest(String requestId
final Function<String, String> singularityApiRequestUri = host ->
String.format(REQUEST_GET_FORMAT, getApiBase(host), requestId);

if (skipApiCache) {
return getSingleWithParams(
singularityApiRequestUri,
"request",
requestId,
Optional.of(ImmutableMap.of("skipCache", true)),
SingularityRequestParent.class
);
}

return getSingle(
singularityApiRequestUri,
"request",
Expand Down Expand Up @@ -1236,7 +1253,14 @@ public Collection<SingularityRequestParent> getSingularityRequests(
return getCollectionWithParams(
requestUri,
"[ACTIVE, PAUSED, COOLDOWN] requests",
Optional.of(ImmutableMap.of("includeFullRequestData", includeFullRequestData)),
Optional.of(
ImmutableMap.of(
"includeFullRequestData",
includeFullRequestData,
"skipCache",
skipApiCache
)
),
REQUESTS_COLLECTION
);
}
Expand Down
5 changes: 0 additions & 5 deletions SingularityService/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -510,11 +510,6 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,29 @@ public synchronized ScheduledExecutorService get(
return service;
}

public synchronized ScheduledExecutorService getSingleThreaded(String name) {
return getSingleThreaded(name, false);
}

public synchronized ScheduledExecutorService getSingleThreaded(
Copy link
Member

Choose a reason for hiding this comment

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

looks like this method is unused?

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 was following the pattern in the SingularityManagedThreadPoolFactory for getSingleThreaded but I'll update to not overload that method

Copy link
Member

Choose a reason for hiding this comment

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

🤦 just realzied the one above calls it. Nevermind didn't even see that

String name,
boolean isLeaderOnlyPoller
) {
checkState(!stopped.get(), "already stopped");

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(
new ThreadFactoryBuilder().setNameFormat(name + "-%d").setDaemon(true).build()
);

if (isLeaderOnlyPoller) {
leaderPollerPools.put(name, service);
} else {
executorPools.put(name, service);
}

return service;
}

public void stopLeaderPollers() throws Exception {
if (!leaderStopped.getAndSet(true)) {
long timeoutLeftInMillis = timeoutInMillis;
Expand Down
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 @@ -444,10 +444,12 @@ public class SingularityConfiguration extends Configuration {
private double statusQueueNearlyFull = 0.8;

// Enable caffeine cache on heavily requested endpoint
private boolean useCaffeineCache = false;
private boolean useApiCacheInRequestManager = false;
private boolean useApiCacheInDeployManager = false;

// Caffeine cache ttl
private int caffeineCacheTtl = 1;
// Atomic Reference cache TTLs
private int deployCacheTtlInSeconds = 5;
private int requestCacheTtlInSeconds = 5;

public long getAskDriverToKillTasksAgainAfterMillis() {
return askDriverToKillTasksAgainAfterMillis;
Expand Down Expand Up @@ -2076,19 +2078,35 @@ public void setStatusQueueNearlyFull(double statusQueueNearlyFull) {
this.statusQueueNearlyFull = statusQueueNearlyFull;
}

public boolean useCaffeineCache() {
return useCaffeineCache;
public boolean useApiCacheInRequestManager() {
return useApiCacheInRequestManager;
}

public void setUseCaffeineCache(boolean useCaffeineCache) {
this.useCaffeineCache = useCaffeineCache;
public void setUseApiCacheInRequestManager(boolean useApiCacheInRequestManager) {
this.useApiCacheInRequestManager = useApiCacheInRequestManager;
}

public int getCaffeineCacheTtl() {
return caffeineCacheTtl;
public boolean useApiCacheInDeployManager() {
return useApiCacheInDeployManager;
}

public void setCaffeineCacheTtl(int caffeineCacheTtl) {
this.caffeineCacheTtl = caffeineCacheTtl;
public void setUseApiCacheInDeployManager(boolean useApiCacheInDeployManager) {
this.useApiCacheInDeployManager = useApiCacheInDeployManager;
}

public int getDeployCacheTtl() {
return deployCacheTtlInSeconds;
}

public void setDeployCacheTtl(int deployCacheTtlInSeconds) {
this.deployCacheTtlInSeconds = deployCacheTtlInSeconds;
}

public int getRequestCacheTtl() {
return requestCacheTtlInSeconds;
}

public void setRequestCacheTtl(int requestCacheTtlInSeconds) {
this.requestCacheTtlInSeconds = requestCacheTtlInSeconds;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.hubspot.singularity.data;

import com.google.inject.Inject;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ApiCache<K, V> {
private static final Logger LOG = LoggerFactory.getLogger(ApiCache.class);

public final boolean isEnabled;
private final AtomicReference<Map<K, V>> zkValues;
private final Supplier<Map<K, V>> supplyMap;
private final int cacheTtl;
private final ScheduledExecutorService executor;

private ScheduledFuture<?> reloadingFuture;

@Inject
public ApiCache(
boolean isEnabled,
int cacheTtl,
Supplier<Map<K, V>> supplyMap,
ScheduledExecutorService executor
) {
this.isEnabled = isEnabled;
this.supplyMap = supplyMap;
this.zkValues = new AtomicReference<>(new HashMap<>());
this.cacheTtl = cacheTtl;
this.executor = executor;
}

public void startReloader() {
Copy link
Member

Choose a reason for hiding this comment

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

should this also synchronously perform the first fetch I wonder? Would avoid the case where getAll was incorrectly empty or get(K key) was null

if (isEnabled) {
reloadZkValues();
reloadingFuture =
executor.scheduleAtFixedRate(this::reloadZkValues, 0, cacheTtl, TimeUnit.SECONDS);
}
}

public void stopReloader() {
if (reloadingFuture != null) {
reloadingFuture.cancel(true);
}
}

private void reloadZkValues() {
try {
Map<K, V> newZkValues = supplyMap.get();
if (!newZkValues.isEmpty()) {
zkValues.set(newZkValues);
} else {
LOG.warn("Empty values on cache reload, keeping old values");
}
} catch (Exception e) {
LOG.warn("Reloading ApiCache failed: {}", e.getMessage());
}
}

public V get(K key) {
V value = this.zkValues.get().get(key);

if (value == null) {
LOG.debug("ApiCache returned null for {}", key);
}

return value;
}

public Map<K, V> getAll() {
Map<K, V> allValues = this.zkValues.get();
if (allValues.isEmpty()) {
LOG.debug("ApiCache getAll returned empty");
} else {
LOG.debug("getAll returned {} values", allValues.size());
Copy link
Member

Choose a reason for hiding this comment

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

for this and the debug statement above, maybe these are more like TRACE level lines? Would get pretty noisy given that we could acall these multiple times a second

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 made all of the get calls' logs trace level.

}
return allValues;
}

public Map<K, V> getAll(Collection<K> keys) {
Map<K, V> allValues = this.zkValues.get();
Map<K, V> filteredValues = keys
.stream()
.filter(allValues::containsKey)
.collect(Collectors.toMap(Function.identity(), allValues::get));

if (filteredValues.isEmpty()) {
LOG.debug("ApiCache getAll returned empty for {}", keys);
} else {
LOG.debug(
"getAll returned {} for {} amount requested",
filteredValues.size(),
keys.size()
);
}

return filteredValues;
}

public boolean isEnabled() {
return isEnabled;
}
}
Loading