Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ docker run --name docuseal -p 3000:3000 -v.:/data docuseal/docuseal

By default DocuSeal docker container uses an SQLite database to store data and configurations. Alternatively, it is possible to use PostgreSQL or MySQL databases by specifying the `DATABASE_URL` env variable.

When using PostgreSQL providers that offer a pooled connection string for application traffic and a direct connection string for schema changes, keep `DATABASE_URL` set to the application connection and set `MIGRATION_DATABASE_URL` to the direct connection string. This lets DocuSeal run startup migrations, such as adding `NOT NULL` constraints, outside transaction poolers that do not support all migration operations.

#### Docker Compose

Download docker-compose.yml into your private server:
Expand Down
4 changes: 3 additions & 1 deletion config/initializers/migrate.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require Rails.root.join('lib/migration_database_url')

Rails.configuration.to_prepare do
ActiveRecord::Tasks::DatabaseTasks.migrate if ENV['RAILS_ENV'] == 'production' && ENV['RUN_MIGRATIONS'] != 'false'
MigrationDatabaseUrl.migrate if ENV['RAILS_ENV'] == 'production' && ENV['RUN_MIGRATIONS'] != 'false'
end
18 changes: 18 additions & 0 deletions lib/migration_database_url.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module MigrationDatabaseUrl
module_function

def migrate
migration_database_url = ENV.fetch('MIGRATION_DATABASE_URL', '').to_s

return ActiveRecord::Tasks::DatabaseTasks.migrate if migration_database_url.empty?

app_database_config = ActiveRecord::Base.connection_db_config

ActiveRecord::Base.establish_connection(migration_database_url)
ActiveRecord::Tasks::DatabaseTasks.migrate
ensure
ActiveRecord::Base.establish_connection(app_database_config) if app_database_config
end
end
57 changes: 57 additions & 0 deletions spec/lib/migration_database_url_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

require 'rails_helper'
require Rails.root.join('lib/migration_database_url')

RSpec.describe MigrationDatabaseUrl do
around do |example|
original_env = ENV.to_hash

example.run
ensure
ENV.replace(original_env)
end

describe '.migrate' do
it 'runs migrations with the application database when no migration URL is configured' do
ENV.delete('MIGRATION_DATABASE_URL')

expect(ActiveRecord::Base).not_to receive(:establish_connection)
expect(ActiveRecord::Tasks::DatabaseTasks).to receive(:migrate)

described_class.migrate
end

it 'uses MIGRATION_DATABASE_URL for migrations and restores the application database' do
original_config = Object.new
migration_database_url = 'postgres://user:***@ep-direct.us-east-1.aws.neon.tech/docuseal?sslmode=require'
connections = []

ENV['MIGRATION_DATABASE_URL'] = migration_database_url

allow(ActiveRecord::Base).to receive(:connection_db_config).and_return(original_config)
allow(ActiveRecord::Base).to receive(:establish_connection) { |config| connections << config }

expect(ActiveRecord::Tasks::DatabaseTasks).to receive(:migrate)

described_class.migrate

expect(connections).to eq([migration_database_url, original_config])
end

it 'restores the application database when migration fails' do
original_config = Object.new
migration_database_url = 'postgres://user:***@ep-direct.us-east-1.aws.neon.tech/docuseal?sslmode=require'
connections = []

ENV['MIGRATION_DATABASE_URL'] = migration_database_url

allow(ActiveRecord::Base).to receive(:connection_db_config).and_return(original_config)
allow(ActiveRecord::Base).to receive(:establish_connection) { |config| connections << config }
allow(ActiveRecord::Tasks::DatabaseTasks).to receive(:migrate).and_raise(ActiveRecord::StatementInvalid)

expect { described_class.migrate }.to raise_error(ActiveRecord::StatementInvalid)
expect(connections).to eq([migration_database_url, original_config])
end
end
end