Rails + Sneakers: не удалось получить соединение из пула - PullRequest
2 голосов
/ 18 марта 2019

Мы используем в производстве кроссовки для большого приложения.И иногда загрузка может быть настолько большой, что одна конкретная очередь может содержать более 250_000 сообщений.И в таких случаях регулярно возникает исключение

ActiveRecord::ConnectionTimeoutError: 
could not obtain a connection from the pool within 5.000 seconds (waited 5.000 seconds); all pooled connections were in use

.

Для базы данных мы используем Amazon RDS на основе PostgreSQL 9.6.max_connections Конфигурационное значение PostgreSQL - 3296.

Наш database.yml файл:

production:
  adapter: postgresql
  encoding: utf8
  pool: 40
  database: <%= ENV['RDS_DB_NAME'] %>
  username: <%= ENV['RDS_USERNAME'] %>
  password: <%= ENV['RDS_PASSWORD'] %>
  host: <%= ENV['RDS_HOSTNAME'] %>
  port: <%= ENV['RDS_PORT'] %>

Я думаю, мы можем увеличить значение pool, но я не могу найти информацию о том, какчтобы вычислить максимально возможное значение, чтобы оно ничего не сломало.

Кроме того, копия приложения для фоновой обработки с использованием гема Sneakers живет отдельно (но использует ту же базу данных) и может быть настроена индивидуально.Но сейчас у него тот же конфиг database.yml.Конфигурационный файл gem для кроссовок:

production:
  heartbeat: 2000
  timeout_job_after: 35
  exchange_type: :fanout
  threads: 4
  prefetch: 4
  durable: true
  ack: true
  daemonize: true
  retry_max_times: 5
  retry_timeout: 2000
  workers: 4

У нас нет проблем с пулом соединений в базовом приложении времени выполнения, но ActiveRecord::ConnectionTimeoutError очень часто встречается у рабочих и это действительно большая проблема.

ИтакПомогите мне переконфигурировать файл databese.yml:

  1. Как правильно рассчитать максимально возможное значение для опции pool, если значение базы данных max_connections равно 3296?
  2. Какправильно рассчитать максимально возможное значение для опции pool при использовании гема Sneakers с конфигами выше?
  3. Или, если мои конфиги хороши, как я могу избежать ActiveRecord::ConnectionTimeoutError у рабочих?

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 09 июля 2019

С здесь .

Active Record, похоже, не освобождает соединения после work() Метод завершен. Если вы передаете блок с помощью Active Record ActiveRecord::Base.connection_pool.with_connection, он выпустит соединения отлично.

def work(msg)
  id = JSON.parse(msg)['id']
  ActiveRecord::Base.connection_pool.with_connection do
    user = User.find(id)
    user.name="Homer Simpson"
    user.save
  end
  ack!
end
0 голосов
/ 21 марта 2019

В ожидании ответа я продолжал искать решение.

И, наверное, моя основная проблема была в размере пула соединений.

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

before_fork = -> {
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.connection_pool.disconnect!
    Sneakers.logger.info('Disconnected from ActiveRecord!')
  end
}

after_fork = -> {
  def count_pool_size
    workers              = ::Sneakers::Worker::Classes
    default_threads_size = ::Sneakers.const_get(:CONFIG)[:threads]
    base_pool_size       = 3 + workers.size * 3

    if Sneakers.const_get(:CONFIG)[:share_threads]
      base_pool_size + default_threads_size
    else
      base_pool_size + connections_per_worker(workers, default_threads_size)
    end
  end

  def connections_per_worker(classes, default)
    classes.inject(0) do |sum, worker_class|
      sum + (worker_class.queue_opts[:threads] || default)
    end
  end

  def reconfig?
    Rails.env.production?
  end

  ActiveSupport.on_load(:active_record) do
    config = Rails.application.config.database_configuration[Rails.env]
    config.merge!('pool' => count_pool_size) if reconfig?

    ActiveRecord::Base.establish_connection(config)
    Sneakers.logger.info("Connected to ActiveRecord! Config: #{config}")
  end
}

Резюме: для всех работников мне нужно иметь ограничение в 600 соединений при максимальной нагрузке. Но у меня было только 40. Пока я буду использовать приведенный выше код. Надеюсь, это поможет.

...