Skip to content

Commit 39144cd

Browse files
authored
Add configurable PostgreSQL CCDB connection parameters (#5260)
Adds support for configuring PostgreSQL-specific connection parameters via a nested ccdb.psql config section. SQL parameters (statement_timeout, idle_in_transaction_session_timeout) are applied via Sequel's connect_sqls on every new connection. libpq parameters (keepalives, keepalives_idle, keepalives_interval, keepalives_count) are passed directly to the driver.
1 parent b007384 commit 39144cd

5 files changed

Lines changed: 130 additions & 11 deletions

File tree

lib/cloud_controller/config_schemas/api_schema.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,15 @@ class ApiSchema < VCAP::Config
121121
optional(:connection_expiration_random_delay) => Integer,
122122
optional(:ssl_verify_hostname) => bool,
123123
optional(:ca_cert_path) => String,
124-
optional(:enable_paginate_window) => bool
124+
optional(:enable_paginate_window) => bool,
125+
optional(:psql) => {
126+
optional(:statement_timeout) => Integer,
127+
optional(:idle_in_transaction_session_timeout) => Integer,
128+
optional(:keepalives) => Integer,
129+
optional(:keepalives_idle) => Integer,
130+
optional(:keepalives_interval) => Integer,
131+
optional(:keepalives_count) => Integer
132+
}
125133
},
126134

127135
optional(:redis) => {

lib/cloud_controller/config_schemas/worker_schema.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,15 @@ class WorkerSchema < VCAP::Config
5959
log_db_queries: bool,
6060
ssl_verify_hostname: bool,
6161
connection_validation_timeout: Integer,
62-
optional(:ca_cert_path) => String
62+
optional(:ca_cert_path) => String,
63+
optional(:psql) => {
64+
optional(:statement_timeout) => Integer,
65+
optional(:idle_in_transaction_session_timeout) => Integer,
66+
optional(:keepalives) => Integer,
67+
optional(:keepalives_idle) => Integer,
68+
optional(:keepalives_interval) => Integer,
69+
optional(:keepalives_count) => Integer
70+
}
6371
},
6472

6573
staging: {

lib/cloud_controller/db_connection/postgres_options_factory.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
module VCAP::CloudController
22
module DbConnection
33
class PostgresOptionsFactory
4+
SQL_CONNECTION_PARAMETERS = %i[statement_timeout idle_in_transaction_session_timeout].freeze
5+
LIBPQ_CONNECTION_PARAMETERS = %i[keepalives keepalives_idle keepalives_interval keepalives_count].freeze
6+
47
def self.build(opts)
58
options = {}
69

@@ -9,9 +12,16 @@ def self.build(opts)
912
options[:sslmode] = opts[:ssl_verify_hostname] ? 'verify-full' : 'verify-ca'
1013
end
1114

12-
options[:after_connect] = proc do |connection|
13-
connection.exec("SET time zone 'UTC'")
15+
psql_opts = opts[:psql] || {}
16+
sql_params = psql_opts.slice(*SQL_CONNECTION_PARAMETERS).compact
17+
connect_sqls = ["SET time zone 'UTC'"]
18+
sql_params.each do |key, value|
19+
connect_sqls << "SET #{key} TO '#{value}'"
1420
end
21+
options[:connect_sqls] = connect_sqls
22+
23+
libpq_params = psql_opts.slice(*LIBPQ_CONNECTION_PARAMETERS).compact
24+
options.merge!(libpq_params)
1525

1626
options
1727
end

spec/unit/lib/cloud_controller/db_connection/options_factory_spec.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@
4646
let(:adapter) { 'postgres' }
4747

4848
it 'returns postgres-specific options' do
49-
connection = double('connection', exec: '')
5049
postgres_options = VCAP::CloudController::DbConnection::OptionsFactory.build(required_options)
51-
postgres_options[:after_connect].call(connection)
52-
expect(connection).to have_received(:exec).with("SET time zone 'UTC'")
50+
expect(postgres_options[:connect_sqls]).to include("SET time zone 'UTC'")
5351
end
5452
end
5553
end

spec/unit/lib/cloud_controller/db_connection/postgres_options_factory_spec.rb

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
)
1818
end
1919

20-
it 'sets the timezone via a Proc' do
21-
connection = double('connection', exec: '')
22-
postgres_options[:after_connect].call(connection)
23-
expect(connection).to have_received(:exec).with("SET time zone 'UTC'")
20+
it 'sets the timezone via connect_sqls' do
21+
expect(postgres_options[:connect_sqls]).to include("SET time zone 'UTC'")
2422
end
2523

