DEV: Support per-unicorn-worker DB variable overrides (#35615)

This commit adds support for setting ActiveRecord's database
configuration variables for unicorn workers via ENVs with the
`DISCOURSE_UNICORN_WORKER_DB_VARIABLES_` prefix.

For example to set `statement_timeout`, one would add the
`DISCOURSE_UNICORN_WORKER_DB_VARIABLES_STATEMENT_TIMEOUT` env with its
corresponding value. Do note that envs with the
`DISCOURSE_UNICORN_WORKER_DB_VARIABLES_` prefix will take precendent
over envs with the `DISCOURSE_DB_VARIABLES_` prefix.
This commit is contained in:
Alan Guo Xiang Tan
2025-10-27 13:45:27 +08:00
committed by GitHub
parent c21a602974
commit 45204e5c55
4 changed files with 65 additions and 3 deletions
+9 -3
View File
@@ -1,3 +1,10 @@
<%
db_variables =
ENV
.filter { |key, _| key.start_with?("DISCOURSE_DB_VARIABLES_") }
.transform_keys { |key| key.slice(("DISCOURSE_DB_VARIABLES_".length)..).downcase }
%>
development:
prepared_statements: false
adapter: postgresql
@@ -11,9 +18,7 @@ development:
### - restart sidekiq if you change this setting
### - rebake all to posts using: `RAILS_ENV=production bundle exec rake posts:rebake`
- "<%= ENV['DISCOURSE_HOSTNAME'] || 'localhost' %>"
variables: <%= ENV.filter { |k,v| k.start_with? 'DISCOURSE_DB_VARIABLES_' }
.transform_keys { |k| k.slice(('DISCOURSE_DB_VARIABLES_'.length)..).downcase }
.to_json %>
variables: <%= db_variables.to_json %>
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
@@ -41,6 +46,7 @@ test:
checkout_timeout: <%= ENV["CHECKOUT_TIMEOUT"] || 5 %>
host_names:
- test.localhost
variables: <%= db_variables.to_json %>
# profile db is used for benchmarking using the script/bench.rb script
profile:
+1
View File
@@ -194,6 +194,7 @@ end
after_fork do |server, worker|
DiscourseEvent.trigger(:web_fork_started)
Discourse.after_unicorn_worker_fork
Discourse.after_fork
SignalTrapLogger.instance.after_fork
+20
View File
@@ -934,6 +934,26 @@ module Discourse
Process.warmup
end
def self.after_unicorn_worker_fork
unicorn_worker_db_variables_prefix = "DISCOURSE_UNICORN_WORKER_DB_VARIABLES_"
db_variables_overrides = {}
ENV.filter do |key, value|
if key.start_with?(unicorn_worker_db_variables_prefix)
db_variables_overrides[
"DISCOURSE_DB_VARIABLES_#{key.sub(unicorn_worker_db_variables_prefix, "")}"
] = value
end
end
if db_variables_overrides.any?
db_variables_overrides.each { |key, value| ENV[key] = value }
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Base.connection_handler.clear_all_connections!(:all)
ActiveRecord::Base.establish_connection
end
end
# all forking servers must call this
# after fork, otherwise Discourse will be
# in a bad state
+35
View File
@@ -67,6 +67,41 @@ RSpec.describe Discourse do
end
end
describe ".after_unicorn_worker_fork" do
around do |example|
original_env = ENV.to_hash
original_config = ActiveRecord::Base.configurations
original_show_statement_timeout =
ActiveRecord::Base.connection.execute("SHOW statement_timeout").first["statement_timeout"]
begin
example.run
ensure
ENV.replace(original_env)
ActiveRecord::Base.configurations = original_config
ActiveRecord::Base.connection_handler.clear_all_connections!(:all)
ActiveRecord::Base.establish_connection
expect(
ActiveRecord::Base.connection.execute("SHOW statement_timeout").first[
"statement_timeout"
],
).to eq(original_show_statement_timeout)
end
end
it "applies worker-specific database variable overrides" do
ENV["DISCOURSE_DB_VARIABLES_STATEMENT_TIMEOUT"] = "5s"
ENV["DISCOURSE_UNICORN_WORKER_DB_VARIABLES_STATEMENT_TIMEOUT"] = "100s"
Discourse.after_unicorn_worker_fork
expect(
ActiveRecord::Base.connection.execute("SHOW statement_timeout").first["statement_timeout"],
).to eq("100s")
end
end
describe ".plugins_sorted_by_name" do
before do
Discourse.stubs(:visible_plugins).returns(