在Google App Engine(java)中,如何在并发期间立即使删除的实体不可用?
我有一个servlet,它根据请求参数获取实体列表,并在立即删除它们之前将它们缓存在本地列表变量中。删除后,将使用对象列表执行操作 有时,如果servlet同时获得并发请求,两个请求都能够获得相同的实体,并且操作会执行两次,这是不应该发生的 我验证了删除时间,大约是100毫秒。我需要发出其他并发请求,但我不能读取已删除的实体在Google App Engine(java)中,如何在并发期间立即使删除的实体不可用?,java,google-app-engine,google-cloud-datastore,objectify,Java,Google App Engine,Google Cloud Datastore,Objectify,我有一个servlet,它根据请求参数获取实体列表,并在立即删除它们之前将它们缓存在本地列表变量中。删除后,将使用对象列表执行操作 有时,如果servlet同时获得并发请求,两个请求都能够获得相同的实体,并且操作会执行两次,这是不应该发生的 我验证了删除时间,大约是100毫秒。我需要发出其他并发请求,但我不能读取已删除的实体 如何在跨Google应用程序引擎实例的并发过程中有效地处理此问题 我的建议是使用事务来隔离并发问题并创建 幂等请求。在Java中,类似以下内容: DatastoreServ
如何在跨Google应用程序引擎实例的并发过程中有效地处理此问题 我的建议是使用事务来隔离并发问题并创建 幂等请求。在Java中,类似以下内容:
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
PreparedQuery pq = createYourQuery(datastore); // remember to setKeysOnly()
for (Entity entity : pq.asIterable()) {
try {
Transaction tx = datastore.beginTransaction();
try {
datastore.get(entity.getKey());
} catch (EntityNotFoundException e) {
continue;
}
datastore.delete(entity.getKey());
tx.commit();
} catch (ConcurrentModificationException e) {
continue;
}
// execute your extra stuff
}
我的建议是使用事务来隔离并发问题并创建 幂等请求。在Java中,类似以下内容:
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
PreparedQuery pq = createYourQuery(datastore); // remember to setKeysOnly()
for (Entity entity : pq.asIterable()) {
try {
Transaction tx = datastore.beginTransaction();
try {
datastore.get(entity.getKey());
} catch (EntityNotFoundException e) {
continue;
}
datastore.delete(entity.getKey());
tx.commit();
} catch (ConcurrentModificationException e) {
continue;
}
// execute your extra stuff
}
不看代码很难判断,但通常情况下,您观察到的行为是对共享数据结构的非同步读/写访问的结果,在您的示例中,这是一个列表。如果是这种情况,您可以做的是同步读/写列表的块。以下是Java伪代码:
/**
* Read from the cache.
*/
Data getCache(String key) {
synchronized (this) {
// ... Do read
}
}
/**
* Delete the cache.
*/
void clearCache() {
synchronized (this) {
// ... Do cleanup
}
}
不看代码很难判断,但通常情况下,您观察到的行为是对共享数据结构的非同步读/写访问的结果,在您的示例中,这是一个列表。如果是这种情况,您可以做的是同步读/写列表的块。以下是Java伪代码:
/**
* Read from the cache.
*/
Data getCache(String key) {
synchronized (this) {
// ... Do read
}
}
/**
* Delete the cache.
*/
void clearCache() {
synchronized (this) {
// ... Do cleanup
}
}
使用?正如其他人所说,使用事务。此外,通过键而不是查询与事务相结合的方式进行抓取也会遇到最终一致性问题。感谢您的宝贵建议。那么,您是否使用多个servlet实例?@Slava Imeshev这是一个简单的servlet,但GAE服务器维护多个实例。使用?正如其他人所说,使用事务。此外,通过键而不是查询与事务相结合的方式获取数据也会遇到最终一致性问题。感谢您的宝贵建议。因此,您是否使用servlet的多个实例?@Slava Imeshev这是一个简单的servlet,但GAE server维护多个实例。由于Appengine具有分布式体系结构,同步将无法跨不同JVM工作,因为Appengine具有分布式体系结构,同步将无法跨不同JVM工作,感谢@feroult对代码的详细解释,我将尝试处理事务一次,但它可能会成为瓶颈(因为读取操作也应该在事务中),因为它处理非常频繁的请求。我还有另一个想法,就是使用Appengine Memcache实现同步,其中存储了一些表示已删除到另一个请求的标志。感谢@feroult的详细解释和代码,我将尝试一次事务,但它可能会成为瓶颈(因为读取操作也应该在事务中)因为它处理非常频繁的请求。我还有另一个想法,就是使用Appengine Memcache实现同步,该Memcache存储一些表示已删除到另一个请求的标志。