2624
describe 'when the CA cert path is not set' do
@@ -57,5 +55,102 @@
5755
end
5856
end
5957
end
58+
59+
describe 'connection parameters' do
60+
context 'when no connection parameters are set' do
61+
let(:postgres_options) do
62+
VCAP::CloudController::DbConnection::PostgresOptionsFactory.build(
63+
database: { adapter: 'postgres' }
64+
)
65+
end
66+
67+
it 'only sets the timezone in connect_sqls' do
68+
expect(postgres_options[:connect_sqls]).to eq(["SET time zone 'UTC'"])
69+
end
70+
71+
it 'does not include any keepalive options in the returned hash' do
72+
expect(postgres_options[:keepalives]).to be_nil
73+
expect(postgres_options[:keepalives_idle]).to be_nil
74+
expect(postgres_options[:keepalives_interval]).to be_nil
75+
expect(postgres_options[:keepalives_count]).to be_nil
76+
end
77+
end
78+
79+
context 'when SQL params are set' do
80+
let(:postgres_options) do
81+
VCAP::CloudController::DbConnection::PostgresOptionsFactory.build(
82+
database: { adapter: 'postgres' },
83+
psql: {
84+
statement_timeout: 3_600_000,
85+
idle_in_transaction_session_timeout: 600_000
86+
}
87+
)
88+
end
89+
90+
it 'sets the SQL params via connect_sqls' do
91+
expect(postgres_options[:connect_sqls]).to include("SET time zone 'UTC'")
92+
expect(postgres_options[:connect_sqls]).to include("SET statement_timeout TO '3600000'")
93+
expect(postgres_options[:connect_sqls]).to include("SET idle_in_transaction_session_timeout TO '600000'")
94+
end
95+
96+
it 'does not put SQL params into the returned options hash' do
97+
expect(postgres_options[:statement_timeout]).to be_nil
98+
expect(postgres_options[:idle_in_transaction_session_timeout]).to be_nil
99+
end
100+
end
101+
102+
context 'when libpq keepalive params are set' do
103+
let(:postgres_options) do
104+
VCAP::CloudController::DbConnection::PostgresOptionsFactory.build(
105+
database: { adapter: 'postgres' },
106+
psql: {
107+
keepalives: 1,
108+
keepalives_idle: 30,
109+
keepalives_interval: 10,
110+
keepalives_count: 3
111+
}
112+
)
113+
end
114+
115+
it 'merges keepalive params into the returned options hash' do
116+
expect(postgres_options[:keepalives]).to eq(1)
117+
expect(postgres_options[:keepalives_idle]).to eq(30)
118+
expect(postgres_options[:keepalives_interval]).to eq(10)
119+
expect(postgres_options[:keepalives_count]).to eq(3)
120+
end
121+
122+
it 'does not SET keepalive params via connect_sqls' do
123+
expect(postgres_options[:connect_sqls]).not_to include(match(/SET keepalives/))
124+
end
125+
end
126+
127+
context 'when both SQL and libpq params are set' do
128+
let(:postgres_options) do
129+
VCAP::CloudController::DbConnection::PostgresOptionsFactory.build(
130+
database: { adapter: 'postgres' },
131+
psql: {
132+
statement_timeout: 3_600_000,
133+
keepalives: 1,
134+
keepalives_idle: 30,
135+
keepalives_interval: 10,
136+
keepalives_count: 3
137+
}
138+
)
139+
end
140+
141+
it 'sets SQL params via connect_sqls and merges libpq params into options hash' do
142+
expect(postgres_options[:connect_sqls]).to include("SET statement_timeout TO '3600000'")
143+
expect(postgres_options[:keepalives]).to eq(1)
144+
expect(postgres_options[:keepalives_idle]).to eq(30)
145+
expect(postgres_options[:keepalives_interval]).to eq(10)
146+
expect(postgres_options[:keepalives_count]).to eq(3)
147+
end
148+
149+
it 'does not mix up the two kinds: SQL params not in options hash, libpq params not SET via SQL' do
150+
expect(postgres_options[:statement_timeout]).to be_nil
151+
expect(postgres_options[:connect_sqls]).not_to include(match(/SET keepalives/))
152+
end
153+
end
154+
end
60155
end
61156
end

0 commit comments

Comments
 (0)