没关系:问题出在客户端,显然 google chrome 在缓存 get 请求的结果方面比我预期的更激进。使用 firefox 进行测试会产生预期的结果。我将投票赞成删除这个问题。
我有 2 个模型,用户和项目。用户有一个“credits”属性,表示用户有多少钱。项目有一个“所有者”属性,如果它不拥有,则为“无”,如果拥有,则为用户的 id。我正在尝试将以下步骤放入事务中:
- 通过 id 获取用户(不是祖先查询)
- 通过 id 获取项目(不是祖先查询)
- 如果该项目已被拥有,则中止。
- 如果用户没有足够的积分,则中止。
- 通过项目的价格减少用户的信用。
- 将项目的“所有者”属性设置为用户的 ID。
如果一切顺利,我会向客户表明他们的购买已经完成,在 @ndb.transactional 装饰函数之外。
为了测试,我快速敲击这个功能(模拟快速按下购买按钮)。作为回应,我得到了几个迹象表明我的购买已经完成,但我预计只有一个这样的迹象。我的期望是第一个事务会通过,其他事务会在第 3 步失败。如果他们在第 3 步没有失败,那么我假设它们是一起开始的,但只有一个事务会在第 3 步结束时通过测试检查更新时间并抛出异常的事务。我的两个假设似乎都是错误的。
值得注意的是,我的交易“有效”,因为我不会超支信用。我只是多次告诉客户他们进行了相同的购买,因为我没有例外。
此外,第三步的中止确实会在短暂延迟后开始触发,但还不足以捕获最初的垃圾邮件。
class BuyItem(webapp2.RequestHandler):
@ndb.transactional(xg=True)
def buyItemTransaction(self, user_id, item_id):
user = User.get_by_id(user_id)
item = Item.get_by_id(item_id)
if item.owner_id is not None:
return dict(result='error', message='Item already owned.')
if user.credits < 5000:
return dict(result='error', message='Not enough credits.')
user.credits -= 5000
user.put()
item.owner_id = user_id
item.put()
return dict(result='success', message='You bought the item.')
def get(self):
user_id = users.get_current_user().user_id()
item_id = self.request.get('item_id')
try:
response = self.buyItemTransaction(user_id, item_id)
catch TransactionFailedError, e:
# Transaction went through previously, so send no response.
return
self.response.out.write(json.dumps(response))
我的误解是什么?