ActiveRecord: обработка гонок БД среди рабочих - PullRequest
0 голосов
/ 29 апреля 2011

У меня есть проект Rails 3, работающий поверх PostgreSQL 9.0.

Вариант использования: Пользователи могут запросить подписку на Artists по имени.Для этого они отправляют список имен в ресурс REST.Если я не могу найти Artist по имени в локальной коллекции, я обращаюсь к last.fm за информацией о них и кеширую эту информацию локально.Этот процесс может занять некоторое время, поэтому он делегируется фоновому заданию с именем IndexArtistJob.

Проблема : IndexArtistJob будет выполняться параллельно.Таким образом, возможно, что два пользователя могут запросить добавление одного и того же Artist одновременно.Оба пользователя должны добавить Artist в свою коллекцию, но только один Artist должен оказаться в локальной базе данных.

Соответствующие части модели Artist:

require 'services/lastfm'

class Artist < ActiveRecord::Base
  validates_presence_of :name
  validates_uniqueness_of :name, :case_sensitive => false

def self.lookup(name)
  artist = Artist.find_by_name(name)
  return artist if not artist.nil?

  info = LastFM.get_artist_info(name)
  return if info.nil?

  # Check local DB again for corrected name.
  if name.downcase != info.name.downcase
    artist = Artist.find_by_name(info.name)
    return artist if not artist.nil?
  end

  Artist.new(
      :name => info.name,
      :image_url => info.image_url,
      :bio => info.bio
  )
  end
end

Класс IndexArtistJob определяется следующим образом:

class IndexArtistJob < Struct.new(:user_id, :artist_name)
  def perform
    user = User.find(user_id)

    # May return a new, uncommitted Artist model, or an existing, committed one.
    artist = Artist.lookup(artist_name)
    return if artist.nil?

    # Presume the thread is pre-empted here for a long enough time such that
    # the work done by this worker violates the DB's unique constraint.
    user.artists << artist

  rescue ActiveRecord::RecordNotUnique  # Lost race, defer to winning model
    user.artists << Artist.lookup(artist_name)
  end
end

Я пытаюсь сделать так, чтобы каждый работник зафиксировал найденный им новый Artist в надежде на лучшее.Если конфликт возникает, я хочу, чтобы более медленные работники отказались от работы, которую они сделали в пользу только что вставленного Artist, и добавили это Artist к указанному пользователю.

IЯ осознаю тот факт, что валидаторы Rails не могут заменить фактическую проверку целостности данных на уровне базы данных.Для этого я добавил уникальный индекс в поле имени в нижнем регистре таблицы Artist, чтобы обработать это (и использовать для поиска).Теперь, если я правильно понимаю документацию, коллекция ассоциаций AR фиксирует изменения для добавляемого элемента (в данном случае * 1031) и базовой коллекции в транзакции.Но я не могу гарантировать, что Artist будет добавлено.

Я делаю это правильно?Если так, есть ли лучший способ сделать это?Мне кажется, что структурирование вокруг исключений подчеркивает тот факт, что проблема связана с параллелизмом, и поэтому немного неуловима.

1 Ответ

1 голос
/ 02 мая 2011

Похоже, вы могли бы использовать простой механизм очередей. Вы можете сделать это, используя таблицу базы данных:

  1. Когда "внешний" поток обнаруживает отсутствующего исполнителя, пусть он записывает имя исполнителя в таблицу со статусом "ожидание" (имеет уникальный индекс имени исполнителя, так что это может произойти только один раз).

  2. Между тем фоновый поток / процесс находится в цикле и запрашивает у таблицы новые задания:
    а) начать транзакцию
    б) найти первого исполнителя со статусом = "ожидание"
    c) обновить статус Исполнителя на «обработка»
    г) завершить сделку

  3. Фоновый поток индексирует Исполнителя. Никто другой не будет пытаться, потому что они могут видеть статус «обработки».

  4. По завершении фоновый поток удаляет исполнителя из таблицы.

Используя этот метод, вы можете запускать несколько фоновых потоков для увеличения параллелизма при индексации вашего Artist.

Также посмотрите на что-то вроде beanstalk, чтобы управлять этим процессом. См http://railscasts.com/episodes/243-beanstalkd-and-stalker.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...