I have a Rails app running on 2 servers in production.
I have a rake task that runs some import and then sends out an email to the users. I receive 2 emails now since there are 2 servers.
To avoid this, I tried creating a table my_import_run.
My import would check if a record with current date exists in the table, only if NOT the import would be run and email would be sent.
After sending email, all records in the table would be deleted and a record with only current date would be added
The above algorithm is implemented as shown here:
def self.import(search_date = Date.current)
if 0 == MyImportRun.count || !MyImportRun.where(:last_run => Date.today).any?
# the code to run import job and send email goes here
#
#
#
#
MyImportRun.delete_all
MyImportRun.new(:last_run => Date.today)
MyImportRun.save
end
But even after implementing the above code, I still receive 2 emails - I have no idea why. I was wondering maybe both the imports run at the exact same time and hence they both would find no records with current date and run import, send email and delete all records and add records with current date.
Is there a clean way of avoiding this from happening? The rake tasks would be automatically imported into cron job in Linux from what I understand; so I cannot delete the rake file from just one server.
Am sure there must be a clean way to avoid this duplication.
If you use Whenever and Capistrano that can be done with roles:
in a deploy file, you can set up a list of the roles:
server 'first-server', user: 'deployer', roles: %w{app db web sidekiq job}, primary: true
server 'second-server', user: 'deployer', roles: %w{app db web sidekiq job2}
and in the schedule.rb:
every 1.day, at: '5:00am', roles: [:job] do # this task will be run only on the first server
rake "-s sitemap:refresh:no_ping"
end
every 1.day, at: '5:00am', roles: [:job2] do # this only on second
rake "another_rake_task"
end
More details you can find this
I'm trying to migrate users from one system to another. Each system has its own database and different classes.
My plan is to connect to one database, read some info from one database via SQL commands:
ActiveRecord::Base.connection.execute(sql_command)
do something with the data and then write some results on the new database using normal models.
I plan on doing that inside Sidekiq job but I'm trying to do some testing using a Rails console on Heroku.
This should be pretty straightforward, but this proves ridiculously difficult.
When I launched a Rails console on Heroku. I'm connecting to DATABASE_URL, which is ok, but when I try to connect to the old database and execute a command, like this:
ActiveRecord::Base.establish_connection(adapter: "postgresql", encoding: "unicode", pool: 10, url: "postgres://...info...")
ActiveRecord::Base.connection.execute("select count(*) from users")
I end up with:
PG::ConnectionBad (could not connect to server: No such file or directory)
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
I can, however, connect to this old database by launching my rails console on heroku using DATABASE_URL as the env variable:
$ heroku run 'DATABASE_URL=postgres://...info... rails console' -a console' -a app
BUT I don't know how to switch back to my new database so I can update things.
How does one switch databases at run time when using rails console on heroku?
Why try to runtime switch the database? Why not have both connected at the same time and specify at the model level which database they read/write from? Rails supports connecting multiple databases and specifying in individual models what database connection to use: https://guides.rubyonrails.org/active_record_multiple_databases.html
The problem was that using url: didn't work and all parameters needed to be specified.
config = {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>10, "username"=>"u...", "password"=>"p...", "port"=>5432, "database"=>"d...", "host"=>"ec2..."}
If you go for a 3tier database yml, you can use this:
config = ActiveRecord::Base.configurations["production"]["seconddb"]
Then, you can use establish_connection
ActiveRecord::Base.establish_connection(config)
ActiveRecord::Base.connection.execute("select count(*) from users")
Once I started specifying username, password, port, database and host, it all worked like a charm.
To work with both databases at the same time, a good way is to create a class
class OtherDB < ActiveRecord::Base
establish_connection(ActiveRecord::Base.configurations["production"]["seconddb"])
end
Then you can call things that way
OtherDB.table_name = "table_name"
OtherDB.first
ref (Establish a connection to another database only in a block?)
And to run SQL commands:
OtherDB.connection.execute("select count(*) from users")
I tried to solve the problem of adding a column on a large MySQL table without locking, and on my dev environment migration it works. But when I launched it on production, then application raised MySQL errors:
Mysql2::Error: Lock wait timeout exceeded;
try restarting transaction: UPDATE competition_clients...
Here is my migration:
require 'lhm'
class AddPersonToCompetitionClients < ActiveRecord::Migration
def up
Lhm.change_table(:competition_clients, atomic_switch: false) do |m|
m.add_column(:person_id, 'INT(12)')
m.add_column(:active, 'TINYINT(1)')
m.add_index([:person_id])
m.add_index([:active])
end
end
def down
Lhm.change_table(:competition_clients, atomic_switch: false) do |m|
m.remove_index([:person_id])
m.remove_index([:active])
m.remove_column(:person_id)
m.remove_column(:active)
end
end
end
Have anyone same problem or have anyone a solution how to run migration without raising exception from main app?
One solution I've used, if you have an uber query or series of queries, is to extend the wait timeout.
If you can't currently restart the server or MySQL, enter the following in the MySQL terminal:
mysql> set GLOBAL wait_timeout=28800;
This setting will go away when the current MySQL instance is terminated.
To set the wait_timeout parameter permanently, edit your MySQL configuration file:
~$ vim /etc/my.conf
Insert the following line:
wait_timeout = 28800
Now restart MySQL:
sudo /etc/init.d/mysql restart
Or on Redhat:
sudo service mysqld restart
Notes
wait_timeout
The number of seconds the server waits for activity on a noninteractive connection before closing it. This timeout applies only to TCP/IP and Unix socket file connections, not to connections made using named pipes, or shared memory.
interactive_timeout
The number of seconds the server waits for activity on an interactive connection before closing it. An interactive client is defined as a client that uses the CLIENT_INTERACTIVE option to mysql_real_connect().
Can be set like:
interactive_timeout = 28800
my.cnf
Possible locations of my.cnf:
/etc/my.cnf
/etc/mysql/my.cnf
$MYSQL_HOME/my.cnf
[datadir]/my.cnf
~/.my.cnf
source: http://moorberry.net/blog/mysql-lock-wait-timeout-exceeded/
I have a rails application running over Postgres.
I have two servers: one for testing and the other for production.
Very often I need to clone the production DB on the test server.
The command I'm runnig via Vlad is:
rake RAILS_ENV='test_server' db:drop db:create
The problem I'm having is that I receive the following error:
ActiveRecord::StatementInvalid: PGError: ERROR: database <database_name> is being accessed by other users DROP DATABASE IF EXISTS <database_name>
This happens if someone has accessed the application via web recently (postgres keeps a "session" opened)
Is there any way that I can terminate the sessions on the postgres DB?
Thank you.
Edit
I can delete the database using phppgadmin's interface but not with the rake task.
How can I replicate phppgadmin's drop with a rake task?
If you kill the running postgresql connections for your application, you can then run db:drop just fine. So how to kill those connections? I use the following rake task:
# lib/tasks/kill_postgres_connections.rake
task :kill_postgres_connections => :environment do
db_name = "#{File.basename(Rails.root)}_#{Rails.env}"
sh = <<EOF
ps xa \
| grep postgres: \
| grep #{db_name} \
| grep -v grep \
| awk '{print $1}' \
| xargs kill
EOF
puts `#{sh}`
end
task "db:drop" => :kill_postgres_connections
Killing the connections out from under rails will sometimes cause it to barf the next time you try to load a page, but reloading it again re-establishes the connection.
Easier and more updated way is:
1. Use ps -ef | grep postgres to find the connection #
2. sudo kill -9 "# of the connection
Note: There may be identical PID. Killing one kills all.
Here's a quick way to kill all the connections to your postgres database.
sudo kill -9 `ps -u postgres -o pid`
Warning: this will kill any running processes that the postgres user has open, so make sure you want to do this first.
I use the following rake task to override the Rails drop_database method.
lib/database.rake
require 'active_record/connection_adapters/postgresql_adapter'
module ActiveRecord
module ConnectionAdapters
class PostgreSQLAdapter < AbstractAdapter
def drop_database(name)
raise "Nah, I won't drop the production database" if Rails.env.production?
execute <<-SQL
UPDATE pg_catalog.pg_database
SET datallowconn=false WHERE datname='#{name}'
SQL
execute <<-SQL
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = '#{name}';
SQL
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
end
end
end
end
When we used the "kill processes" method from above, the db:drop was failing (if :kill_postgres_connections was prerequisite). I believe it was because the connection which that rake command was using was being killed. Instead, we are using a sql command to drop the connection. This works as a prerequisite for db:drop, avoids the risk of killing processes via a rather complex command, and it should work on any OS (gentoo required different syntax for kill).
cmd = %(psql -c "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE procpid <> pg_backend_pid();" -d '#{db_name}')
Here is a rake task that reads the database name from database.yml and runs an improved (IMHO) command. It also adds db:kill_postgres_connections as a prerequisite to db:drop. It includes a warning that yells after you upgrade rails, indicating that this patch may no longer be needed.
see: https://gist.github.com/4455341, references included
Step 1
Get a list of all postgres connections with ps -ef | grep postgres
The list will look like this:
502 560 553 0 Thu08am ?? 0:00.69 postgres: checkpointer process
502 565 553 0 Thu08am ?? 0:00.06 postgres: bgworker: logical replication launcher
502 45605 553 0 2:23am ?? 0:00.01 postgres: st myapp_development [local] idle
Step 2
Stop whatever connection you want with sudo kill -9 <pid>, where pid is the value in the second column. In my case, I wanted to stop the last row, with pid 45605, so I use:
sudo kill -9 45605
Please check if your rails console or server is running in another tab and then
stop the rails server and console.
then run
rake db:drop
Let your application close the connection when it's done. PostgreSQL doesn't keep connections open , it's the application keeping the connection.
I wrote a gem called pgreset that will automatically kill connections to the database in question when you run rake db:drop (or db:reset, etc). All you have to do is add it to your Gemfile and this issue should go away. At the time of this writing it works with Rails 4 and up and has been tested on Postgres 9.x. Source code is available on github for anyone interested.
gem 'pgreset'
Rails is likely connecting to the database to drop it but when you log in via phppgadmin it is logging in via the template1 or postgres database, thus you are not affected by it.
This worked for me (rails 6):
rake db:drop:_unsafe
I think we had something in our codebase that initiated a db connection before the rake task attempted to drop it.
After restarting the server or computer, please try again.
It could be the simple solution.
You can simply monkeypatch the ActiveRecord code that does the dropping.
For Rails 3.x:
# lib/tasks/databases.rake
def drop_database(config)
raise 'Only for Postgres...' unless config['adapter'] == 'postgresql'
Rake::Task['environment'].invoke
ActiveRecord::Base.connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{config['database']}' AND state='idle';"
ActiveRecord::Base.establish_connection config.merge('database' => 'postgres', 'schema_search_path' => 'public')
ActiveRecord::Base.connection.drop_database config['database']
end
For Rails 4.x:
# config/initializers/postgresql_database_tasks.rb
module ActiveRecord
module Tasks
class PostgreSQLDatabaseTasks
def drop
establish_master_connection
connection.select_all "select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname='#{configuration['database']}' AND state='idle';"
connection.drop_database configuration['database']
end
end
end
end
(from: http://www.krautcomputing.com/blog/2014/01/10/how-to-drop-your-postgres-database-with-rails-4/)
I had this same issue when working with a Rails 5.2 application and PostgreSQL database in production.
Here's how I solved it:
First, log out every connection to the database server on the PGAdmin Client if any.
Stop every session using the database from the terminal.
sudo kill -9 `ps -u postgres -o pid=`
Start the PostgreSQL server, since the kill operation above stopped the PostgreSQL server.
sudo systemctl start postgresql
Drop the database in the production environment appending the production arguments.
rails db:drop RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1
That's all.
I hope this helps
[OSX][Development/Test] Sometimes it is hard to determine the proper PID when you have a lot of PostgreSQL processes like checkpointer, autovacuum launcher, etc. In this case, you can simply run:
brew services restart postgresql#12
If you dockerized your app, then restart your db service
sudo docker-compose restart db
Just make sure that the you have exited the rails console on any open terminal window and exited the rails server...this is one of the most common mistake made by people
I had a similar error saying 1 user was using the database, I realized it was ME! I shut down my rails server and then did the rake:drop command and it worked!
Solution
Bash script
ENV=development
# restart postgresql
brew services restart postgresql
# get name of the db from rails app
RAILS_CONSOLE_COMMAND="bundle exec rails c -e $ENV"
DB_NAME=$(echo 'ActiveRecord::Base.connection_config[:database]' | $RAILS_CONSOLE_COMMAND | tail -2 | tr -d '\"')
# delete all connections to $DB_NAME
for pid in $(ps -ef | grep $DB_NAME | awk {'print$2'})
do
kill -9 $pid
done
# drop db
DISABLE_DATABASE_ENVIRONMENT_CHECK=1 RAILS_ENV=$ENV bundle exec rails db:drop:_unsafe
ActiveRecord::StatementInvalid: PG::ObjectInUse: ERROR: database "database_enviroment" is being accessed by other users
DETAIL: There is 1 other session using the database.
For rails 7: you can use -> rails db:purge
I need to create a rake task to do some active record operations via a ssh tunnel.
The rake task is run on a remote windows machine so I would like to keep things in ruby. This is my latest attempt.
desc "Syncronizes the tablets DB with the Server"
task(:sync => :environment) do
require 'rubygems'
require 'net/ssh'
begin
Thread.abort_on_exception = true
tunnel_thread = Thread.new do
Thread.current[:ready] = false
hostname = 'host'
username = 'tunneluser'
Net::SSH.start(hostname, username) do|ssh|
ssh.forward.local(3333, "mysqlhost.com", 3306)
Thread.current[:ready] = true
puts "ready thread"
ssh.loop(0) { true }
end
end
until tunnel_thread[:ready] == true do
end
puts "tunnel ready"
Importer.sync
rescue StandardError => e
puts "The Database Sync Failed."
end
end
The task seems to hang at "tunnel ready" and never attempts the sync.
I have had success when running first a rake task to create the tunnel and then running the rake sync in a different terminal. I want to combine these however so that if there is an error with the tunnel it will not attempt the sync.
This is my first time using ruby Threads and Net::SSH forwarding so I am not sure what is the issue here.
Any Ideas!?
Thanks
The issue is very likely the same as here:
Cannot connect to remote db using ssh tunnel and activerecord
Don't use threads, you need to fork the importer off in another process for it to work, otherwise you will lock up with the ssh event loop.
Just running the code itself as a ruby script (with Importer.sync disabled) seems to work without any errors. This would suggest to me that the issue is with Import.sync. Would it be possible for you to paste the Import.sync code?
Just a guess, but could the issue here be that your :sync rake task has the rails environment as a prerequisite? Is there anything happening in your Importer class initialization that would rely on this SSH connection being available at load time in order for it to work correctly?
I wonder what would happen if instead of having environment be a prereq for this task, you tried...
...
Rake::Task["environment"].execute
Importer.sync
...