Skip to content

Commit eb14730

Browse files
authored
admin: SHOW CONFIG (#50)
* admin: SHOW CONFIG * test
1 parent 26f75f8 commit eb14730

File tree

3 files changed

+139
-3
lines changed

3 files changed

+139
-3
lines changed

.circleci/run_tests.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ cd ../..
5959

6060
# Admin tests
6161
psql -e -h 127.0.0.1 -p 6432 -d pgbouncer -c 'SHOW STATS' > /dev/null
62+
psql -h 127.0.0.1 -p 6432 -d pgbouncer -c 'RELOAD' > /dev/null
63+
psql -h 127.0.0.1 -p 6432 -d pgbouncer -c 'SHOW CONFIG' > /dev/null
6264
(! psql -e -h 127.0.0.1 -p 6432 -d random_db -c 'SHOW STATS' > /dev/null)
6365

6466
# Start PgCat in debug to demonstrate failover better
@@ -86,9 +88,6 @@ sed -i 's/pool_mode = "transaction"/pool_mode = "session"/' pgcat.toml
8688
# Reload config test
8789
kill -SIGHUP $(pgrep pgcat)
8890

89-
# Reload again with the admin database
90-
psql -h 127.0.0.1 -p 6432 -d pgbouncer -c 'RELOAD' > /dev/null
91-
9291
# Prepared statements that will only work in session mode
9392
pgbench -h 127.0.0.1 -p 6432 -t 500 -c 2 --protocol prepared
9493

src/admin.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use bytes::{Buf, BufMut, BytesMut};
22
use log::{info, trace};
33
use tokio::net::tcp::OwnedWriteHalf;
44

5+
use std::collections::HashMap;
6+
57
use crate::config::{get_config, parse};
68
use crate::constants::{OID_NUMERIC, OID_TEXT};
79
use crate::errors::Error;
@@ -27,6 +29,9 @@ pub async fn handle_admin(stream: &mut OwnedWriteHalf, mut query: BytesMut) -> R
2729
} else if query.starts_with("RELOAD") {
2830
trace!("RELOAD");
2931
reload(stream).await
32+
} else if query.starts_with("SHOW CONFIG") {
33+
trace!("SHOW CONFIG");
34+
show_config(stream).await
3035
} else {
3136
Err(Error::ProtocolSyncError)
3237
}
@@ -61,6 +66,92 @@ pub async fn reload(stream: &mut OwnedWriteHalf) -> Result<(), Error> {
6166
write_all_half(stream, res).await
6267
}
6368

69+
pub async fn show_config(stream: &mut OwnedWriteHalf) -> Result<(), Error> {
70+
let guard = get_config();
71+
let config = &*guard.clone();
72+
let config: HashMap<String, String> = config.into();
73+
drop(guard);
74+
75+
// Configs that cannot be changed dynamically.
76+
let immutables = ["host", "port", "connect_timeout"];
77+
78+
// Columns
79+
let columns = ["key", "value", "default", "changeable"];
80+
81+
// RowDescription
82+
let mut row_desc = BytesMut::new();
83+
row_desc.put_i16(4 as i16); // key, value, default, changeable
84+
85+
for column in columns {
86+
row_desc.put_slice(&format!("{}\0", column).as_bytes());
87+
88+
// Doesn't belong to any table
89+
row_desc.put_i32(0);
90+
91+
// Doesn't belong to any table
92+
row_desc.put_i16(0);
93+
94+
// Data type
95+
row_desc.put_i32(OID_TEXT);
96+
97+
// text size = variable (-1)
98+
row_desc.put_i16(-1);
99+
100+
// Type modifier: none that I know
101+
row_desc.put_i32(-1);
102+
103+
// Format being used: text (0), binary (1)
104+
row_desc.put_i16(0);
105+
}
106+
107+
// Response data
108+
let mut res = BytesMut::new();
109+
res.put_u8(b'T');
110+
res.put_i32(row_desc.len() as i32 + 4);
111+
res.put(row_desc);
112+
113+
// DataRow rows
114+
for (key, value) in config {
115+
let mut data_row = BytesMut::new();
116+
117+
data_row.put_i16(4 as i16); // key, value, default, changeable
118+
119+
let key_bytes = key.as_bytes();
120+
let value = value.as_bytes();
121+
122+
data_row.put_i32(key_bytes.len() as i32);
123+
data_row.put_slice(&key_bytes);
124+
125+
data_row.put_i32(value.len() as i32);
126+
data_row.put_slice(&value);
127+
128+
data_row.put_i32(1 as i32);
129+
data_row.put_slice(&"-".as_bytes());
130+
131+
let changeable = if immutables.iter().filter(|col| *col == &key).count() == 1 {
132+
"no".as_bytes()
133+
} else {
134+
"yes".as_bytes()
135+
};
136+
data_row.put_i32(changeable.len() as i32);
137+
data_row.put_slice(&changeable);
138+
139+
res.put_u8(b'D');
140+
res.put_i32(data_row.len() as i32 + 4);
141+
res.put(data_row);
142+
}
143+
144+
res.put_u8(b'C');
145+
res.put_i32("SHOW CONFIG\0".as_bytes().len() as i32 + 4);
146+
res.put_slice(&"SHOW CONFIG\0".as_bytes());
147+
148+
res.put_u8(b'Z');
149+
res.put_i32(5);
150+
res.put_u8(b'I');
151+
152+
write_all_half(stream, res).await
153+
}
154+
64155
/// SHOW STATS
65156
pub async fn show_stats(stream: &mut OwnedWriteHalf) -> Result<(), Error> {
66157
let columns = [

src/config.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,52 @@ impl Default for Config {
153153
}
154154
}
155155

156+
impl From<&Config> for std::collections::HashMap<String, String> {
157+
fn from(config: &Config) -> HashMap<String, String> {
158+
HashMap::from([
159+
("host".to_string(), config.general.host.to_string()),
160+
("port".to_string(), config.general.port.to_string()),
161+
(
162+
"pool_size".to_string(),
163+
config.general.pool_size.to_string(),
164+
),
165+
(
166+
"pool_mode".to_string(),
167+
config.general.pool_mode.to_string(),
168+
),
169+
(
170+
"connect_timeout".to_string(),
171+
config.general.connect_timeout.to_string(),
172+
),
173+
(
174+
"healthcheck_timeout".to_string(),
175+
config.general.healthcheck_timeout.to_string(),
176+
),
177+
("ban_time".to_string(), config.general.ban_time.to_string()),
178+
(
179+
"statsd_address".to_string(),
180+
config.general.statsd_address.to_string(),
181+
),
182+
(
183+
"default_role".to_string(),
184+
config.query_router.default_role.to_string(),
185+
),
186+
(
187+
"query_parser_enabled".to_string(),
188+
config.query_router.query_parser_enabled.to_string(),
189+
),
190+
(
191+
"primary_reads_enabled".to_string(),
192+
config.query_router.primary_reads_enabled.to_string(),
193+
),
194+
(
195+
"sharding_function".to_string(),
196+
config.query_router.sharding_function.to_string(),
197+
),
198+
])
199+
}
200+
}
201+
156202
impl Config {
157203
pub fn show(&self) {
158204
info!("Pool size: {}", self.general.pool_size);

0 commit comments

Comments
 (0)