我正在阅读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();
}
}
答案 0 :(得分:6)
直接从文档中删除此部分表明您完全正确需要进行交易:
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);
这需要一个事务,因为在此代码获取对象之后,但在保存修改后的对象之前,其他用户可能会更新该值。 没有事务,用户的请求将在其他用户更新之前使用计数器的值,并且保存将覆盖新值。通过事务,应用程序将被告知其他用户的更新。如果在事务期间更新实体,则事务将失败并发生异常。应用程序可以重复事务以使用新数据。
它非常接近于该分片示例正在做的事情,并且像您一样,我无法找到任何理由为什么分片计数器会有所不同。