1

GAEのドキュメントが警告:GAEでは、どのように貨幣振替取引を冪等化するのですか?

可能な限りを使用すると、トランザクションを繰り返した場合、最終結果は同じになるように、データストアの取引の冪等を行います。

私は二人の間で金額を転送したいとします

class User(ndb.Model): 
    balance = ndb.IntegerProperty(default=0) 

@ndb.transactional(xg=True) 
def transfer(from_key, to_key, amount) 
    from = from_key.get() 
    to = to_key.get() 
    from.balance -= amount 
    to.balance += amount 
    ndb.put_multi([from, to]) 

これは冪等ではないので、それが複数回起こると、問題を引き起こす可能性があります。私は、トランザクションが冪等であることを保証するためにこれをリファクタリングしたいと思います。

このanswerは解決策を提案している:

問題を解決するには、「トランザクションキー」を作成し、トランザクションの一部として、新しいエンティティでそのキーを記録することにより、トランザクション冪等を行うことができます。 2番目のトランザクションはそのトランザクション・キーをチェックでき、見つかった場合は何も行いません。トランザクションが完了したことを確認するか、再試行を断念したら、トランザクションキーを削除することができます。

しかし、私はそれを実装する方法を理解していません。

誰かがこのトランザクションを冪等にする方法を説明できますか?

+1

この古い記事を読んでください。 appengineには多くの新機能が登場する前に書かれていましたが、やっていることを実装するためのガイドとして見ておく価値があります。 http://blog.notdot.net/2009/9/Distributed-Transactions-on-App-Engine –

答えて

1

次の例のように、取引内容に基づいてキーを作成することができます。

import datetime 
import hashlib 

>>> txn = { 
    'from_account': '100123', 
    'to_account': '200456', 
    'amount': 123456, 
    'timestamp': datetime.datetime(2017, 9, 23, 10, 11, 12, 123456) 
} 

# Combine the values into a string 
>>> raw_key = u''.join([unicode(v) for k, v in sorted(txn.items())]) 

>>> print raw_key 
12345610-09-23 10:11:12.123456200456 

# hash the key so exposing it in logs etc. doesn't expose transaction data 
>>> key = hashlib.sha256().hexdigest() 
>>> print key 
261c516faa580d6604850967c5804f3fce5f323aae90e36debdb84aa0b950dcb 

をあなたがデータストアでハッシュされたキーを保存したり、持っていれば、それはあなたのトランザクションモデルの計算されたプロパティにすることができます新しいトランザクションを作成しようとする前にクエリを実行します。

class TransactionKeys(ndb.model): 
     pass 


class TransactionHandler(webapp2.RequestHandler): 

    def post(self): 
     txn = { 
      'from_account': self.request.POST['from'], 
      'to_account': self.request.POST['to'], 
      'value': self.request.POST['value'] 
      'timestamp': datetime.datetime.now() 
     } 
     raw_key = u''.join([unicode(v) for k, v in sorted(txn.items())]) 
     txn_key = hashlib.sha256().hexdigest() 
     ... 
     transfer(from_key, to_key, amount, txn_key) 


@ndb.transactional(xg=True) 
def transfer(from_key, to_key, amount, txn_key) 
    already_exists = TransactionKeys.get_by_id(txn_key) 
    if already_exists: 
     raise DuplicateTransactionError('Duplicate transaction!') 
    else: 
     transaction_key = TransactionKey(id=txn_key) 
    from = from_key.get() 
    to = to_key.get() 
    from.balance -= amount 
    to.balance += amount 
    ndb.put_multi([from, to, txn_key]) 

この方法は完璧ではありません - 二つの同一のトランザクションが例えば同じマイクロ秒で到着する場合、それは失敗します。 App EngineのインスタンスIDやリクエストIDなど、他のデータを追加してキーをより一意にすることができます。

最後に義務的な免責事項:私はセキュリティ専門家ではありません。実際のお金でこれを行う場合は、適切なレベルのデューデリジェンスを実施し、専門的補償/公的責任保険を検討する必要があります。

+0

キーをどのように使用するかを拡張できますか? (私の状況は実際には危険なものではありませんが、お金のやりとりは簡単な例です。 –

+0

@ JeffO'Neillのサンプルコードが追加されました – snakecharmerb

+0

txnキーにランダムなUUIDを使用することはできますか?キーの情報?また、 'post'の終わりに' TransactionKeys'エンティティを削除して、物事を整理することができるようです。 –

関連する問題