#1796 - ENH: add query.fetchCache(path) ... for explicitly building part of graph from L2 cache

This commit is contained in:
rob bygrave
2019-08-19 21:36:19 +12:00
parent 9155716752
commit 9674711516
20 changed files with 319 additions and 54 deletions
+22
View File
@@ -148,6 +148,8 @@ public class FetchConfig implements Serializable {
private boolean queryAll;
private boolean cache;
/**
* Construct the fetch configuration object.
*/
@@ -188,6 +190,17 @@ public class FetchConfig implements Serializable {
return this;
}
/**
* Eagerly fetch the beans fetching the beans from the L2 bean cache
* and using the DB for beans not in the cache.
*/
public FetchConfig cache() {
this.cache = true;
this.queryBatchSize = 0;
this.queryAll = true;
return this;
}
/**
* Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
@@ -247,6 +260,13 @@ public class FetchConfig implements Serializable {
return queryAll;
}
/**
* Return true if this uses L2 bean cache.
*/
public boolean isCache() {
return cache;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -255,6 +275,7 @@ public class FetchConfig implements Serializable {
FetchConfig that = (FetchConfig) o;
if (lazyBatchSize != that.lazyBatchSize) return false;
if (queryBatchSize != that.queryBatchSize) return false;
if (cache != that.cache) return false;
return queryAll == that.queryAll;
}
@@ -263,6 +284,7 @@ public class FetchConfig implements Serializable {
int result = lazyBatchSize;
result = 92821 * result + queryBatchSize;
result = 92821 * result + (queryAll ? 1 : 0);
result = 92821 * result + (cache ? 1 : 0);
return result;
}
}
@@ -67,12 +67,26 @@ public interface FetchGroupBuilder<T> {
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path);
/**
* Fetch the path including all its properties using L2 cache.
* Cache misses fallback to fetchQuery().
*/
@Nonnull
FetchGroupBuilder<T> fetchCache(String path);
/**
* Fetch the path including specified properties using a query join.
*/
@Nonnull
FetchGroupBuilder<T> fetchQuery(String path, String properties);
/**
* Fetch the path including specified properties using L2 cache.
* Cache misses fallback to fetchQuery().
*/
@Nonnull
FetchGroupBuilder<T> fetchCache(String path, String properties);
/**
* Fetch the path including all its properties lazily.
*/
+13
View File
@@ -501,6 +501,14 @@ public interface Query<T> {
*/
Query<T> fetchQuery(String path, String fetchProperties);
/**
* Fetch the path and properties using L2 bean cache.
*
* @param path The path of the beans we are fetching from L2 cache.
* @param fetchProperties The properties that should be loaded.
*/
Query<T> fetchCache(String path, String fetchProperties);
/**
* Fetch the path and properties lazily (via batch lazy loading).
* <p>
@@ -587,6 +595,11 @@ public interface Query<T> {
*/
Query<T> fetchQuery(String path);
/**
* Fetch the path eagerly using L2 cache.
*/
Query<T> fetchCache(String path);
/**
* Fetch the path lazily (via batch lazy loading).
* <p>
@@ -95,13 +95,10 @@ public final class EntityBeanIntercept implements Serializable {
private final byte[] flags;
private boolean fullyLoadedBean;
private boolean loadedFromCache;
private Object[] origValues;
private Exception[] loadErrors;
private int lazyLoadProperty = -1;
private Object ownerId;
private int sortOrder;
@@ -311,6 +308,22 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Set true when the bean has been loaded from L2 bean cache.
* The effect of this is that we should skip the cache if there
* is subsequent lazy loading (bean cache partially populated).
*/
public void setLoadedFromCache(boolean loadedFromCache) {
this.loadedFromCache = loadedFromCache;
}
/**
* Return true if this bean was loaded from L2 bean cache.
*/
public boolean isLoadedFromCache() {
return loadedFromCache;
}
/**
* Return true if the bean should be treated as readOnly. If a setter method
* is called when it is readOnly an Exception is thrown.
@@ -780,7 +793,7 @@ public final class EntityBeanIntercept implements Serializable {
}
public boolean[] getLoaded() {
boolean[] ret= new boolean[flags.length];
boolean[] ret = new boolean[flags.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
}
@@ -23,11 +23,14 @@ public class LoadBeanRequest extends LoadRequest {
private final boolean loadCache;
private boolean loadedFromCache;
/**
* Construct for lazy load request.
*/
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, String lazyLoadProperty, boolean loadCache) {
this(LoadBuffer, null, true, lazyLoadProperty, loadCache);
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, EntityBeanIntercept ebi, boolean loadCache) {
this(LoadBuffer, null, true, ebi.getLazyLoadProperty(), loadCache);
this.loadedFromCache = ebi.isLoadedFromCache();
}
/**
@@ -52,11 +55,17 @@ public class LoadBeanRequest extends LoadRequest {
return loadBuffer.getBeanDescriptor().getBeanType();
}
/**
* Return true if the beans invoking lazy loading were previously loaded from cache.
*/
public boolean isLoadedFromCache() {
return loadedFromCache;
}
private boolean isLoadCache() {
return loadCache;
}
public String getDescription() {
return "path:" + loadBuffer.getFullPath() + " batch:" + batch.size();
}
@@ -13,6 +13,8 @@ public class CachedBeanDataToBean {
public static void load(BeanDescriptor<?> desc, EntityBean bean, CachedBeanData cacheBeanData, PersistenceContext context) {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// any future lazy loading skips L2 bean cache
ebi.setLoadedFromCache(true);
BeanProperty idProperty = desc.getIdProperty();
if (desc.getInheritInfo() != null) {
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.CacheMode;
import io.ebean.ExpressionList;
import io.ebean.Transaction;
import io.ebean.bean.BeanCollection;
@@ -38,10 +39,6 @@ class DefaultBeanLoader {
this.onIterateUseExtraTxn = server.getDatabasePlatform().useExtraTransactionOnIterateSecondaryQueries();
}
void refreshMany(EntityBean parentBean, String propertyName) {
refreshMany(parentBean, propertyName, null);
}
void loadMany(LoadManyRequest loadRequest) {
SpiQuery<?> query = loadRequest.createQuery(server);
@@ -57,8 +54,8 @@ class DefaultBeanLoader {
loadManyInternal(parentBean, propertyName, null, false, onlyIds);
}
private void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
loadManyInternal(parentBean, propertyName, t, true, false);
void refreshMany(EntityBean parentBean, String propertyName) {
loadManyInternal(parentBean, propertyName, null, true, false);
}
private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, boolean onlyIds) {
@@ -159,6 +156,9 @@ class DefaultBeanLoader {
SpiQuery<?> query = server.createQuery(loadRequest.getBeanType());
loadRequest.configureQuery(query, idList);
if (loadRequest.isLoadedFromCache()) {
query.setBeanCacheMode(CacheMode.PUT);
}
List<?> list = executeQuery(loadRequest, query);
loadRequest.postLoad(list);
@@ -570,37 +570,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
@Override
public void refreshMany(Object parentBean, String propertyName) {
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName);
}
@Override
public void loadMany(LoadManyRequest loadRequest) {
beanLoader.loadMany(loadRequest);
}
@Override
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
beanLoader.loadMany(bc, onlyIds);
}
@Override
public void refresh(Object bean) {
beanLoader.refresh(checkEntityBean(bean));
}
@Override
public void loadBean(LoadBeanRequest loadRequest) {
beanLoader.loadBean(loadRequest);
}
@Override
public void loadBean(EntityBeanIntercept ebi) {
beanLoader.loadBean(ebi);
}
@@ -97,6 +97,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
this.queryEngine = queryEngine;
this.query = query;
this.readOnly = query.isReadOnly();
this.persistenceContext = query.getPersistenceContext();
}
public PersistenceException translate(String bindLog, String sql, SQLException e) {
@@ -2316,6 +2316,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return idBinder.convertSetId(idValue, bean);
}
/**
* Set the Id value to the bean (without type conversion).
*/
public void setId(Object idValue, EntityBean bean) {
idProperty.setValueIntercept(bean, idValue);
}
@Override
public Property getProperty(String propName) {
return findProperty(propName);
@@ -675,25 +675,30 @@ final class BeanDescriptorCacheHelp<T> {
*/
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
id = desc.convertId(id);
EntityBean bean = null;
if (context == null) {
context = new DefaultPersistenceContext();
} else {
bean = (EntityBean)desc.contextGet(context, id);
}
if (bean == null) {
bean = desc.createEntityBean();
desc.setId(id, bean);
desc.contextPut(context, id, bean);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// Not using loadContext here so no batch lazy loading for these beans
ebi.setBeanLoader(desc.getEbeanServer());
if (Boolean.TRUE.equals(readOnly)) {
ebi.setReadOnly(true);
}
ebi.setPersistenceContext(context);
}
EntityBean bean = desc.createEntityBean();
id = desc.convertSetId(id, bean);
CachedBeanDataToBean.load(desc, bean, data, context);
EntityBeanIntercept ebi = bean._ebean_getIntercept();
// Not using a loadContext for beans coming out of L2 cache
// so that means no batch lazy loading for these beans
ebi.setBeanLoader(desc.getEbeanServer());
if (Boolean.TRUE.equals(readOnly)) {
ebi.setReadOnly(true);
}
ebi.setPersistenceContext(context);
desc.contextPut(context, id, bean);
if (desc.isReadAuditing()) {
desc.readAuditBean("l2", "", bean);
}
@@ -24,11 +24,9 @@ public class IdBinderFactory {
* Create the IdConvertSet for the given type of Id properties.
*/
public IdBinder createIdBinder(BeanProperty id) {
if (id == null) {
// for report type beans that don't need an id
return EMPTY;
}
if (id.isEmbedded()) {
return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne<?>) id);
@@ -191,7 +191,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
}
}
LoadBeanRequest req = new LoadBeanRequest(this, ebi.getLazyLoadProperty(), context.hitCache);
LoadBeanRequest req = new LoadBeanRequest(this, ebi, context.hitCache);
context.desc.getEbeanServer().loadBean(req);
}
}
@@ -11,6 +11,8 @@ import io.ebeaninternal.server.querydefn.SpiFetchGroup;
*/
class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
@@ -61,6 +63,12 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
return this;
}
@Override
public FetchGroupBuilder<T> fetchCache(String path) {
detail.fetch(path, null, FETCH_CACHE);
return this;
}
@Override
public FetchGroupBuilder<T> fetchLazy(String path) {
detail.fetch(path, null, FETCH_LAZY);
@@ -79,6 +87,12 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
return this;
}
@Override
public FetchGroupBuilder<T> fetchCache(String path, String properties) {
detail.fetch(path, properties, FETCH_CACHE);
return this;
}
@Override
public FetchGroupBuilder<T> fetchLazy(String path, String properties) {
detail.fetch(path, properties, FETCH_LAZY);
@@ -43,6 +43,8 @@ import java.util.function.Predicate;
*/
class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
@@ -76,6 +78,11 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
return fetch(property, null, FETCH_QUERY);
}
@Override
public Query<T> fetchCache(String property) {
return fetch(property, null, FETCH_CACHE);
}
@Override
public Query<T> fetchLazy(String property) {
return fetch(property, null, FETCH_LAZY);
@@ -96,6 +103,11 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
return fetch(property, columns, FETCH_QUERY);
}
@Override
public Query<T> fetchCache(String property, String columns) {
return fetch(property, columns, FETCH_CACHE);
}
@Override
public Query<T> fetchLazy(String property, String columns) {
return fetch(property, columns, FETCH_LAZY);
@@ -79,6 +79,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private static final String DEFAULT_QUERY_NAME = "default";
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
@@ -1405,6 +1407,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return fetch(property, null, FETCH_QUERY);
}
public Query<T> fetchCache(String property) {
return fetch(property, null, FETCH_CACHE);
}
@Override
public Query<T> fetchLazy(String property) {
return fetch(property, null, FETCH_LAZY);
@@ -1425,6 +1431,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return fetch(property, columns, FETCH_QUERY);
}
@Override
public Query<T> fetchCache(String property, String columns) {
return fetch(property, columns, FETCH_CACHE);
}
@Override
public Query<T> fetchLazy(String property, String columns) {
return fetch(property, columns, FETCH_LAZY);
@@ -107,6 +107,9 @@ public class OrmQueryProperties implements Serializable {
this.readOnly = response.readOnly;
if (fetchConfig != null) {
this.fetchConfig = fetchConfig;
if (fetchConfig.isCache()) {
this.cache = true;
}
} else {
this.fetchConfig = response.fetchConfig;
}