Skip to content

Does solid queue have issues working with PGBouncer ? #537

@jwoodrow

Description

@jwoodrow

Hi !

I was trying to migrate from Resque to SolidQueue on our staging environment before attempting production, but I'm running into issues.

Our database.yml looks something like this

default_primary: &default_primary
  <<: *default
  url: <%= primary_database_url %>
  migrations_paths: 'db/migrate/primary'
  schema_search_path: 'public,salesforce,solid_queue,heroku_ext'
  schema_cache_path: db/schema_cache.dump

default_primary_replica: &default_primary_replica
  <<: *default
  url: <%= replica_database_url %>
  replica: true
  schema_search_path: 'public,salesforce,solid_queue,heroku_ext'
  schema_cache_path: db/schema_cache.dump

default_queue: &default_queue
  <<: *default
  url: <%= primary_database_url %>
  database_tasks: false
  schema_search_path: 'public,salesforce,solid_queue,heroku_ext'
  schema_cache_path: db/schema_cache.dump

default_queue_replica: &default_queue_replica
  <<: *default
  url: <%= replica_database_url %>
  replica: true
  schema_search_path: 'public,salesforce,solid_queue,heroku_ext'
  schema_cache_path: db/schema_cache.dump

with these applied to all environments equally and where the _database_url variables prioritize a connection pool URL from PGBouncer (we are using heroku) since we use up a lot more than 500 open connections otherwise.

The issue seems to be that when we are using the connection pool when running either bin/jobs and bundle exec rails solid_queue:start then we get random errors like these:

/app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:160:in `exec': PG::UndefinedTable: ERROR:  relation "solid_queue_pauses" does not exist (ActiveRecord::StatementInvalid)
LINE 1: SELECT "solid_queue_pauses"."queue_name" FROM "solid_queue_p...
                                                      ^

	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:160:in `perform_query'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:556:in `block (2 levels) in raw_execute'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:1011:in `block in with_raw_connection'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:983:in `with_raw_connection'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:555:in `block in raw_execute'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/notifications/instrumenter.rb:58:in `instrument'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:1129:in `log'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:554:in `raw_execute'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:591:in `internal_execute'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:547:in `internal_exec_query'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:693:in `select'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:73:in `select_all'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:248:in `block in select_all'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:286:in `block (2 levels) in cache_sql'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:80:in `compute_if_absent'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:284:in `block in cache_sql'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/concurrency/null_lock.rb:9:in `synchronize'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:283:in `cache_sql'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/query_cache.rb:248:in `select_all'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/relation/calculations.rb:322:in `block (2 levels) in pluck'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:418:in `with_connection'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/connection_handling.rb:310:in `with_connection'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/relation/calculations.rb:321:in `block in pluck'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/relation.rb:1470:in `skip_query_cache_if_necessary'
	from /app/vendor/bundle/ruby/3.3.0/gems/activerecord-8.0.1/lib/active_record/relation/calculations.rb:317:in `pluck'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/app/models/solid_queue/queue_selector.rb:73:in `paused_queues'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/app/models/solid_queue/queue_selector.rb:31:in `queue_names'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/app/models/solid_queue/queue_selector.rb:27:in `none?'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/app/models/solid_queue/queue_selector.rb:15:in `scoped_relations'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/app/models/solid_queue/ready_execution.rb:11:in `claim'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/worker.rb:41:in `block in claim_executions'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/poller.rb:47:in `block (2 levels) in with_polling_volume'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/logger_silence.rb:18:in `block in silence'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/logger_thread_safe_level.rb:37:in `log_at'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/logger_silence.rb:18:in `silence'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/poller.rb:47:in `block in with_polling_volume'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/notifications.rb:212:in `instrument'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue.rb:73:in `instrument'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/poller.rb:45:in `with_polling_volume'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/worker.rb:40:in `claim_executions'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/worker.rb:30:in `poll'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/poller.rb:29:in `block (2 levels) in start_loop'
	from /app/vendor/bundle/ruby/3.3.0/gems/activesupport-8.0.1/lib/active_support/execution_wrapper.rb:91:in `wrap'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/app_executor.rb:7:in `wrap_in_app_executor'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/poller.rb:28:in `block in start_loop'
	from <internal:kernel>:187:in `loop'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/poller.rb:25:in `start_loop'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/poller.rb:21:in `run'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/processes/runnable.rb:15:in `start'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:84:in `block in start_process'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:83:in `fork'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:83:in `start_process'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:58:in `block in start_processes'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:58:in `each'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:58:in `start_processes'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:35:in `start'
	from <internal:kernel>:90:in `tap'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/supervisor.rb:16:in `start'
	from /app/vendor/bundle/ruby/3.3.0/gems/solid_queue-1.1.4/lib/solid_queue/cli.rb:26:in `start'
	from /app/vendor/bundle/ruby/3.3.0/gems/thor-1.3.2/lib/thor/command.rb:28:in `run'
	from /app/vendor/bundle/ruby/3.3.0/gems/thor-1.3.2/lib/thor/invocation.rb:127:in `invoke_command'
	from /app/vendor/bundle/ruby/3.3.0/gems/thor-1.3.2/lib/thor.rb:538:in `dispatch'
	from /app/vendor/bundle/ruby/3.3.0/gems/thor-1.3.2/lib/thor/base.rb:584:in `start'
	from bin/jobs:6:in `<main>'

but if I open a bash terminal on that application (disabling the workers from the procfile manually first) then proceed to unset the connection pool URL (so it falls back to direct connection to the database) and run bin/jobs or rails solid_queue:start then the error goes away

I'm thinking maybe solid queue has issues not being directly connected to the database for some queries, maybe from the supervisor ?

I don't mind supervisors and the such having direct access, they don't need many connections. But I want my jobs' perform to run using the connection pool to avoid saturating them, if there was a way to configure that so that different configs in database.yml can be used for SolidQueue actions and others for the execution of the worker itself, maybe pooling would no longer be an issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions