気にしないでください:問題はクライアント側にありました.どうやら Google Chrome は、get リクエストの結果をキャッシュすることに関して、私が予想したよりも積極的です. firefox でテストすると、期待どおりの結果が得られます。この質問の削除に投票します。
ユーザーとアイテムの2つのモデルがあります。ユーザーには、ユーザーが持っている金額を示す「クレジット」プロパティがあります。アイテムには、所有されていない場合は「なし」である「所有者」プロパティがあり、所有されている場合はユーザーの ID があります。次の手順をトランザクションに入れようとしています。
- ID でユーザーを取得します (祖先クエリではありません)
- ID で項目を取得します (祖先クエリではありません)
- アイテムが既に所有されている場合は中止します。
- ユーザーが十分なクレジットを持っていない場合は中止します。
- ユーザーのクレジットをアイテムの価格だけ減らします。
- item の 'owner' プロパティをユーザーの ID に設定します。
すべてがうまくいった場合は、@ndb.transactional 装飾関数の外で、購入が完了したことをクライアントに示します。
テストするために、この関数をすばやく叩きます (購入ボタンをすばやく押すことをシミュレートします)。それに応じて、購入が完了したことを示すいくつかの兆候を受け取りますが、そのような兆候は 1 つしか期待できません。私の予想では、最初のトランザクションは成功し、他のトランザクションはステップ 3 で失敗します。ステップ 3 で失敗しない場合は、それらが一緒に開始されたと想定しますが、トランザクションの最後に 1 つのトランザクションだけがテストに合格します。更新時刻をチェックし、例外をスローするトランザクション。私の仮定は両方とも間違っているようです。
特に、クレジットを使いすぎないという意味で、私のトランザクションは「機能」しています。例外がないので、同じ購入をしたことをクライアントに何度か伝えます。
また、ステップ 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))
私の誤解は何ですか?