10

Rails には、クレジット カードに請求される金融取引を表すトランザクション モデルがあります。

トランザクションが作成されると、そのステータスは になり:newます。(DelayedJob で発生する) 料金を決済しようとすると、ステータスを に更新します:pendingchargestatus が でない場合、以降の呼び出しは無視されます:new

単純なバージョン (同時実行性には関係ありません):

# Trigger a card to be charged
def charge_transaction
  return unless status == :new

  self.transaction do
    self.delay.settle_credit_card
    self.update_attribute(:status, :pending)
  end
end

# Actually do the charging (in a delayed worker)
def settle_credit_card
   # ... Interact with our payment gateway
end

これは load_balanced Web アプリであるため、同時実行を考慮し、(同時要求による) 重複料金を作成しないようにしたいと考えています。私は楽観的ロックの利点を理解していますが、この場合、課金 (または何らかの方法でトランザクションを更新) を同時に試みることは例外的なケースであるため、この重要な領域を持つことは気にしません。

これは、悲観的な行レベルのロックを使用する試みです

同時バージョン (オプション 1)

# Trigger a card to be charged
def charge_transaction

  # Obtain row-lock
  self.with_lock do
    self.reload # Reload once lock is obtained - necessary?

    # Check status after lock/reload
    return unless status == :new

    self.delay.settle_credit_card
    self.update_attribute(:status, :pending)
  end
end

同時バージョン (オプション 2)

# Trigger a card to be charged
def charge_transaction

  # Begin transaction without lock
  self.transaction do
    self.reload(lock: true) # Reload and obtain lock

    # Check status after lock/reload
    return unless status == :new

    self.delay.settle_credit_card
    self.update_attribute(:status, :pending)
  end
end

これらのアプローチのいずれか (または両方) は有効ですか? ロックを取得したら (トランザクション オブジェクトが最新であることを確認するために) 明示的なリロードが必要ですか、それともロックを取得するときに Rails は自動的にそれを行いますか? 両方のアプローチが有効である場合、どちらが望ましいですか?

どうもありがとう!

4

1 に答える 1

16

どちらのアプローチも有効であり、機能します。バージョン 1reloadでは必要ありませんが、ロックすると自動的にレコードがリロードされるため、削除できます。

次に、 と のソース コードを確認するwith_locklock!、2 つのバージョンが 100% 同等であることがわかります。

def lock!(lock = true)
  reload(:lock => lock) if persisted?
  self
end

def with_lock(lock = true)
  transaction do
    lock!(lock)
    yield
  end
end

使用with_lockするのが最も簡単で好ましいでしょう:

# Obtain row-lock
with_lock do
  # Check status after lock/reload
  return unless status == :new

  delay.settle_credit_card
  update_attribute(:status, :pending)
end

self(注:メソッド呼び出しから安全に削除できます)

于 2014-02-13T12:57:01.793 に答える