Я использую objectify v5.1.11 из автоматически масштабированных экземпляров ядра приложения в среде выполнения java8.
У меня есть API, который IOT-устройства периодически вызывают для загрузки статистической информации.В этом API я вставляю сущность в хранилище данных для хранения статистической информации.Этот объект использует автоматически сгенерированные идентификаторы хранилища данных.Определение сущности выглядит следующим образом:
@Entity(name = "Stats")
public class StatsEntity {
@Id
private Long statisticsId;
@Index
private Long deviceId;
@Index
private String statsKey;
@Index
private Date creationTime;
}
Но у меня было требование проверять дубликаты перед вставкой сущности.Я переключился на пользовательские (строковые) идентификаторы.Я разработал механизм добавления строки deviceId
к statsKey
(уникальной для каждой статистики в устройстве), предоставляемой устройством для генерации идентификатора.
Это позволяет избежать возможной согласованности Поведение , если я использую запрос, чтобы проверить, существует ли сущность.Так как get by ID строго согласован, я могу использовать его для проверки дубликатов.
Существует еще один API для получения статистики, загруженной устройством.В этом API я перечисляю сущности, фильтруя по deviceId
и упорядочивая по creationTime
в порядке убывания (сначала самое новое) с размером страницы 100. Этот запрос истекает по времени, так как запрос превышает 60-секундный предел appengine.Я вижу следующее исключение в журналах:
Task was cancelled.
java.util.concurrent.CancellationException: Task was cancelled.
at com.google.common.util.concurrent.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1355)
at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:555)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:436)
at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:99)
at com.google.appengine.tools.development.TimedFuture.get(TimedFuture.java:42)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture.java:62)
at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:93)
at com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:69)
at com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:33)
at com.google.appengine.api.datastore.BaseQueryResultsSource.loadMoreEntities(BaseQueryResultsSource.java:243)
at com.google.appengine.api.datastore.BaseQueryResultsSource.loadMoreEntities(BaseQueryResultsSource.java:180)
at com.google.appengine.api.datastore.QueryResultIteratorImpl.ensureLoaded(QueryResultIteratorImpl.java:173)
at com.google.appengine.api.datastore.QueryResultIteratorImpl.hasNext(QueryResultIteratorImpl.java:70)
at com.googlecode.objectify.impl.KeysOnlyIterator.hasNext(KeysOnlyIterator.java:29)
at com.google.common.collect.Iterators$5.hasNext(Iterators.java:580)
at com.google.common.collect.TransformedIterator.hasNext(TransformedIterator.java:42)
at com.googlecode.objectify.impl.ChunkIterator.hasNext(ChunkIterator.java:39)
at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:50)
at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:50)
at com.google.common.collect.Iterators$PeekingImpl.hasNext(Iterators.java:1105)
at com.googlecode.objectify.impl.ChunkingIterator.hasNext(ChunkingIterator.java:51)
at com.ittiam.cvml.dao.repository.PerformanceStatsRepositoryImpl.list(PerformanceStatsRepositoryImpl.java:154)
at com.ittiam.cvml.service.PerformanceStatsServiceImpl.listPerformanceStats(PerformanceStatsServiceImpl.java:227)
Значение statsKey
, предоставляемое устройством, основано на времени и, следовательно, монотонно увеличивается (увеличение шага на 15 минут), что плохо для этого ссылка .Но мой трафик недостаточно велик, чтобы оправдать такое поведение.Каждое устройство отправляет от 2 до 3 запросов каждые 15 минут и около 300 устройств.Когда я пытаюсь составить список сущностей для устройств, которые не сделали никаких запросов с тех пор, как я переключился на пользовательский идентификатор, я все еще наблюдаю эту проблему.
Редактировать
Мой код для перечисления сущностиследующим образом:
Query<StatsEntity> query = ofy().load().type(StatsEntity.class);
List<StatsEntity> entityList =
new ArrayList<StatsEntity>();
query = query.filter("deviceId", deviceId);
query = query.order("-creationTime");
query = query.limit(100);
QueryResultIterator<StatsEntity> iterator = query.iterator();
while (iterator.hasNext()) {
entityList.add(iterator.next());
}