Postgres: prepared statement already exists

2019-05-14 08:56发布

问题:

I use Devise for authentication in my Rails API app. Sometimes I see following error in the logs:

ActiveRecord::StatementInvalid: PGError: ERROR: prepared statement "a3926" already exists: UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = 12345

The error is coming out of Devise:

  def update_tracked_fields!(request)
    old_current, new_current = self.current_sign_in_at, Time.now.utc
    self.last_sign_in_at     = old_current || new_current
    self.current_sign_in_at  = new_current

    old_current, new_current = self.current_sign_in_ip, request.remote_ip
    self.last_sign_in_ip     = old_current || new_current
    self.current_sign_in_ip  = new_current

    self.sign_in_count ||= 0
    self.sign_in_count += 1

    # error happens below
    save(validate: false) or raise "Devise trackable could not save #{inspect}." \
      "Please make sure a model using trackable can be saved at sign in."
  end

As I understand this error usually happens when there is something wrong with how database connections are used. Is there something I should be looking for?

回答1:

If you are using a server which forks your process (like unicorn) you need to create a connection for every forked process.

In case of unicorn add this:

#config/unicorn.rb
before_fork do |server, worker|

  Signal.trap 'TERM' do
   puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
   Process.kill 'QUIT', Process.pid
 end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|

  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to sent QUIT'
  end

 defined?(ActiveRecord::Base) and
   ActiveRecord::Base.establish_connection(
     Rails.application.config.database_configuration[Rails.env]
   )

end