I have configured Appstats on my Java Appengine application and noticed that a single JDO Query which returns several objects results in a separate RunQuery RPC call for every object retrieved by the Query.
Shouldn't a Query be done in a single RPC call?
I've tried configuring Fetchgroups and Fetchplans to try to avoid this but to no avail.
My code is something like this:
Query query = pm.newQuery(WidgetDSO.class);
String filter = "widgetId == param1 || widgetId == param2 || widgetId == param3";
String parameters = "String param1, String param2, String param3";
query.setFilter(filter.toString());
query.declareParameters(parameters.toString());
List<WidgetDSO> results = (List<WidgetDSO>) query.executeWithArray(widgetIds);
if (!results.isEmpty())
...
When this runs, Appstats tells me that the last line results.isEmpty()
results in as much RPC calls as objects retrieved:
@104ms datastore_v3.RunQuery real=5ms api=21ms
@422ms datastore_v3.RunQuery real=4ms api=12ms
@428ms datastore_v3.RunQuery real=4ms api=12ms
@434ms datastore_v3.RunQuery real=3ms api=12ms
@439ms datastore_v3.RunQuery real=4ms api=12ms
@445ms datastore_v3.RunQuery real=4ms api=12ms
@451ms datastore_v3.RunQuery real=4ms api=21ms
@463ms datastore_v3.RunQuery real=5ms api=21ms
The stack trace for each of these calls is the same (just a partial stack trace):
com.google.appengine.tools.appstats.Recorder:290 makeAsyncCall()
com.google.apphosting.api.ApiProxy:184 makeAsyncCall()
com.google.appengine.api.datastore.DatastoreApiHelper:81 makeAsyncCall()
com.google.appengine.api.datastore.PreparedQueryImpl:144 runQuery()
com.google.appengine.api.datastore.PreparedQueryImpl:70 asIterator()
com.google.appengine.api.datastore.PreparedMultiQuery$FilteredMultiQueryIterator:165 getNextIterator()
com.google.appengine.api.datastore.PreparedMultiQuery$FilteredMultiQueryIterator:184 computeNext()
com.google.appengine.api.datastore.PreparedMultiQuery$FilteredMultiQueryIterator:98 computeNext()
com.google.appengine.api.datastore.AbstractIterator:52 tryToComputeNext()
com.google.appengine.api.datastore.AbstractIterator:47 hasNext()
com.google.appengine.api.datastore.BasePreparedQuery$UncompilablePreparedQuery$1:86 hasNext()
org.datanucleus.store.appengine.query.RuntimeExceptionWrappingIterator$1:50 get()
org.datanucleus.store.appengine.query.RuntimeExceptionWrappingIterator$1:46 get()
org.datanucleus.store.appengine.query.QueryExceptionWrappers$1:51 get()
org.datanucleus.store.appengine.query.QueryExceptionWrappers$2:86 get()
org.datanucleus.store.appengine.query.RuntimeExceptionWrappingIterator:105 hasNext()
org.datanucleus.store.appengine.query.LazyResult:115 resolveAll()
org.datanucleus.store.appengine.query.LazyResult:110 size()
org.datanucleus.store.appengine.query.StreamingQueryResult:130 size()
org.datanucleus.store.query.AbstractQueryResult:312 isEmpty()
org.instantplaces.im.server.dso.WidgetDSO:209 getWidgetsFromDSO()
org.instantplaces.im.server.resource.WidgetResource:239 doDelete()
org.instantplaces.im.server.resource.GenericResource:244 delete()
Is there a way to fetch all the objects in just one call?
Is that your exact code? If so I would expect at least 3 queries, minimum. There is no native "||" ("OR") query in the datastore. JDO is forced to translate your query into one query for each option. For more info, see the docs and a related blog post. It isn't stated directly - you need to combine the fact that ||'s are turned into .contains(), and .contains() requires multiple gets.
and