Fix for #131 - Memory issue (Hard refs) when using findIterate

This commit is contained in:
Rob Bygrave
2014-05-27 20:35:53 +12:00
parent 9211c7e6ab
commit 5491b27528
7 changed files with 80 additions and 60 deletions
@@ -29,6 +29,7 @@ import com.avaje.ebeaninternal.server.deploy.DeployPropertyParserMap;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.query.CQueryPlan;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Wraps the objects involved in executing a Query.
@@ -43,12 +44,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private final BeanFinder<T> finder;
private final LoadContext graphContext;
private final Boolean readOnly;
private final RawSql rawSql;
private LoadContext loadContext;
private PersistenceContext persistenceContext;
private HashQuery cacheKey;
@@ -68,13 +69,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
this.queryEngine = queryEngine;
this.query = query;
this.readOnly = query.isReadOnly();
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
graphContext.registerSecondaryQueries(query);
}
public void executeSecondaryQueries(int defaultQueryBatch) {
graphContext.executeSecondaryQueries(this, defaultQueryBatch);
loadContext.executeSecondaryQueries(this, defaultQueryBatch);
}
/**
@@ -86,7 +84,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* </p>
*/
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
return graphContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
return loadContext.getSecondaryQueriesMinBatchSize(this, defaultQueryBatch);
}
/**
@@ -107,7 +105,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Return the graph context for this query.
*/
public LoadContext getGraphContext() {
return graphContext;
return loadContext;
}
/**
@@ -165,10 +163,20 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
createdTransaction = true;
}
}
// initialise the persistenceContext and loadContext
this.persistenceContext = getPersistenceContext(query, transaction);
this.graphContext.setPersistenceContext(persistenceContext);
this.loadContext = new DLoadContext(this);
this.loadContext.registerSecondaryQueries(query);
}
/**
* For iterate queries reset the persistenceContext and loadContext.
*/
public void flushPersistenceContextOnIterate() {
persistenceContext = new DefaultPersistenceContext();
loadContext.setPersistenceContext(persistenceContext);
}
/**
* Get the TransactionContext either explicitly set on the query or
* transaction scoped.
@@ -359,10 +367,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
}
}
public void flushPersistenceContextOnIterate() {
beanDescriptor.flushPersistenceContextOnIterate(persistenceContext);
}
/**
* Return true if the request wants to log the secondary queries (test purpose).
*/
@@ -2254,12 +2254,5 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
return ctx.popBeanState();
}
public void flushPersistenceContextOnIterate(PersistenceContext persistenceContext) {
persistenceContext.clear(beanType);
for (int i = 0; i < propertiesMany.length; i++) {
persistenceContext.clear(propertiesMany[i].getBeanDescriptor().getBeanType());
}
}
}
@@ -25,7 +25,6 @@ import com.avaje.ebeaninternal.server.cache.CachedBeanDataUpdate;
import com.avaje.ebeaninternal.server.cache.CachedManyIds;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
@@ -328,14 +327,15 @@ public final class BeanDescriptorCacheHelp<T> {
if (context == null) {
context = new DefaultPersistenceContext();
}
context.put(query.getId(), bean);
DLoadContext loadContext = new DLoadContext(desc.getEbeanServer(), desc, query.isReadOnly(), query);
loadContext.setPersistenceContext(context);
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();
// Not using a loadContext for beans coming out of L2 cache
// so that means no batch lazy loading for these beans
EntityBean entityBean = (EntityBean)bean;
EntityBeanIntercept ebi = entityBean._ebean_getIntercept();
ebi.setPersistenceContext(context);
loadContext.register(null, ebi);
Object id = desc.getId(entityBean);
context.put(id, bean);
}
/**
@@ -29,7 +29,8 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
super(parent, desc, path, defaultBatchSize, queryProps);
this.bufferList = new ArrayList<DLoadBeanContext.LoadBuffer>();
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<DLoadBeanContext.LoadBuffer>();
this.currentBuffer = createBuffer(firstBatchSize);
}
@@ -52,11 +53,11 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
protected void register(EntityBeanIntercept ebi){
ebi.setBeanLoader(0, currentBuffer, getPersistenceContext());
if (currentBuffer.add(ebi)) {
// the currentBuffer is full so create another one
currentBuffer = createBuffer(secondaryBatchSize);
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(secondaryBatchSize);
}
currentBuffer.add(ebi);
ebi.setBeanLoader(0, currentBuffer, getPersistenceContext());
}
private LoadBuffer createBuffer(int size) {
@@ -99,23 +100,33 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
*/
public static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
private final PersistenceContext persistenceContext;
private final DLoadBeanContext context;
private final int batchSize;
private final List<EntityBeanIntercept> list;
public LoadBuffer(DLoadBeanContext context, int batchSize) {
this.context = context;
// set the persistence context as at this moment in
// case it changes as part of a findIterate etc
this.persistenceContext = context.getPersistenceContext();
this.batchSize = batchSize;
this.list = new ArrayList<EntityBeanIntercept>(batchSize);
}
/**
* Return true if the buffer is full.
*/
public boolean add(EntityBeanIntercept ebi) {
list.add(ebi);
public boolean isFull() {
return batchSize == list.size();
}
/**
* Return true if the buffer is full.
*/
public void add(EntityBeanIntercept ebi) {
list.add(ebi);
}
@Override
public List<EntityBeanIntercept> getBatch() {
@@ -139,7 +150,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
@Override
public PersistenceContext getPersistenceContext() {
return context.getPersistenceContext();
return persistenceContext;
}
@Override
@@ -49,15 +49,18 @@ public class DLoadContext implements LoadContext {
private final Map<String,ObjectGraphNode> nodePathMap = new HashMap<String, ObjectGraphNode>();
private PersistenceContext persistenceContext;
private List<OrmQueryProperties> secQuery;
public DLoadContext(OrmQueryRequest<?> request) {
public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor<?> rootDescriptor, Boolean readOnly, SpiQuery<?> query) {
this.ebeanServer = ebeanServer;
this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize();
this.rootDescriptor = rootDescriptor;
this.readOnly = readOnly;
this.persistenceContext = request.getPersistenceContext();
this.ebeanServer = request.getServer();
this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize();
this.rootDescriptor = request.getBeanDescriptor();
SpiQuery<?> query = request.getQuery();
this.readOnly = query.isReadOnly();
this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache());
this.useAutofetchManager = query.getAutoFetchManager() != null;
@@ -72,7 +75,7 @@ public class DLoadContext implements LoadContext {
// initialise rootBeanContext after origin and relativePath have been set
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
}
}
protected boolean isExcludeBeanCache() {
return excludeBeanCache;
@@ -31,14 +31,16 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
this.property = property;
this.bufferList = new ArrayList<DLoadManyContext.LoadBuffer>();
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<DLoadManyContext.LoadBuffer>();
this.currentBuffer = createBuffer(firstBatchSize);
}
private LoadBuffer createBuffer(int size) {
LoadBuffer buffer = new LoadBuffer(this, size);
bufferList.add(buffer);
if (bufferList != null) {
bufferList.add(buffer);
}
return buffer;
}
@@ -74,11 +76,11 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
public void register(BeanCollection<?> bc){
bc.setLoader(0, currentBuffer);
if (currentBuffer.add(bc)) {
// the currentBuffer is full so create another one
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(secondaryBatchSize);
}
}
currentBuffer.add(bc);
bc.setLoader(0, currentBuffer);
}
@@ -112,22 +114,32 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
*/
public static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
private final PersistenceContext persistenceContext;
private final DLoadManyContext context;
private final int batchSize;
private final List<BeanCollection<?>> list;
public LoadBuffer(DLoadManyContext context, int batchSize) {
this.context = context;
// set the persistence context as at this moment in
// case it changes as part of a findIterate etc
this.persistenceContext = context.getPersistenceContext();
this.batchSize = batchSize;
this.list = new ArrayList<BeanCollection<?>>(batchSize);
}
/**
* Return true if the buffer is full.
*/
public boolean add(BeanCollection<?> bc) {
public boolean isFull() {
return batchSize == list.size();
}
/**
* Return true if the buffer is full.
*/
public void add(BeanCollection<?> bc) {
list.add(bc);
return batchSize == list.size();
}
@Override
@@ -162,7 +174,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
@Override
public PersistenceContext getPersistenceContext() {
return context.getPersistenceContext();
return persistenceContext;
}
@Override
@@ -188,8 +188,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
private final int maxRowsLimit;
private final PersistenceContext persistenceContext;
private DataReader dataReader;
/**
@@ -266,7 +264,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
this.logWhereSql = queryPlan.getLogWhereSql();
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
this.persistenceContext = request.getPersistenceContext();
this.maxRowsLimit = query.getMaxRows() > 0 ? query.getMaxRows() : GLOBAL_ROW_LIMIT;
this.help = createHelp(request);
this.collection = (BeanCollection<T>) (help != null ? help.createEmpty(false) : null);
@@ -420,7 +417,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
* Return the persistence context.
*/
public PersistenceContext getPersistenceContext() {
return persistenceContext;
return request.getPersistenceContext();
}
public void setLoadedBean(EntityBean bean, Object id, Object lazyLoadParentId) {
@@ -443,7 +440,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
if (lazyLoadParentId != null) {
if (!lazyLoadParentId.equals(this.lazyLoadParentId)) {
// get the appropriate parent bean from the persistence context
this.lazyLoadParentBean = (EntityBean)persistenceContext.get(lazyLoadManyProperty.getBeanDescriptor().getBeanType(), lazyLoadParentId);
this.lazyLoadParentBean = (EntityBean)getPersistenceContext().get(lazyLoadManyProperty.getBeanDescriptor().getBeanType(), lazyLoadParentId);
this.lazyLoadParentId = lazyLoadParentId;
}