Тайм-ауты в запросах хранилища данных - PullRequest
0 голосов
/ 22 февраля 2019

Я использую 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());
}

1 Ответ

0 голосов
/ 23 февраля 2019

Эта ошибка обычно возникает из-за конфликта записи . Логика этого проста, если у вас есть несколько транзакций, таких как запись и чтение некоторых вещей из одной и той же группы сущностей одновременно.
ТамСуществуют различные подходы к решению этой проблемы:

  • Запрос живет только 30 секунд, но вы можете расширить его, преобразовав свой API в очередь задач.Обычно для решения таких проблем записи вы всегда должны использовать очередь задач, которая длится около 10 минут.
  • Если возможно, уменьшите группу объектов.

Вы можете найти больше подходов здесь.

Надеюсь, что это ответ на ваш вопрос !!!

...