【问题标题】:Java Google Appengine sharded counters without transactions没有事务的 Java Google Appengine 分片计数器
【发布时间】:2011-04-16 11:41:57
【问题描述】:

我正在查看 Java 中的 Sharded Counters 示例: http://code.google.com/appengine/articles/sharding_counters.html

我有一个关于增量方法的实现的问题。在 python 中,它显式地包装了 get() 并在事务中递增。在 Java 示例中,它只是检索并设置它。我不确定我是否完全理解数据存储和事务,但似乎关键更新部分应该包含在数据存储事务中。我错过了什么吗?

原码:

  public void increment() {
    PersistenceManager pm = PMF.get().getPersistenceManager();

    Random generator = new Random();
    int shardNum = generator.nextInt(NUM_SHARDS);

    try {
      Query shardQuery = pm.newQuery(SimpleCounterShard.class);
      shardQuery.setFilter("shardNumber == numParam");
      shardQuery.declareParameters("int numParam");

      List<SimpleCounterShard> shards =
          (List<SimpleCounterShard>) shardQuery.execute(shardNum);
      SimpleCounterShard shard;

      // If the shard with the passed shard number exists, increment its count
      // by 1. Otherwise, create a new shard object, set its count to 1, and
      // persist it.
      if (shards != null && !shards.isEmpty()) {
        shard = shards.get(0);
        shard.setCount(shard.getCount() + 1);
      } else {
        shard = new SimpleCounterShard();
        shard.setShardNumber(shardNum);
        shard.setCount(1);
      }

      pm.makePersistent(shard);
    } finally {
      pm.close();
    }
  }
}

事务代码(我相信您需要在事务中运行它以保证并发事务下的正确性?):

public void increment() { 
    PersistenceManager pm = PMF.get().getPersistenceManager(); 
    Random generator = new Random(); 
    int shardNum = generator.nextInt(NUM_SHARDS); 
    try { 
      Query shardQuery = pm.newQuery(SimpleCounterShard.class); 
      shardQuery.setFilter("shardNumber == numParam"); 
      shardQuery.declareParameters("int numParam"); 
      List<SimpleCounterShard> shards = 
          (List<SimpleCounterShard>) shardQuery.execute(shardNum); 
      SimpleCounterShard shard; 
      // If the shard with the passed shard number exists, increment its count 
      // by 1. Otherwise, create a new shard object, set its count to 1, and 
      // persist it. 
      if (shards != null && !shards.isEmpty()) { 
            Transaction tx = pm.currentTransaction(); 
        try { 
            tx.begin(); 
            //I believe in a transaction objects need to be loaded by ID (can't use the outside queried entity) 
             Key shardKey = KeyFactory.Builder(SimpleCounterShard.class.getSimpleName(), shards.get(0).getID()) 
            shard =  pm.getObjectById(SimpleCounterShard.class, shardKey); 
            shard.setCount(shard.getCount() + 1); 
            tx.commit(); 
        } finally { 
            if (tx.isActive()) { 
                tx.rollback(); 
            } 
        } 
      } else { 
        shard = new SimpleCounterShard(); 
        shard.setShardNumber(shardNum); 
        shard.setCount(1); 
      } 
      pm.makePersistent(shard); 
    } finally { 
      pm.close(); 
    } 
  } 

【问题讨论】:

    标签: java google-app-engine transactions concurrency


    【解决方案1】:

    这部分直接来自文档表明您完全正确地需要交易:

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

    此示例演示了事务的一种用法:使用相对于当前值的新属性值更新实体。

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

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

    它与分片示例所做的非常接近,并且像您一样,我无法找到分片计数器不同的任何原因。

    【讨论】:

    • 我在 Appengine 问题跟踪器上创建了一个未解决的问题:code.google.com/p/googleappengine/issues/detail?id=3778
    • @Dougnukem - 非常好。如果可以的话,我会再次支持你的问题,因为我会努力将反馈反馈给项目以改进它。我赞成你的其他问题之一:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多