简体   繁体   English

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

[英]Inconsistent Fetch From Google App Engine Datastore

I have an application deployed in Google app engine. 我在Google应用引擎中部署了一个应用程序。 I am getting inconsistent data when i fetch an entity by id immediately after updating that entity. 当我在更新该实体后立即通过id获取实体时,我得到的数据不一致。 I'm using JDO 3.0 to access the app engine datastore. 我正在使用JDO 3.0来访问应用程序引擎数据存储区。

I have an entity Employee 我有一个实体员工

@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() * */

}

Initially, when I create an employee I do not set the fields salary and email. 最初,当我创建员工时,我没有设置字段salary和email。

I update the Employee entity to add salary and email later. 我更新Employee实体以便稍后添加工资和电子邮件。 The update works fine and the data gets persisted into the appengine datastore. 更新工作正常,数据将持久保存到appengine数据存储区中。 However, when i immediately try to fetch the same employee entity by id, I sometimes get the stale data, where salary and email are null. 但是,当我立即尝试通过id获取相同的员工实体时,我有时会得到过时的数据,其中薪水和电子邮件为空。 The code I use to create and to fetch the employee entity is given below. 我用于创建和获取员工实体的代码如下所示。

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

I have set the number of idle instances to 5 and there are around 8 instances running at a time. 我已将空闲实例数设置为5,并且一次运行大约8个实例。 When I checked the logs of various instances this is what I found. 当我检查各种实例的日志时,这就是我找到的。 在此输入图像描述

Why do i get stale data when the request is served by certain instances. 当某些实例提供请求时,为什么我会收到陈旧数据。 I can assure that, if the fetch request is handled by the instance which initially handled the update request I always get the updated data. 我可以保证,如果获取请求由最初处理更新请求的实例处理,我总是得到更新的数据。 But when other instances handle the fetch request stale data may be returned. 但是当其他实例处理获取请求时,可能会返回过时数据。 I have explicitly set the datastore read consistency to strong in my jdoconfig.xml. 我在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>

If you are using the the High Replication datastore, setting the read policy does not ensure that all reads are strongly consistent, those only work for ancestor queries. 如果您使用的是High Replication数据存储区,则设置读取策略并不能确保所有读取都非常一致,这些只能用于祖先查询。 From the documentation; 从文件;

The API also allows you to explicitly set a strong consistency policy, but this setting will have no practical effect, since non-ancestor queries are always eventually consistent regardless of policy. 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 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

Please have a look at the document about Structuring Data for Strong Consistency , the preferred approach is to the caching layer to serve the data. 请查看有关为强一致性构建数据的文档,首选方法是为缓存层提供数据。

I noticed that you are using get by ID, not sure, but "get by key" is supposed to be strongly consistent even for HR datastore ( reference ), can you try changing this to query based on the key? 我注意到你正在使用get by ID,不确定,但是“按键获取”应该是非常一致的,即使对于HR数据存储区( 参考 ),你能尝试根据密钥进行更改吗? Key is built using the id and the entity kind and ancestry. 密钥是使用id和实体种类和祖先构建的。

I have a suggestion, however you're not gonna like that: use low level API exclusively and forget about JDO / JPA when using GAE. 我有一个建议,但你不会那样:使用低级API并在使用GAE时忘记JDO / JPA。

Just like @asp said, get by ID is supposed to be strongly consistent, however GAE JDO plugin seems bugged to me. 就像@asp所说的那样,获取ID应该是非常一致的,但是GAE JDO插件似乎对我来说是错误的。 Unfortunately, migrating to JPA was no help in my case as well (more here: JDO transactions + many GAE instances = overriding data ). 不幸的是,迁移到JPA在我的情况下也没有帮助(更多这里: JDO事务+许多GAE实例=覆盖数据 )。 Also, if I annotate any class as @PersistenceAware, Eclipse goes crazy, and enhances the classes in infinite loop. 此外,如果我将任何类注释为@PersistenceAware,Eclipse会变得疯狂,并在无限循环中增强类。 Also, I had a lot of problems when using @PersistenceCapable class with embedded class and caching (without caching it worked fine). 另外,当使用带有嵌入式类和缓存的@PersistenceCapable类时,我遇到了很多问题(没有缓存它工作正常)。

Well, the point is, I think it will be way faster with low level API - you know exactly what is happening and it seems to work as intended. 好吧,重点是,我认为使用低级API会更快 - 你确切知道发生了什么,它似乎按预期工作。 You can treat Entity like a Map, with a little bit of self-written wrapping code it seems like a quite interesting alternative. 您可以将实体视为地图,只需一点自编包装代码,它就像是一个非常有趣的选择。 I run some tests and with low level API I passed them no problem, while passing it with JDO/JPA was not possible. 我运行了一些测试,并且使用低级API我没有通过它们,而传递它与JDO / JPA是不可能的。 I am in the middle of migrating my whole application from JDO to low level API. 我正在将整个应用程序从JDO迁移到低级API。 It is time-consuming, but less than waiting indefinitely for some magical solution or bugfix from GAE team. 这是耗时的,但不能无限期地等待来自GAE团队的一些神奇的解决方案或错误修正。

