【问题标题】:GAE update different fields of the same entityGAE更新同一实体的不同字段
【发布时间】:2010-04-07 15:16:14
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    这是你预想的事情吗?

    1. Alice 检索对象。 { a = 1, b = "Foo" }
    2. Bob 检索对象。 { a = 1, b = "Foo" }
    3. Alice 通过更改 b 来修改对象。 { a = 1, b = "酒吧" }
    4. Bob 修改了对象,通过更改 a. { a = 2, b = "Foo" }
    5. Alice 保留了她的对象副本。 { a = 1, b = "酒吧" }
    6. Bob 保留了他的对象副本。 { a = 2, b = "Foo" }

    Bob 的对象副本将覆盖数据存储中的副本,因为他保留了他的整个对象,而不仅仅是他更改的字段集。或者,一般来说,无论它们中的哪一个最后持久化,它们的整个对象都会持久化在数据存储区中。

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

    1. Alice 在事务中检索对象。 { a = 1, b = "Foo" }
    2. Bob 在事务中检索对象。 { a = 1, b = "Foo" }
    3. Alice 通过更改 b 来修改对象。 { a = 1, b = "酒吧" }
    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 = "酒吧" }
    9. Alice 保留了该对象,并且它可以工作,因为没有其他人打开事务。 { a = 2, b = "酒吧" }

    我不确定哪个用户会得到异常,但只要他们愿意在看到异常时重试,他们最终都可以对对象进行更改并持久化。

    这叫Optimistic Locking

    【讨论】:

    • 您可能需要重新考虑使用“id”和“name” - 它们都是 App Engine 中的保留术语,不可修改。
    • 优秀推荐。重命名为 a 和 b。我还将用户 A 和用户 B 重命名为 Alice 和 Bob,以避免更多的混淆。
    【解决方案2】:

    感谢您的回答。 遗憾的是 makePersistence() 实现是将整个对象写入数据存储区,而不仅仅是写入已更改的字段。 这一事实实际上迫使 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();
        }
    }
    

    或者:

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

    【讨论】:

    • 他们也没有处理OverQuotaExceptions 或CapabilityDisabledExceptions,但这可能只是因为它会使示例过于复杂而无用。事实是,大多数应用程序永远不会有这些问题,因为大多数应用程序不会被大量使用,以至于同时编辑是一个问题。有些应用程序甚至根本不编辑现有实体,只创建和显示它们。您的应用似乎需要将其考虑在内,因此您已经对其进行了调查,这是一件好事。
    • 另外,通过讨论问题来回答您自己的问题通常不是一个好主意。这就是 cmets 的用途,无论是在您的问题上还是在答案上。
    猜你喜欢
    • 2019-11-18
    • 2021-12-21
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多