2 Stimmen

Rails-Applikation mit Zeilensperrung und Transaktionen auf MySQL gibt falsche Daten

Ich betreibe eine Auktions-Website in Produktion. Ich habe ein Auktionsmodell und ein Gebotsmodell. Die Auktionszeile enthält die Endzeit, den aktuellen Preis, die Anzahl der Gebote, den Benutzernamen des letzten Bieters usw. Eine Gebotszeile enthält einen Benutzernamen, eine Auktions-ID, einen Zeitstempel, einen Preis usw.

Auf der Auktionsseite möchte ich die Statistiken der Auktion und die letzten zehn Bieter mit dem aktuellen Preis der Auktion anzeigen. Wenn ich also ein Gebot speichere, füge ich der Gebotstabelle eine Zeile hinzu, aktualisiere das Konto des Benutzers und die Auktionszeile - alles in einem Vorgang.

Dies ist mein vereinfachtes Bid-Modell:

class Bid < ActiveRecord::Base
  belongs_to :auction, :inverse_of => :bids
  belongs_to :account, :inverse_of => :bids
  scope :recent, order('id DESC').limit(10) # used to get last ten bids
  before_create :update_auction
  after_create :update_account

  def update_auction
    auction.lock!
    auction.highest_bidder = username
    auction.price = ...
    # more stuff
    auction.save!
  end

  def update_account
    # do stuff
    account.save!
  end

Wenn jedoch genügend Leute gleichzeitig auf die Gebotsschaltfläche klicken, werden die Auktionsstatistiken inkonsistent. Der Zähler weicht um ein oder zwei Punkte ab, oder der Höchstbietende stimmt nicht mit der letzten Zeile der Gebotstabelle überein. Ich dachte, da diese drei Schreibvorgänge in eine Transaktion eingeschlossen waren und ich die eine gemeinsame Zeile (Auktionen) sperre, wären die Daten in Ordnung, aber das ist nicht der Fall.

Ich könnte die Anwendung umschreiben, um die Gebotstabelle dynamisch abzufragen, um jeden Gewinner, die Anzahl der Gebote usw. zu finden, aber das verkompliziert viele andere Abfragen, die ich mache.

Was ist also die geeignete Gleichzeitigkeitskontrolle hier? Ich muss einen Gewinner in der Auktionsreihe speichern, aber dieser Gewinner muss die letzte Reihe in der Gebotstabelle sein. Und ich bekomme viele Schreibvorgänge in dieselbe Zeile auf einmal.

0voto

sled Punkte 14307

Ich sehe nicht, dass Sie eine Transaktion öffnen, hier ist ein Weg, den Sie versuchen sollten:

def update_auction
  Auction.transaction do
    auction.lock!
    auction.highest_bidder = username
    auction.price = ...
    # more stuff
    auction.save!
  end
end

Sehen Sie sich auch an ActiveRecord::Locking::Pessimistisch

Ein anderer Ansatz besteht darin, die Auktion nicht zu aktualisieren, wenn jemand ein Gebot abgibt, da ein Gebot keinen Einfluss auf den Auktionsdatensatz selbst hat.

Ich würde es folgendermaßen machen:

  • Angenommen, jede Aktion hat eine Spalte "Ablaufdatum" in der Auktionstabelle.

  • Der Benutzer XY versucht, ein Gebot im Validierungsprozess des von Ihnen abgegebenen Gebots abzugeben:

    • Prüfen Sie, ob die Auktion noch offen ist, wenn nicht, geben Sie eine Fehlermeldung aus, dass es zu spät ist, ein Gebot abzugeben.
    • Prüfen Sie, ob es bereits ein höheres Gebot für die Auktion gibt, wenn ja, geben Sie eine Fehlermeldung aus, dass das Gebot zu niedrig ist.

So wird sichergestellt, dass keine ungültigen Gebote abgegeben werden können. Wenn das Auktionsende erreicht ist, ist es nicht möglich, ein Gebot abzugeben.

Um den Gewinner zu ermitteln, gehen Sie wie folgt vor

winning_bid = the_auction.bids.maximum(:amount)
winner      = winning_bid.account

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X