Sidekiq, не хватает потоков - Rails покидает и не пожинает connection_pool потоки - PullRequest
2 голосов
/ 21 мая 2019

У нас есть производственная система, работающая в следующей конфигурации:

Ruby 2.5.1
Rails 5.2.2
Sidekiq 5.2.5
Sidekiq-cron 1.1.0
Redis 4.1.0
  adapter: postgresql
  pool: 10
  reaping_frequency: 10
  timeout: 5000
  username: ...
  password: ...
  host: ...

Имеет 3 очереди (по умолчанию / высокий / средний приоритет), каждая из которых имеет 4 потока. Недавно мы добавили новое задание sidekiq cron, которое выполняется каждые 30 минут в верхней очереди, и через пару дней система просто заходит в тупик, и больше потоков не может быть создано для пулов соединений. Мы проследили его до «высокой» очереди, и это новое задание, когда в последний раз он зависал, в этой «высокой» очереди было 1900 потоков, почти все из которых выглядят как «пул соединений». kill -9 в процессе для очереди, и наш супервизор перезапускает ее, и все снова отлично в течение 5-7 дней, а затем снова выключается.

Это новое задание создает ряд новых списков в удаленной БД, у нас есть локальная модель ActiveRecord для локальных записей и модель суперкласса RemoteList. Мы используем RemoteModel.establish_connection.... транзакция открыть, написать, написать ..., закрыть транзакцию, закрыть соединение. Мы общаемся с большим количеством удаленных БД, поэтому эта модель хорошо работает для нас.

Новый сотрудник неоднократно обращается к издателю списка, который использовался более 3 лет без блокировки. Мы видим, как один новый процесс пула соединений добавляется каждый раз, когда мы пишем в удаленную БД через издателя старого списка.

Я пробовал:

  1. Ручное получение соединения из пула и возврат как для самого ActiveRecord, так и для нашего суперкласса, один, оба, ни одного.
  2. Завершение блока в ActiveRecord::Base.connection_pool.with_connection

Ничто из вышеперечисленного не оказывает никакого влияния, и число потоков просто увеличивается с каждым разделенным нами файлом, пока мы не достигнем тупика и больше не будет потоков. Похоже, что пожинает, вообще ничего не делает. Единственное, что отличается в этом работнике, это то, что он open3.capture3 обращается к программе 'C', чтобы выполнить деление файлов гораздо быстрее, чем мы можем сделать в ruby, но я вижу, что порожденная оболочка закрылась и завершилась, но все же мы получить эти потоки «пула соединений».

У кого-нибудь есть какие-нибудь хорошие идеи.

Спасибо Kate

Список публикаций

CoreDBListModel.semaphore.synchronize do
    begin

      .... setup removed....
      CoreDBListModel.establish_connection(@config['database'])

      CoreDBListModel.transaction do
        core = CoreDBListModel.where(:description => list.list_id).first
        core.pending = true
        core.name = list.name
        core.tags = category.name
        core.pcount = list.count
        core.active = list.deleted ? 2:0
        core.save

        ... make list insert data.....
        mass_insert = "INSERT INTO #{mapping['table']} (data_id, data, fulldata) VALUES #{inserts.join(", ")}"
        CoreDBListModel.connection.execute(mass_insert)

      # Mark as completed
        core.pending = false
        core.save

      end

    rescue => e
      @code = 500
      @message = "Failed - #{e.message}, #{e.backtrace[0]}"
      Rails.logger.error("CoreDBList() - Publishing failed - #{list.list_id}")
      Rails.logger.error("CoreDBList() - Publishing failed - #{e.message}")
      Rails.logger.error("CoreDBList() - Publishing failed - #{e.backtrace.first(10).join("\n")}")
    ensure
      begin
        # Close our DB connection
        CoreDBListModel.connection.close
      rescue
      end
    end
  end

Добавили ответ ниже с более подробной информацией, но в основном проблема заключается в Rails где-то между 5.1.6 и 5.2.1. Если мы вернемся к 5.1.6, проблема исчезнет.

https://github.com/rails/rails/issues/36333

Ответы [ 2 ]

1 голос
/ 21 мая 2019

Сначала определите частоту, с которой происходит утечка потоков - это происходит при каждом запуске задачи или время от времени?Если первое - вам повезло, и вы можете попробовать отладку, зарегистрировав ObjectSpace.each_object(Thread).count (будьте осторожны, это тяжелая функция, может не подходить для высокой нагрузки в производственном процессе) в нескольких точках вашего кода, чтобы попытаться обнаружить утечку потоков.

Suspect - это Open3.capture3 - он запускает два потока для чтения stdin / stdout процесса и еще один для чтения статуса завершения процесса, если вы не используете отдельный stderr в своем коде C - я бы предложил переключиться на capture2 и посмотреть, не течет ли нить примерно в 1/3 раза медленнее.Я помню, что проблемы с popen3 и дочерним процессом, не использующим stderr, или комбинацией открытия / закрытия потоков std или не чтения stdin, теперь не могут вспомнить детали.

0 голосов
/ 23 мая 2019

Не ответ, а обновление по этому вопросу, сегодня утром один из наших серверов снова вышел из строя с 31000+ потоков для одной из задач sidekiq.

Похоже, что это проблема с Rails ActiveRecord,мы можем успешно оставить спящие потоки с некоторым тестовым кодом.Это CoreDBListModel.connection.close, который переводит пул соединений в спящий режим, и он никогда не собирается из-за его состояния.

Похоже, что проблема для нас началась, когда мы перешли с Rails 5.1.5 на 5.2.1, если мы вернемся к этой версии, проблема исчезнет, ​​эта проблема также, похоже, присутствует в Rails 6, поскольку аналогичная проблема также возникла у кого-то другого, немного другое имя, но та же проблема:

https://github.com/rails/rails/issues/36333

...