Skip to content

Commit 21b0070

Browse files
committed
Tenant partitioned caches could improve cache-hit ratio
1 parent 6d5f740 commit 21b0070

10 files changed

Lines changed: 305 additions & 69 deletions

File tree

ebean-api/src/main/java/io/ebean/DatabaseBuilder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,12 @@ default DatabaseBuilder backgroundExecutorWrapper(BackgroundExecutorWrapper back
896896
@Deprecated
897897
DatabaseBuilder setBackgroundExecutorWrapper(BackgroundExecutorWrapper backgroundExecutorWrapper);
898898

899+
/**
900+
* Sets the tenant partitioning mode for caches. This means, caches are created on demand,
901+
* but they may not get invalidated across tenant boundaries *
902+
*/
903+
void setTenantPartitionedCache(boolean tenantPartitionedCache);
904+
899905
/**
900906
* Set the L2 cache default max size.
901907
*/
@@ -2563,6 +2569,11 @@ interface Settings extends DatabaseBuilder {
25632569
*/
25642570
boolean isAutoPersistUpdates();
25652571

2572+
/**
2573+
* Returns, if the caches are partitioned by tenant.
2574+
*/
2575+
boolean isTenantPartitionedCache();
2576+
25662577
/**
25672578
* Return the L2 cache default max size.
25682579
*/

ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ public class DatabaseConfig implements DatabaseBuilder.Settings {
452452
private int backgroundExecutorShutdownSecs = 30;
453453
private BackgroundExecutorWrapper backgroundExecutorWrapper = new MdcBackgroundExecutorWrapper();
454454

455+
private boolean tenantPartitionedCache;
456+
455457
// defaults for the L2 bean caching
456458

457459
private int cacheMaxSize = 10000;
@@ -1200,6 +1202,26 @@ public DatabaseConfig setBackgroundExecutorWrapper(BackgroundExecutorWrapper bac
12001202
return this;
12011203
}
12021204

1205+
/**
1206+
* Returns, if the caches are partitioned by tenant.
1207+
*/
1208+
@Override
1209+
public boolean isTenantPartitionedCache() {
1210+
return tenantPartitionedCache;
1211+
}
1212+
1213+
/**
1214+
* Sets the tenant partitioning mode for caches. This means, caches are created on demand,
1215+
* but they may not get invalidated across tenant boundaries *
1216+
*/
1217+
@Override
1218+
public void setTenantPartitionedCache(boolean tenantPartitionedCache) {
1219+
this.tenantPartitionedCache = tenantPartitionedCache;
1220+
}
1221+
1222+
/**
1223+
* Return the L2 cache default max size.
1224+
*/
12031225
@Override
12041226
public int getCacheMaxSize() {
12051227
return cacheMaxSize;
@@ -2235,6 +2257,15 @@ protected void loadSettings(PropertiesWrapper p) {
22352257
ddlPlaceholders = p.get("ddl.placeholders", ddlPlaceholders);
22362258
ddlHeader = p.get("ddl.header", ddlHeader);
22372259

2260+
tenantPartitionedCache = p.getBoolean("tenantPartitionedCache", tenantPartitionedCache);
2261+
2262+
cacheMaxIdleTime = p.getInt("cacheMaxIdleTime", cacheMaxIdleTime);
2263+
cacheMaxSize = p.getInt("cacheMaxSize", cacheMaxSize);
2264+
cacheMaxTimeToLive = p.getInt("cacheMaxTimeToLive", cacheMaxTimeToLive);
2265+
queryCacheMaxIdleTime = p.getInt("queryCacheMaxIdleTime", queryCacheMaxIdleTime);
2266+
queryCacheMaxSize = p.getInt("queryCacheMaxSize", queryCacheMaxSize);
2267+
queryCacheMaxTimeToLive = p.getInt("queryCacheMaxTimeToLive", queryCacheMaxTimeToLive);
2268+
22382269
// read tenant-configuration from config:
22392270
// tenant.mode = NONE | DB | SCHEMA | CATALOG | PARTITION
22402271
String mode = p.get("tenant.mode");

ebean-core/src/main/java/io/ebeaninternal/server/cache/CacheManagerOptions.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,31 @@
1313
public final class CacheManagerOptions {
1414

1515
private final ClusterManager clusterManager;
16-
private final DatabaseBuilder.Settings databaseBuilder;
16+
private final String serverName;
1717
private final boolean localL2Caching;
1818
private CurrentTenantProvider currentTenantProvider;
1919
private QueryCacheEntryValidate queryCacheEntryValidate;
2020
private ServerCacheFactory cacheFactory = new DefaultServerCacheFactory();
2121
private ServerCacheOptions beanDefault = new ServerCacheOptions();
2222
private ServerCacheOptions queryDefault = new ServerCacheOptions();
23+
private final boolean tenantPartitionedCache;
2324

2425
CacheManagerOptions() {
2526
this.localL2Caching = true;
2627
this.clusterManager = null;
27-
this.databaseBuilder = null;
28+
this.serverName = "db";
29+
this.tenantPartitionedCache = false;
2830
this.cacheFactory = new DefaultServerCacheFactory();
2931
this.beanDefault = new ServerCacheOptions();
3032
this.queryDefault = new ServerCacheOptions();
3133
}
3234

3335
public CacheManagerOptions(ClusterManager clusterManager, DatabaseBuilder.Settings config, boolean localL2Caching) {
3436
this.clusterManager = clusterManager;
35-
this.databaseBuilder = config;
37+
this.serverName = config.getName();
3638
this.localL2Caching = localL2Caching;
3739
this.currentTenantProvider = config.getCurrentTenantProvider();
40+
this.tenantPartitionedCache = config.isTenantPartitionedCache();
3841
}
3942

4043
public CacheManagerOptions with(ServerCacheOptions beanDefault, ServerCacheOptions queryDefault) {
@@ -55,7 +58,7 @@ public CacheManagerOptions with(CurrentTenantProvider currentTenantProvider) {
5558
}
5659

5760
public String getServerName() {
58-
return (databaseBuilder == null) ? "db" : databaseBuilder.getName();
61+
return serverName;
5962
}
6063

6164
public boolean isLocalL2Caching() {
@@ -85,4 +88,6 @@ public ClusterManager getClusterManager() {
8588
public QueryCacheEntryValidate getQueryCacheEntryValidate() {
8689
return queryCacheEntryValidate;
8790
}
91+
92+
public boolean isTenantPartitionedCache() { return tenantPartitionedCache; }
8893
}

ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ final class DefaultCacheHolder {
3232
private final ServerCacheOptions queryDefault;
3333
private final CurrentTenantProvider tenantProvider;
3434
private final QueryCacheEntryValidate queryCacheEntryValidate;
35+
private final boolean tenantPartitionedCache;
3536

3637
DefaultCacheHolder(CacheManagerOptions builder) {
3738
this.cacheFactory = builder.getCacheFactory();
3839
this.beanDefault = builder.getBeanDefault();
3940
this.queryDefault = builder.getQueryDefault();
4041
this.tenantProvider = builder.getCurrentTenantProvider();
4142
this.queryCacheEntryValidate = builder.getQueryCacheEntryValidate();
43+
this.tenantPartitionedCache = builder.isTenantPartitionedCache();
4244
}
4345

4446
void visitMetrics(MetricVisitor visitor) {
@@ -56,16 +58,42 @@ ServerCache getCache(Class<?> beanType, String collectionProperty) {
5658
return getCacheInternal(beanType, ServerCacheType.COLLECTION_IDS, collectionProperty);
5759
}
5860

61+
private String key(String beanName) {
62+
if (tenantPartitionedCache) {
63+
StringBuilder sb = new StringBuilder(beanName.length() + 64);
64+
sb.append(beanName);
65+
sb.append('.');
66+
sb.append(tenantProvider.currentId());
67+
return sb.toString();
68+
} else {
69+
return beanName;
70+
}
71+
}
72+
5973
private String key(String beanName, ServerCacheType type) {
60-
return beanName + type.code();
74+
StringBuilder sb = new StringBuilder(beanName.length() + 64);
75+
sb.append(beanName);
76+
if (tenantPartitionedCache) {
77+
sb.append('.');
78+
sb.append(tenantProvider.currentId());
79+
}
80+
sb.append(type.code());
81+
return sb.toString();
6182
}
6283

6384
private String key(String beanName, String collectionProperty, ServerCacheType type) {
85+
StringBuilder sb = new StringBuilder(beanName.length() + 64);
86+
sb.append(beanName);
87+
if (tenantPartitionedCache) {
88+
sb.append('.');
89+
sb.append(tenantProvider.currentId());
90+
}
6491
if (collectionProperty != null) {
65-
return beanName + "." + collectionProperty + type.code();
66-
} else {
67-
return beanName + type.code();
92+
sb.append('.');
93+
sb.append(collectionProperty);
6894
}
95+
sb.append(type.code());
96+
return sb.toString();
6997
}
7098

7199
/**
@@ -82,12 +110,17 @@ private ServerCache createCache(Class<?> beanType, ServerCacheType type, String
82110
if (type == ServerCacheType.COLLECTION_IDS) {
83111
lock.lock();
84112
try {
85-
collectIdCaches.computeIfAbsent(beanType.getName(), s -> new ConcurrentSkipListSet<>()).add(key);
113+
collectIdCaches.computeIfAbsent(key(beanType.getName()), s -> new ConcurrentSkipListSet<>()).add(key);
86114
} finally {
87115
lock.unlock();
88116
}
89117
}
90-
return cacheFactory.createCache(new ServerCacheConfig(type, key, shortName, options, tenantProvider, queryCacheEntryValidate));
118+
if (tenantPartitionedCache) {
119+
return cacheFactory.createCache(new ServerCacheConfig(type, key, shortName, options, null, queryCacheEntryValidate));
120+
} else {
121+
return cacheFactory.createCache(new ServerCacheConfig(type, key, shortName, options, tenantProvider, queryCacheEntryValidate));
122+
}
123+
91124
}
92125

93126
void clearAll() {
@@ -103,7 +136,7 @@ public void clear(String name) {
103136
clearIfExists(key(name, ServerCacheType.QUERY));
104137
clearIfExists(key(name, ServerCacheType.BEAN));
105138
clearIfExists(key(name, ServerCacheType.NATURAL_KEY));
106-
Set<String> keys = collectIdCaches.get(name);
139+
Set<String> keys = collectIdCaches.get(key(name));
107140
if (keys != null) {
108141
for (String collectionIdKey : keys) {
109142
clearIfExists(collectionIdKey);
@@ -147,4 +180,7 @@ private ServerCacheOptions getBeanOptions(Class<?> cls) {
147180
return beanDefault.copy(nearCache);
148181
}
149182

183+
boolean isTenantPartitionedCache() {
184+
return tenantPartitionedCache;
185+
}
150186
}

ebean-core/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,8 @@ public ServerCache getBeanCache(Class<?> beanType) {
154154
return cacheHolder.getCache(beanType, ServerCacheType.BEAN);
155155
}
156156

157+
@Override
158+
public boolean isTenantPartitionedCache() {
159+
return cacheHolder.isTenantPartitionedCache();
160+
}
157161
}

ebean-core/src/main/java/io/ebeaninternal/server/cache/SpiCacheManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,10 @@ public interface SpiCacheManager {
8888
*/
8989
void clearLocal(Class<?> beanType);
9090

91+
/**
92+
* returns true, if this chacheManager runs in tenant partitioned mode
93+
* @return
94+
*/
95+
boolean isTenantPartitionedCache();
96+
9197
}

ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ public BeanDescriptor(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy) {
318318
this.idOnlyReference = isIdOnlyReference(propertiesBaseScalar);
319319
boolean noRelationships = propertiesOne.length + propertiesMany.length == 0;
320320
this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly();
321-
this.cacheHelp = new BeanDescriptorCacheHelp<>(this, owner.cacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
321+
this.cacheHelp = BeanDescriptorCacheHelpPartitioned.create(this, owner.cacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported);
322322
this.jsonHelp = initJsonHelp();
323323
this.draftHelp = new BeanDescriptorDraftHelp<>(this);
324324
this.docStoreAdapter = owner.createDocStoreBeanAdapter(this, deploy);

0 commit comments

Comments
 (0)