Also, while writting GAE JDO I felt... alone. 此外,在撰写GAE JDO时,我觉得......独自一人。 If you have a problem with java, or even android, a thousand of other people already had this problem, asked about it on stackoverflow and got tons of valid solutions. 如果你有java,甚至是android的问题,其他数千人已经遇到过这个问题,在stackoverflow上询问它并获得了大量有效的解决方案。 Here you are all by yourself, so use as low level API as possible and you'll be sure whats happening. 在这里,你们都是独立的,所以尽量使用低级API,你会发现它们发生了什么。 Even though migration seems scary as hell and time-consuming, I think you'll waste less time migrating to low level API than dealing with GAE JDO/JPA. 尽管迁移看起来很可怕,但是我认为与使用GAE JDO / JPA相比,您将浪费更少的时间迁移到低级API。 I don't write it to pinch the team that develops GAE JDO/JPA or to offend them, I'm sure they do their best. 我不会写它来捏造开发GAE JDO / JPA的团队或冒犯他们,我相信他们会尽力而为。 But: 但:

  1. There is not so many people using GAE comparing to, lets say, Android or Java in general 使用GAE的人并不多,比如Android或Java

  2. Using GAE JDO/JPA with multiple server instances is not that simple and straightforward as you would think. 将GAE JDO / JPA与多个服务器实例一起使用并不像您想象的那么简单直接。 The developer like me wants to have his job done ASAP, see some example, read a bit of documentation - not to study it all in detail, read a short tutorial and the developer has a problem, he would like to share it on stackoverflow and get quick help. 像我这样的开发人员希望尽快完成他的工作,看一些例子,阅读一些文档 - 不要详细研究它,阅读简短的教程并且开发人员有问题,他想在stackoverflow上分享它快速帮助。 Its easy to get help if you do something wrong on Android, no matter if its complicated or its easy mistake. 如果你在Android上做错了什么很容易得到帮助,无论它是复杂还是容易出错。 Its not that easy with GAE JDO/JPA. 使用GAE JDO / JPA并不容易。 I've spent much more time on GAE JDO articles, tutorials and documentation than I would like to, and I failed to do what I wanted even though it seemed pretty basic. 我花了更多的时间在GAE JDO文章,教程和文档上,而不是我想要的,虽然它看起来非常基本,但我做不到我想要的。 If I just used low level API and not tried to take a shortcut with JDO (yeah, I thought JDO will save my time), it would be much, much quicker. 如果我只是使用低级API并且没有尝试使用JDO的快捷方式(是的,我认为JDO将节省我的时间),它会更快,更快。

  3. Google is focused on Python GAE much more than Java. Google比Java更关注Python GAE。 In many articles that are targeted for all the audiences, there is Python code and hints exclusively, quick examples here: http://googlecloudplatform.blogspot.com/2013/12/best-practices-for-app-engine-memcache.html or here: https://cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/ . 在许多针对所有受众的文章中,有专门的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/ I've noticed that even before starting development, but I wanted to share some code with my Android client, so I chose Java. 我注意到即使在开始开发之前,我想与我的Android客户端共享一些代码,所以我选择了Java。 Even though I have solid Java background and even that I do share some code now, if I could go back in time and choose again, I'd choose Python now. 即使我有扎实的Java背景,甚至我现在也分享一些代码,如果我能回到过去并再次选择,我现在就选择Python。

Thats why I think its best to use only the most basic methods to access and manipulate data. 这就是为什么我认为最好只使用最基本的方法来访问和操作数据。

Good luck, I wish you all the best. 祝你好运,祝你万事如意。

Add @Cacheable(value = "false") in entity class. 在实体类中添加@Cacheable(value = "false") This issue will be resolved. 这个问题将得到解决。

Above issue mainly due to the JDO cache. 以上问题主要是由于JDO缓存。 So if we disable the cache, JDO will read the data from datastore. 因此,如果我们禁用缓存,JDO将从数据存储中读取数据。

Or you can disable L2 cache in jdoconfig.xml. 或者您可以在jdoconfig.xml中禁用L2缓存。

Ref Link : http://www.datanucleus.org/products/accessplatform_3_0/jdo/cache.html 参考链接: http//www.datanucleus.org/products/accessplatform_3_0/jdo/cache.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM