Несколько сначала или создать в sidekiq создает несколько записей вместо обновления - PullRequest
0 голосов
/ 05 июня 2018

Когда пользователь создается, я пытаюсь добавить 1 к счетчику, если запись существует, или создать запись с count: 1, если нет.

У меня есть что-то вроде этого:

class User < ApplicationRecord
  after_commit :record, on: :create

  def record
    SomeWorker.perform_async(id)
  end
end

После того, как пользователь создан, SomeWorker сработает в sidekiq и вызовет что-то вроде:

class SomeWorker
  include Sidekiq::Worker

  def perform(id)
    Record.where(some_conditions).first_or_create()
  end
end

Это работает как шарм, когда я не использую sidekiq, и просто вызываю его сразу послепользователь создан.Однако, когда я собираю данные, которые создают 10 пользователей, я ожидаю создать 1 запись с count: 10, но она создает 10 записей с count: 1.

Есть идеи, что происходит и как я могу это исправить?

1 Ответ

0 голосов
/ 05 июня 2018

Это классическое состояние гонки.Обратите внимание, что "Sidekiq является многопоточным, поэтому ваши рабочие должны быть поточно-ориентированными." .Все ваши работники работают параллельно, и никто из них изначально не находит соответствующую запись в where и заканчивает тем, что создает новую запись со счетом 1.

Я советую вместо этого создать / найти запись заранее и отправитьэто идентификатор для увеличения значения count.Хотя я не уверен, почему вы используете Sidekiq для разгрузки базовой операции UPDATE.

class User < ApplicationRecord
  after_commit :record, on: :create

  def record
    record = Record.create_with(count: 0).find_by(some_conditions)
    SomeWorker.perform_async(record.id)
  end
end

class SomeWorker
  include Sidekiq::Worker

  def perform(id)
    Record.find(id).increment!(:count)
  end
end

Также обратите внимание, что increment! также не подходит для параллелизма, но он должен работать в вашем случае.

a = Record.first #<Record id: 1, count: 1>
b = Record.first #<Record id: 1, count: 1>
a.increment!(:count) #<Record id: 1, count: 2>
b.increment!(:count) #<Record id: 1, count: 2> ## SHOULD BE 3
b.reload #<Record id: 1, count: 3> It's actually three, but object was stale on update.

SQL запускаетзапрос, ниже которого отображается значение последнего установленного значения.

UPDATE `records` SET `count` = COALESCE(`parent_id`, 0) + 1 WHERE `records`.`id` = 1
...