Atomowość operacji

Sytuacja wygląda tak:

  • Śmiga sobie nginx a pod nim kilka mongreli w clusterze.
  • Jest sobie user na którego można głosować za pomocą klasy UserVotes (klasa zlicza głosy oddane przez jednego usera na drugiego- raz dziennie każdy może na każdego głosować)
  • Jest sobie kolejna klas UserContestVotes, która to z kolei jest w relacji z konkursem i zawiera licznik głosów wszystkich userów na tego jednego, czyli za każdym wywołaniem głosowania zwiększa się licznuk w UserVotes i UserContestVotes.
  • Problem:
    — Co stanie się jeśli w jednym momencie dwóch różnych userów zagłosuje na tego jednego? Mamy kilka mongreli więc zostaną obsłużeni równolegle.

UserVotes, na pewno będą spójne, ale co stanie się z licznikiem w UserContestVotes, skoro głosujący pobrali identyczna liczbę głosów ogólnych (np. 300) i zwiększając go o jeden, każdy z nich wyśle informacje do db, że nowa wartość to 301.

wydaje mi się, że optymistic locking jest dla Ciebie:

Witam, o ile dobrze zrozumiałem temat, optimistic locking na nic sie nie zda w tym wypadku.
Moja propozycja:

[code=ruby]class User < ActiveRecord::Base
has_many :given_votes, :class_name => “Vote”, :foreign_key => “sender_id”
has_many :received_votes, :class_name => “Vote”, :foreign_key => “sender_id”
end

kolumny tabeli votes:

sender_id Integer

receiver_id Integer

created_on Date

Ważne, by w migracji użyć created_on (nie używać t.timestamps).

Pozwoli to utworzyć prostą walidację

class Vote < ActiveRecord::Base
belongs_to :sender, :class_name => “User”, :foreign_key => “sender_id”
belongs_to :receiver, :class_name => “User”, :foreign_key => “receiver_id”

validates_uniqueness_of :receiver, :on => :create, :scope => [:created_on, :sender]

named_scope :today, lambda do
{
:conditions => {:created_on => Date.today}
}
end
end

Przykłady uzycia

Na ile osób dany osobnik dziś głosował

@user.given_votes.today.count

Ile osób głosowało na mnie dziś?

@user.received_votes.today.count

Ile osób głosowało na mnie ogólnie

@user.received_votes.count[/code]
Pracą domowa do odrobienia może być counter_cache, tak by informację o liczbie głosów pobierać razem z userem.

Użyj transakcji jeśli upierasz się przy osobnych modelach. Jak zainwestujesz w counter cache jak radzi sevos to ich aktualizacja zostanie automatycznie zamknięta w transakcji.

Dzięki wszystkim za odpowiedź.
Struktura bazy jest u mnie trochę bardziej złożona =>

[code]class UserContest < ActiveRecord::Base
belongs_to :user
belongs_to :contest
has_many :contest_votes
end

class Contest < ActiveRecord::Base
has_many :users
has_many :user_contests
end[/code]
Więc counter_cache w moim przypadku w czystej formie się raczej nie sprawdzi, ponieważ każdy użytkownik, może zagłosować kilka razy, co uaktualni licznik w Vote. A counter_cache zwróciłby nam w takim wypadku o ile dobrze rozumiem liczbę głosujących. W związku z czym licznik globalny user jest w UserContest i Optimistic Locking to rozwiązanie, którego szukałem. Tranzakcja jest też opcją, ale raczej nadmiarową w tym przypadku.