从Google App引擎数据存储中获取不一致 [英] Inconsistent Fetch From Google App Engine Datastore

查看:116
本文介绍了从Google App引擎数据存储中获取不一致的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我有一个实体Employee

  @PersistenceCapable(detachable = true)
public class Employee实现Serializable {

/ **
*
* /
private static final long serialVersionUID = -8319851654750418424L;
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY,defaultFetchGroup =true)
@Extension(vendorName =datanucleus,key =gae.encoded-pk,value = true)
私人字符串ID;
@Persistent(defaultFetchGroup =true)
私人字符串名称;
@Persistent(defaultFetchGroup =true)
私有字符串指定;
@Persistent(defaultFetchGroup =true)
私人日期dateOfJoin;
@Persistent(defaultFetchGroup =true)
私人字符串电子邮件;
@Persistent(defaultFetchGroup =true)
私人整数年龄;
@Persistent(defaultFetchGroup =true)
私人双重薪水;
@Persistent(defaultFetchGroup =true)
private HashMap< String,String>经验;
@Persistent(defaultFetchGroup =true)
私人列表<地址>地址;

$ b / **
* Setter和getters,toString()* * /

}

最初,当我创建员工时,我没有设置领域工资和电子邮件。

我更新Employee实体以后添加薪水和电子邮件。更新工作正常,数据被保存到appengine数据存储中。但是,当我立即尝试通过ID获取同一雇员实体时,我有时会收到陈旧的数据,其中工资和电子邮件为空。下面给出了我用来创建和获取员工实体的代码。

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

persObj = pm.makePersistent(object);

tx.commit(); $(
)finally {

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

pm.close();
}

返回persObj;


$ b public Employee findById(Serializable id){

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

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

我已经将空闲实例的数量设置为5,一次运行大约8个实例。当我查看各种实例的日志时,这是我发现的。



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

 <?xml version =1.0encoding =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.ConnectionURLvalue =appengine/>
< property name =javax.jdo.option.NontransactionalReadvalue =true/>
< property name =javax.jdo.option.NontransactionalWritevalue =true/>
< property name =javax.jdo.option.RetainValuesvalue =true/>
< property name =datanucleus.appengine.autoCreateDatastoreTxnsvalue =true/>
< property name =datanucleus.appengine.singletonPMFForNamevalue =true/>
< property name =datanucleus.appengine.datastoreEnableXGTransactionsvalue =true/>
< property name =datanucleus.query.jdoql.allowAllvalue =true/>
< property name =datanucleus.appengine.datastoreReadConsistencyvalue =STRONG/>

< / persistence-manager-factory>
< / jdoconfig>


解决方案

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

就像@asp说的那样,通过ID获取应该是非常一致的,然而GAE JDO插件似乎对我有所蛊惑。不幸的是,迁移到JPA在我的情况也没有帮助(更多在这里: JDO交易+许多GAE实例=覆盖数据)。此外,如果我将@PersistenceAware注释为任何类,那么Eclipse会变得疯狂,并且会在无限循环中增强类。另外,当使用带有嵌入类和缓存的@PersistenceCapable类时,我遇到了很多问题(无需缓存就可以正常工作)。

好的,关键是,我认为使用低级别的API会更快 - 您确切知道发生了什么,它似乎按预期工作。你可以像Entity一样对待Map,有一点自编写的包装代码,它似乎是一个非常有趣的选择。我运行一些测试和低级别的API,我通过它们没有问题,而传递它与JDO / JPA是不可能的。我正在将我的整个应用程序从JDO迁移到低级API。这是非常耗时的,但比无限期地等待GAE团队提供的一些神奇的解决方案或错误修正。

另外,在写GAE JDO的时候,我觉得......独自一人。如果你在使用java或者android时遇到问题,其他数千人已经遇到了这个问题,在stackoverflow上询问了它并得到了大量有效的解决方案。在这里你全部靠自己,所以尽可能使用低级别的API,你会确定发生了什么。尽管迁移看起来非常可怕,但是我认为与使用GAE JDO / JPA相比,您将浪费更少的时间迁移到低级API。我不写这个文件夹来开发GAE JDO / JPA的团队,或者冒犯他们,我相信他们会尽力而为。但是:


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

  2. 使用GAE对于多个服务器实例,使用GAO JDO / JPA并不像您想象的那么简单直接。像我这样的开发人员希望尽快完成他的工作,看一些例子,阅读一些文档 - 不是详细研究它,阅读一个简短的教程和开发人员有问题,他想分享它在stackoverflow和快速获得帮助。如果你在Android上做了错误的事情,它很容易得到帮助,无论它是复杂还是容易出错。 GAE JDO / JPA并不那么容易。我花了很多时间在GAE JDO文章,教程和文档上,而且我没有做到我想做的事情,即使它看起来很基本。如果我只是使用低级别的API,并且不尝试使用JDO的快捷方式(是的,我认为JDO会节省我的时间),它会快得多。


  3. http://googlecloudplatform.blogspot.com/2013/12/best-practices-for-app-engine-memcache.html 或这里: https://cloud.google。 com / developers / articles / balanced-strong-and-eventual-consistency-with-google-cloud-datastore / 。我注意到,即使在开始开发之前,我想与我的Android客户端分享一些代码,所以我选择了Java。尽管我拥有坚实的Java背景,并且即使我现在也分享了一些代码,但如果我能够及时回顾并再次选择,我现在会选择Python。
  4. >

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



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


    I have an application deployed in Google app engine. I am getting inconsistent data when i fetch an entity by id immediately after updating that entity. I'm using JDO 3.0 to access the app engine datastore.

    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.

    I update the Employee entity to add salary and email later. The update works fine and the data gets persisted into the appengine datastore. 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. 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. 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.

    <?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>
    

    解决方案

    I have a suggestion, however you're not gonna like that: use low level API exclusively and forget about JDO / JPA when using GAE.

    Just like @asp said, get by ID is supposed to be strongly consistent, however GAE JDO plugin seems bugged to me. Unfortunately, migrating to JPA was no help in my case as well (more here: JDO transactions + many GAE instances = overriding data). Also, if I annotate any class as @PersistenceAware, Eclipse goes crazy, and enhances the classes in infinite loop. Also, I had a lot of problems when using @PersistenceCapable class with embedded class and caching (without caching it worked fine).

    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. 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. I am in the middle of migrating my whole application from JDO to low level API. It is time-consuming, but less than waiting indefinitely for some magical solution or bugfix from GAE team.

    Also, while writting GAE JDO I felt... alone. 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. Here you are all by yourself, so use as low level API as possible and you'll be sure whats happening. 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. 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. But:

    1. There is not so many people using GAE comparing to, lets say, Android or Java in general

    2. Using GAE JDO/JPA with multiple server instances is not that simple and straightforward as you would think. 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. Its easy to get help if you do something wrong on Android, no matter if its complicated or its easy mistake. Its not that easy with 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. 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.

    3. Google is focused on Python GAE much more than Java. 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/ . I've noticed that even before starting development, but I wanted to share some code with my Android client, so I chose 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.

    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.

    这篇关于从Google App引擎数据存储中获取不一致的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