从Google App Engine数据存储区获取的内容不一致

Har*_*kar 7 java google-app-engine jdo datanucleus google-cloud-datastore

我在Google应用引擎中部署了一个应用程序.当我在更新该实体后立即通过id获取实体时,我得到的数据不一致.我正在使用JDO 3.0来访问应用程序引擎数据存储区.

我有一个实体员工

@PersistenceCapable(detachable = "true")
public class Employee implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -8319851654750418424L;
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY, defaultFetchGroup = "true")
    @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
    private String id;
    @Persistent(defaultFetchGroup = "true")
    private String name;
    @Persistent(defaultFetchGroup = "true")
    private String designation;    
    @Persistent(defaultFetchGroup = "true")
    private Date dateOfJoin;    
    @Persistent(defaultFetchGroup = "true")
    private String email;
    @Persistent(defaultFetchGroup = "true")
    private Integer age;
    @Persistent(defaultFetchGroup = "true")
    private Double salary;
    @Persistent(defaultFetchGroup = "true")
    private HashMap<String, String> experience;
    @Persistent(defaultFetchGroup = "true")
    private List<Address> address;


    /**
      * Setters and getters, toString() * */

}
Run Code Online (Sandbox Code Playgroud)

最初,当我创建员工时,我没有设置字段salary和email.

我更新Employee实体以便稍后添加工资和电子邮件.更新工作正常,数据将持久保存到appengine数据存储区中.但是,当我立即尝试通过id获取相同的员工实体时,我有时会得到过时的数据,其中薪水和电子邮件为空.我用于创建和获取员工实体的代码如下所示.

    public Employee create(Employee object) {
        Employee persObj = null;
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Transaction tx = null;
        try {
            tx = pm.currentTransaction();
            tx.begin();

            persObj = pm.makePersistent(object);

            tx.commit();
        } finally {

            if ((tx != null) && tx.isActive()) {
                tx.rollback();
            }

            pm.close();
        }

        return persObj;
    }


    public Employee findById(Serializable id) {

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            Employee e = pm.getObjectById(Employee.class, id);

            System.out.println("INSIDE EMPLOYEE DAO : " + e.toString());
            return e;

        } finally {

            pm.close();

        }
    }


    public void update(Employee object) {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        Transaction tx = null;
        try {
            tx = pm.currentTransaction();
            tx.begin();
            Employee e = pm.getObjectById(object.getClass(), object.getId());
            e.setName(object.getName());
            e.setDesignation(object.getDesignation());
            e.setDateOfJoin(object.getDateOfJoin());
            e.setEmail(object.getEmail());
            e.setAge(object.getAge());
        e.setSalary(object.getSalary());
            tx.commit();
        } finally {
            if (tx != null && tx.isActive()) {
                tx.rollback();
            }

            pm.close();
        }
    }
Run Code Online (Sandbox Code Playgroud)

我已将空闲实例数设置为5,并且一次运行大约8个实例.当我检查各种实例的日志时,这就是我找到的. 在此输入图像描述

当某些实例提供请求时,为什么我会收到陈旧数据.我可以保证,如果获取请求由最初处理更新请求的实例处理,我总是得到更新的数据.但是当其他实例处理获取请求时,可能会返回过时数据.我在jdoconfig.xml中明确地将数据存储读取一致性设置为strong.

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig_3_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig http://java.sun.com/xml/ns/jdo/jdoconfig_3_0.xsd">

   <persistence-manager-factory name="transactions-optional">
       <property name="javax.jdo.PersistenceManagerFactoryClass"
           value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
       <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
       <property name="javax.jdo.option.NontransactionalRead" value="true"/>
       <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
       <property name="javax.jdo.option.RetainValues" value="true"/>
       <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
       <property name="datanucleus.appengine.singletonPMFForName" value="true"/>
       <property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true"/>
       <property name="datanucleus.query.jdoql.allowAll" value="true"/>      
       <property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />

   </persistence-manager-factory>
</jdoconfig>
Run Code Online (Sandbox Code Playgroud)

asp*_*asp 5

如果您使用的是High Replication数据存储区,则设置读取策略并不能确保所有读取都非常一致,这些只能用于祖先查询.从文件;

