java - 数据存储查询超时

标签 java google-app-engine google-cloud-datastore objectify

我正在 java8 运行时环境中的自动缩放应用程序引擎实例中使用 objectify v5.1.11。

我有一个API,物联网设备定期调用它来上传统计信息。在此 API 中,我将一个实体插入数据存储区以存储统计信息。该实体使用自动生成的数据存储 ID。实体定义如下:

@Entity(name = "Stats")
public class StatsEntity {
    @Id
    private Long statisticsId;

    @Index
    private Long deviceId;

    @Index
    private String statsKey;

    @Index
    private Date creationTime;
}

但我需要在插入实体之前检查重复项。我切换到自定义生成的(字符串)ID。我想出了一种机制,将 deviceId 附加到设备提供的 statsKey(对于设备内的每个统计数据都是唯一的)字符串,以生成 ID。

这是为了避免 eventual consistency behaviour如果我使用查询来检查实体是否已经存在。由于通过 ID 获取具有很强的一致性,因此我可以使用它来检查重复项。

还有另一个 API 可以获取设备上传的统计信息。在此 API 中,我通过按 deviceId 进行过滤并按 creationTime 按降序(最新的在前)排序来列出实体,页面大小为 100。此请求超时,因为请求超出了 appengine 的 60 秒限制。我在日志中看到以下异常:

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 分钟),这根据此 link 是不好的。 。 但我的流量不足以保证这种行为。每个设备每 15 分钟发出 2 到 3 个请求,大约有 300 台设备。 当我尝试列出自从切换到自定义 ID 以来未发出任何请求的设备的实体时,我仍然观察到此问题。

编辑

我列出实体的代码如下:

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());
}

最佳答案

此错误通常是由于write contention.而发生的。如果您有多个事务(例如同时从同一实体组写入和读取一些内容),则其背后的逻辑很简单。
有多种方法可以解决这个问题:

  • 查询仅存在 30 秒,但您可以通过将 API 转换为任务队列来扩展它。通常处理此类写入争用问题时,您应该始终使用持续大约 10 分钟的任务队列。
  • 如果可能,请缩小您的实体组。

您可以找到更多方法 here.

希望这能回答您的问题!!!

关于java - 数据存储查询超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54824534/

相关文章:

java - Intellij - 错误 :java: invalid source release: 1. 8

Java尝试三种方法,仅当没有成功时才进入catch block

java - 递归自顶向下合并排序

java - 通过我的 Google 应用引擎应用程序将大型 excel 文件上传到 bigquery

google-app-engine - 使用 Go 将文件写入 Google App Engine Storage 似乎有 10MB 的限制?

java - 有没有办法在启动 mvn appengine :devserver? 时不运行单元测试

PYTHON-数据存储中 TextProperty 的属性 "{field_name}"的值超过 1048487 个字节

java - java 如何判断没有捕获到异常

python - 在将实体保存到 ndb 之前通过其键引用实体

python - 使用自定义验证器存储重复的 KeyProperty 时出现 BadValueError