Skip to content

Commit

Permalink
Core: Add jitter for cache (#3135)
Browse files Browse the repository at this point in the history
  • Loading branch information
CTMBNara authored May 2, 2024
1 parent ec2670f commit 630067d
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 22 deletions.
1 change: 1 addition & 0 deletions docs/config-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ See [application settings](application-settings.md) for full reference of availa
For caching available next options:
- `settings.in-memory-cache.ttl-seconds` - how long (in seconds) data will be available in LRU cache.
- `settings.in-memory-cache.cache-size` - the size of LRU cache.
- `settings.in-memory-cache.jitter-seconds` - jitter (in seconds) for `settings.in-memory-cache.ttl-seconds` parameter.
- `settings.in-memory-cache.notification-endpoints-enabled` - if equals to `true` two additional endpoints will be
available: [/storedrequests/openrtb2](endpoints/storedrequests/openrtb2.md) and [/storedrequests/amp](endpoints/storedrequests/amp.md).
- `settings.in-memory-cache.account-invalidation-enabled` - if equals to `true` additional admin protected endpoints will be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public USCustomLogicModuleCreator(USCustomLogicGppReaderFactory gppReaderFactory
this.metrics = Objects.requireNonNull(metrics);

jsonLogicNodesCache = cacheTtl != null && cacheSize != null
? SettingsCache.createCache(cacheTtl, cacheSize)
? SettingsCache.createCache(cacheTtl, cacheSize, 0)
: null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,21 @@ public CachingApplicationSettings(ApplicationSettings delegate,
SettingsCache videoCache,
Metrics metrics,
int ttl,
int size) {
int size,
int jitter) {

if (ttl <= 0 || size <= 0) {
throw new IllegalArgumentException("ttl and size must be positive");
}
if (jitter < 0 || jitter >= ttl) {
throw new IllegalArgumentException("jitter must match the inequality: 0 <= jitter < ttl");
}

this.delegate = Objects.requireNonNull(delegate);
this.accountCache = SettingsCache.createCache(ttl, size);
this.accountToErrorCache = SettingsCache.createCache(ttl, size);
this.adServerPublisherToErrorCache = SettingsCache.createCache(ttl, size);
this.categoryConfigCache = SettingsCache.createCache(ttl, size);
this.accountCache = SettingsCache.createCache(ttl, size, jitter);
this.accountToErrorCache = SettingsCache.createCache(ttl, size, jitter);
this.adServerPublisherToErrorCache = SettingsCache.createCache(ttl, size, jitter);
this.categoryConfigCache = SettingsCache.createCache(ttl, size, jitter);
this.cache = Objects.requireNonNull(cache);
this.ampCache = Objects.requireNonNull(ampCache);
this.videoCache = Objects.requireNonNull(videoCache);
Expand Down
82 changes: 75 additions & 7 deletions src/main/java/org/prebid/server/settings/SettingsCache.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package org.prebid.server.settings;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.checkerframework.checker.index.qual.NonNegative;
import org.prebid.server.settings.model.StoredItem;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadLocalRandom;

/**
* Just a simple wrapper over in-memory caches for requests and imps.
Expand All @@ -20,17 +22,26 @@ public class SettingsCache implements CacheNotificationListener {
private final Map<String, Set<StoredItem>> requestCache;
private final Map<String, Set<StoredItem>> impCache;

public SettingsCache(int ttl, int size) {
public SettingsCache(int ttl, int size, int jitter) {
if (ttl <= 0 || size <= 0) {
throw new IllegalArgumentException("ttl and size must be positive");
}
requestCache = createCache(ttl, size);
impCache = createCache(ttl, size);
if (jitter < 0 || jitter >= ttl) {
throw new IllegalArgumentException("jitter must match the inequality: 0 <= jitter < ttl");
}

requestCache = createCache(ttl, size, jitter);
impCache = createCache(ttl, size, jitter);
}

public static <T> Map<String, T> createCache(int ttl, int size) {
public static <T> Map<String, T> createCache(int ttlSeconds, int size, int jitterSeconds) {
final long expireAfterNanos = (long) (ttlSeconds * 1e9);
final long jitterNanos = jitterSeconds == 0 ? 0L : (long) (jitterSeconds * 1e9);

return Caffeine.newBuilder()
.expireAfterWrite(ttl, TimeUnit.SECONDS)
.expireAfter(jitterNanos == 0L
? new StaticExpiry<>(expireAfterNanos)
: new ExpiryWithJitter<>(expireAfterNanos, jitterNanos))
.maximumSize(size)
.<String, T>build()
.asMap();
Expand All @@ -53,7 +64,10 @@ void saveImpCache(String accountId, String impId, String impValue) {
}

private static void saveCachedValue(Map<String, Set<StoredItem>> cache,
String accountId, String id, String value) {
String accountId,
String id,
String value) {

final Set<StoredItem> values = ObjectUtils.defaultIfNull(cache.get(id), new HashSet<>());
values.add(StoredItem.of(accountId, value));
cache.put(id, values);
Expand All @@ -79,4 +93,58 @@ public void invalidate(List<String> requests, List<String> imps) {
requests.forEach(requestCache.keySet()::remove);
imps.forEach(impCache.keySet()::remove);
}

private static class StaticExpiry<K, V> implements Expiry<K, V> {

private final long expireAfterNanos;

private StaticExpiry(long expireAfterNanos) {
this.expireAfterNanos = expireAfterNanos;
}

@Override
public long expireAfterCreate(K key, V value, long currentTime) {
return expireAfterNanos;
}

@Override
public long expireAfterUpdate(K key, V value, long currentTime, @NonNegative long currentDuration) {
return expireAfterNanos;
}

@Override
public long expireAfterRead(K key, V value, long currentTime, @NonNegative long currentDuration) {
return currentDuration;
}
}

private static class ExpiryWithJitter<K, V> implements Expiry<K, V> {

private final Expiry<K, V> baseExpiry;
private final long jitterNanos;

private ExpiryWithJitter(long baseExpireAfterNanos, long jitterNanos) {
this.baseExpiry = new StaticExpiry<>(baseExpireAfterNanos);
this.jitterNanos = jitterNanos;
}

@Override
public long expireAfterCreate(K key, V value, long currentTime) {
return baseExpiry.expireAfterCreate(key, value, currentTime) + jitter();
}

@Override
public long expireAfterUpdate(K key, V value, long currentTime, @NonNegative long currentDuration) {
return baseExpiry.expireAfterUpdate(key, value, currentTime, currentDuration) + jitter();
}

@Override
public long expireAfterRead(K key, V value, long currentTime, @NonNegative long currentDuration) {
return baseExpiry.expireAfterRead(key, value, currentTime, currentDuration);
}

private long jitter() {
return ThreadLocalRandom.current().nextLong(-jitterNanos, jitterNanos);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ CachingApplicationSettings cachingApplicationSettings(
videoCache,
metrics,
cacheProperties.getTtlSeconds(),
cacheProperties.getCacheSize());
cacheProperties.getCacheSize(),
cacheProperties.getJitterSeconds());
}
}

Expand All @@ -306,19 +307,28 @@ static class CacheConfiguration {
@Bean
@Qualifier("settingsCache")
SettingsCache settingsCache(ApplicationSettingsCacheProperties cacheProperties) {
return new SettingsCache(cacheProperties.getTtlSeconds(), cacheProperties.getCacheSize());
return new SettingsCache(
cacheProperties.getTtlSeconds(),
cacheProperties.getCacheSize(),
cacheProperties.getJitterSeconds());
}

@Bean
@Qualifier("ampSettingsCache")
SettingsCache ampSettingsCache(ApplicationSettingsCacheProperties cacheProperties) {
return new SettingsCache(cacheProperties.getTtlSeconds(), cacheProperties.getCacheSize());
return new SettingsCache(
cacheProperties.getTtlSeconds(),
cacheProperties.getCacheSize(),
cacheProperties.getJitterSeconds());
}

@Bean
@Qualifier("videoSettingCache")
SettingsCache videoSettingCache(ApplicationSettingsCacheProperties cacheProperties) {
return new SettingsCache(cacheProperties.getTtlSeconds(), cacheProperties.getCacheSize());
return new SettingsCache(
cacheProperties.getTtlSeconds(),
cacheProperties.getCacheSize(),
cacheProperties.getJitterSeconds());
}
}

Expand All @@ -336,5 +346,7 @@ private static class ApplicationSettingsCacheProperties {
@NotNull
@Min(1)
private Integer cacheSize;
@Min(0)
private int jitterSeconds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,13 @@ public void setUp() {

target = new CachingApplicationSettings(
delegateSettings,
new SettingsCache(360, 100),
new SettingsCache(360, 100),
new SettingsCache(360, 100),
new SettingsCache(360, 100, 0),
new SettingsCache(360, 100, 0),
new SettingsCache(360, 100, 0),
metrics,
360,
100);
100,
0);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class SettingsCacheTest {

@Before
public void setUp() {
settingsCache = new SettingsCache(10, 10);
settingsCache = new SettingsCache(10, 10, 0);
}

@Test
Expand Down

0 comments on commit 630067d

Please sign in to comment.