Java 提交的JDO写入不适用于本地GAE HRD,也可能不适用于重用的事务
我在AppEngine上使用JDO2.3。我使用主/从数据存储进行本地测试,最近切换到使用HRD数据存储进行本地测试,我的应用程序的一部分正在崩溃(这是意料之中的)。该应用程序的一个突破之处是它可以快速发送大量的写操作——这是因为1秒的限制,它失败了,同时出现了一个修改异常 好的,这也是意料之中的,所以我让浏览器在写入失败后再次重试写入(可能不是最好的黑客,但我只是想让它快速工作) 但一件奇怪的事情正在发生。一些应该成功的写入(那些没有得到并发修改异常的写入)也失败了,即使提交阶段完成,请求返回我的成功代码。我可以从日志中看到重试的请求工作正常,但我猜,在第一次尝试时提交的这些请求从未“应用”。但从我读到的关于应用阶段的内容来看,再次写入同一实体应该会强制应用。。。但事实并非如此 代码如下。需要注意的一些事项:Java 提交的JDO写入不适用于本地GAE HRD,也可能不适用于重用的事务,java,google-app-engine,memcached,jdo,Java,Google App Engine,Memcached,Jdo,我在AppEngine上使用JDO2.3。我使用主/从数据存储进行本地测试,最近切换到使用HRD数据存储进行本地测试,我的应用程序的一部分正在崩溃(这是意料之中的)。该应用程序的一个突破之处是它可以快速发送大量的写操作——这是因为1秒的限制,它失败了,同时出现了一个修改异常 好的,这也是意料之中的,所以我让浏览器在写入失败后再次重试写入(可能不是最好的黑客,但我只是想让它快速工作) 但一件奇怪的事情正在发生。一些应该成功的写入(那些没有得到并发修改异常的写入)也失败了,即使提交阶段完成,请求返回
因为没有实现事务,回滚本质上是一个禁止操作。因此,当两个事务试图同时修改记录时,我会得到一个脏读。换句话说,A读取数据,B同时读取数据。A尝试修改数据,B尝试修改数据的不同部分。A写入数据存储,然后B写入,删除A的更改。然后B被appengine“回滚”,但由于回滚在本地数据存储上运行时是不可操作的,因此B的更改保留,而a的更改不保留。同时,由于B是引发异常的线程,客户端重试B,但不重试A(因为A被认为是成功的事务)。也许对您来说是个坏消息,我离开了JDO,使用Objectify,在某些地方直接使用datanucleus。我完全可以控制我的持久性,这是一个性能和设计更好的选择(如果你考虑长远的话) 由于db不是sql,因此针对JPA、JDO和标准假设存在结构性变化: 使用本机datanucleus API,您可以执行标准JPA甚至Objectify中都没有的操作:我使用的示例是动态创建列 GAE中不存在事务,有些事务有时看起来像事务(实体组)。因此,使用本机API将避免您执行此类操作
试着用操纵杆驾驶一辆汽车是可行的,但肯定有新的东西需要学习。在我看来,学习原生方式是值得的您是否想过重新设计您的数据存储以及如何使用它,以避免每秒多次持久化到同一实体组?或者,您是否尝试过将持久化转移到数据存储以使任务排队,并按照1/s实体组写入频率限制进行安排?我已经想到了这一点。但在我这么做之前,我想了解为什么会发生这种特殊的错误。。。我担心的是,我根本不了解有关HRD或app engine/jdo事务或其他方面的内容,或者我在文档中遗漏了一些内容,这会影响我以后的工作,因为我至少还有25个其他服务需要添加事务(如果数据存储访问不在事务中,JDO缓存将不起作用)FWIW,使用当前的插件(GAE JDO v2.x),我认为二级缓存不需要在事务中访问;如果读取了一个对象,那么它就是二级缓存,如果没有,那么应该报告它(显然,旧插件不受支持,因此只有在使用当前插件时才报告此情况)@DataNucleus升级到新插件后,得到了相同的行为。我不明白的是,代码执行两次数据存储读取,然后执行一次写入。当启用缓存时,您可能会认为这两次读取来自缓存,因此唯一会进入ds的是写入。但事实并非如此。相反,我所做的唯一计费事务是只有一个数据存储读取,而没有数据存储写入。为什么?@DataNucleus:为了澄清这一点,当两个读取和一个写入不在一个事务中时,就没有缓存:所有这三个操作都进入数据存储。Zied是对的。虽然谷歌提供JPA和JDO抽象层有点有趣,但我从来没有这样做过r看到一个项目进行得非常顺利。它通常以各种恶劣的黑客行为结束,因为人们一直认为备份数据存储是RDBMS,而事实上它肯定不是。我强烈建议人们不要使用JPA或JDO库,使用原始的Google API或Objectify。
PersistenceManager pm = PMF.getManager();
Transaction tx = pm.currentTransaction();
String responsetext = "";
try {
tx.begin();
// I have extra calls to "makePersistent" because I found that relying
// on pm.close didn't always write the objects to cache, maybe that
// was only a DataNucleus 1.x issue though
Key userkey = obtainUserKeyFromCookie();
User u = pm.getObjectById(User.class, userkey);
pm.makePersistent(u); // to make sure it gets cached for next time
Key mapkey = obtainMapKeyFromQueryString();
// this is NOT a java.util.Map, just FYI
Map currentmap = pm.getObjectById(Map.class, mapkey);
Text mapData = currentmap.getMapData(); // mapData is JSON stored in the entity
Text newMapData = parseModifyAndReturn(mapData); // transform the map
currentmap.setMapData(newMapData); // mutate the Map object
pm.makePersistent(currentmap); // make sure to persist so there is a cache hit
tx.commit();
responsetext = "OK";
} catch (JDOCanRetryException jdoe) {
// log jdoe
responsetext = "RETRY";
} catch (Exception e) {
// log e
responsetext = "ERROR";
} finally {
if (tx.isActive()) {
tx.rollback();
}
pm.close();
}
resp.getWriter().println(responsetext);