Google App Engine原子部分?

时间:2010-03-31 21:36:41

标签: google-app-engine servlets

  • 假设您从数据存储中检索一组记录(例如:select * from MyClass,其中reserved ='false')。

  • 我如何确保其他用户未设置保留仍为假?

我查看了交易文档,并对google的解决方案感到震惊,该解决方案是捕获异常并在循环中重试。

我缺少的任何解决方案 - 很难相信在这种环境中无法进行原子操作。

(顺便说一句 - 我可以在servlet中使用'syncronize',但我认为它无效,因为没有办法确保只有一个servlet对象的实例,不是吗?同样适用于静态变量解决方案)< / p>

关于如何解决的任何想法?

(这是谷歌解决方案:

http://code.google.com/appengine/docs/java/datastore/transactions.html#Entity_Groups

看看:

Key k = KeyFactory.createKey("Employee", "k12345");
Employee e = pm.getObjectById(Employee.class, k);
e.counter += 1;
pm.makePersistent(e);

'这需要一个事务,因为在此代码获取对象之后,但在保存修改后的对象之前,该值可能会被另一个用户更新。如果没有事务,用户的请求将在其他用户更新之前使用计数器的值,并且保存将覆盖新值。通过事务,应用程序将被告知其他用户的更新。如果在事务期间更新实体,则事务将失败并发生异常。应用程序可以重复该事务以使用新数据'

可怕的解决方案,不是吗?

2 个答案:

答案 0 :(得分:4)

你是对的,你不能使用同步或静态变量。

您在App Engine环境中无法进行原子操作是不正确的。 (看看原子意味着什么here)当你做一个事务时,它是原子的 - 要么一切都发生了,要么什么都没发生。听起来你想要的是某种全局锁定机制。在RDBMS世界中,可能类似于“select for update”或将事务隔离级别设置为序列化事务。这些类型的选项中的任何一种都不具有可扩展性。或者如您所说,它们都是可怕的解决方案:)

如果你真的想要在app引擎中进行全局锁定,那么你可以做到这一点,但它会很丑陋并严重影响可扩展性。您需要做的就是创建某种CurrentUser实体,您可以在其中存储具有全局锁定的当前用户的用户名。在让用户执行任何操作之前,您需要首先检查没有用户已经列为CurrentUser,然后将该用户的密钥写入CurrentUser实体。检查和写入必须在交易中。这样,只有一个用户将成为“当前”,因此具有全局锁定。

答案 1 :(得分:0)

你的意思是这样的:

   public void func(Data data2) {

        String query = "select from " + objectA.class.getName()
                + " where reserved == false";
        List<objectA> Table = (List<objectA>) pm.newQuery(
                query).execute();

        for (objectA row : Table)
        {
            Data data1 = row.getData1();
            row.setWeight(JUtils.CalcWeight(data1, data2));
        }

        Collections.sort(Table, new objectA.SortByWeight());

        int retries = 0;
        int NUM_RETRIES = 10;
        for (int i = 0; i < Table.size() ; i++)
        {   
            retries++;      
            pm.currentTransaction().begin(); //    <---- BEGIN
            ObjectA obj = pm.getObjectById(Table.get(i).class, Table.get(i).getKey());
            if (obj .getReserved() == false)   // <--- CHECK if still reserved
                obj.setReserved(true);
            else
                break;      

            try
            {
                pm.currentTransaction().commit();
                break;
            } 
            catch (JDOCanRetryException ex)
            {
                if (j == (NUM_RETRIES - 1))
                {
                    throw ex;
                }
                i--; //so we retry again on the same object
            }
        }
        }