API还允许您显式设置强一致性策略,但此设置不会产生任何实际效果,因为无论策略如何,非祖先查询始终始终保持一致.

https://cloud.google.com/appengine/docs/java/datastore/queries#Java_Data_consistency https://cloud.google.com/appengine/docs/java/datastore/jdo/overview-dn2#Setting_the_Datastore_Read_Policy_and_Call_Deadline

请查看有关为强一致性构建数据的文档,首选方法是为缓存层提供数据.

我注意到你正在使用get by ID,不确定,但是"按键获取"应该是非常一致的,即使对于HR数据存储区(参考),你能尝试根据密钥进行更改吗?密钥是使用id和实体种类和祖先构建的.


use*_*896 4

我有一个建议,但你不会喜欢这样的:在使用 GAE 时只使用低级 API,忘记 JDO / JPA。

就像@asp所说,通过ID获取应该是高度一致的,但是GAE JDO插件对我来说似乎有问题。不幸的是,迁移到 JPA 对我来说也没有帮助(更多信息:JDO 事务 + 许多 GAE 实例 = 覆盖数据)。另外,如果我将任何类注释为 @PersistenceAware,Eclipse 就会变得疯狂,并在无限循环中增强这些类。另外,当我将 @PersistenceCapable 类与嵌入式类和缓存一起使用时,我遇到了很多问题(没有缓存它工作得很好)。

好吧,重点是,我认为使用低级 API 会更快 - 你确切地知道发生了什么,并且它似乎按预期工作。您可以将实体视为映射,并使用一些自己编写的包装代码,这似乎是一个非常有趣的选择。我运行了一些测试,使用低级 API 时我没有问题地通过了它们,而使用 JDO/JPA 则不可能通过它们。我正在将整个应用程序从 JDO 迁移到低级 API。这很耗时,但比无限期地等待 GAE 团队提供一些神奇的解决方案或错误修复要少。

另外,在写 GAE JDO 时,我感到……孤独。如果你遇到了 java 甚至 android 的问题,那么其他上千人已经遇到了这个问题,他们在 stackoverflow 上询问并得到了大量有效的解决方案。在这里,您只能独自一人,因此请尽可能使用低级 API,这样您就可以确定发生了什么。尽管迁移看起来非常可怕且耗时,但我认为与处理 GAE JDO/JPA 相比,迁移到低级 API 所浪费的时间会更少。我写这篇文章并不是为了捏造 GAE JDO/JPA 开发团队或冒犯他们,我确信他们会尽力而为。但:

  1. 一般来说,与 Android 或 Java 相比,使用 GAE 的人并不多

  2. 将 GAE JDO/JPA 与多个服务器实例结合使用并不像您想象的那么简单和直接。像我这样的开发人员希望尽快完成他的工作,查看一些示例,阅读一些文档 - 不要详细研究所有内容,阅读一个简短的教程,并且开发人员遇到了问题,他想在 stackoverflow 上分享它并且获得快速帮助。如果您在 Android 上犯了错误,无论是复杂的错误还是简单的错误,都可以轻松获得帮助。使用 GAE JDO/JPA 并不那么容易。我在 GAE JDO 文章、教程和文档上花费的时间比我想要的要多得多,而且我没能做到我想做的事,尽管它看起来很基础。如果我只使用低级 API,而不尝试使用 JDO 走捷径(是的,我认为 JDO 会节省我的时间),那么速度会快得多。

  3. 谷歌对 Python GAE 的关注远多于 Java。在许多针对所有受众的文章中,都有专门的 Python 代码和提示,这里是快速示例:http://googlecloudplatform.blogspot.com/2013/12/best-practices-for-app-engine-memcache.html或在这里: https: //cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/。我注意到,即使在开始开发之前,我也想与我的 Android 客户端共享一些代码,所以我选择了 Java。尽管我有扎实的 Java 背景,而且我现在确实分享了一些代码,但如果我可以回到过去重新选择,我现在会选择 Python。

这就是为什么我认为最好只使用最基本的方法来访问和操作数据。

祝你好运,祝你一切顺利。