GAE更新同一实体的不同字段

时间:2010-04-07 15:16:15

标签: google-app-engine concurrency transactions google-cloud-datastore optimistic-locking

UserA和UserB分别同时更改objectA.filedA objectA.filedB。 因为它们不会改变相同的字段,所以可能会认为没有重叠。 真的吗? 或者pm.makePersistnace()的实现实际上覆盖了整个对象...... 很高兴知道...

2 个答案:

答案 0 :(得分:1)

这是你想象的那样吗?

  1. Alice检索对象。 {a = 1,b =“Foo”}
  2. Bob检索对象。 {a = 1,b =“Foo”}
  3. Alice通过更改b来修改对象。 {a = 1,b =“Bar”}
  4. Bob通过更改a来修改对象。 {a = 2,b =“Foo”}
  5. 爱丽丝坚持她的副本。 {a = 1,b =“Bar”}
  6. 鲍勃坚持他的副本。 {a = 2,b =“Foo”}
  7. 鲍勃的对象副本将覆盖数据存储区中的副本,因为他持久保存了他的整个对象,而不仅仅是他的一组更改字段。或者,通常情况下,无论哪个持续存在,他们的整个对象都会保留在数据存储区中。

    您可以通过在事务中运行每个get-set-and-persist操作来解决此问题。 App Engine事务不会锁定整个对象在本地检索或修改,它们只是阻止其他用户持久化。所以:

    1. Alice在事务中检索对象。 {a = 1,b =“Foo”}
    2. Bob在事务中检索对象。 {a = 1,b =“Foo”}
    3. Alice通过更改b来修改对象。 {a = 1,b =“Bar”}
    4. Bob通过更改a来修改对象。 {a = 2,b =“Foo”}
    5. Alice 尝试来持久保存对象,但不能,因为Bob在事务中打开它。将抛出异常,Alice将通过结束她的交易和重试来捕获......
    6. Bob持有该对象,没有任何问题,因为Alice完成了他的交易{a = 2,b =“Foo”}
    7. Alice通过再次检索重试她的交易。 {a = 2,b =“Foo”}
    8. Alice通过更改b来修改对象。 {a = 2,b =“Bar”}
    9. Alice坚持使用该对象,并且它可以工作,因为没有其他人打开了一个事务。 {a = 2,b =“Bar”}
    10. 我不确定哪个用户会获得异常,但只要他们愿意在他们看到它时重试,他们都能够对对象做出更改并最终坚持下去。

      这称为Optimistic Locking

答案 1 :(得分:0)

感谢您的回答。 遗憾的是,makePersistence()实现是将WHOLE对象写入数据存储区而不仅仅是已更改的字段。 这个事实实际上迫使GAE中的任何共享对象更新使用事务作为规则。 更进一步 - 在这种情况下,您必须实现“重试机制”作为事务中的异常。

所以...更新GAE中的任何共享对象应该总是有这些额外的东西:

  • 在交易中执行
  • 实施重试机制

Google网站上的大多数示例实际上都没有考虑到这一点。好像他们假设大多数应用程序不会使用共享对象

例如(http://code.google.com/appengine/docs/java/datastore/creatinggettinganddeletingdata.html):

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

OR:

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}