mirror of
https://github.com/postgresml/pgcat.git
synced 2026-03-28 03:06:29 +00:00
Prevent clients from sticking to old pools after config update (#113)
* Re-acquire pool at the beginning of Protocol loop * Fix query router + add tests for recycling behavior
This commit is contained in:
committed by
GitHub
parent
3719c22322
commit
7592339092
@@ -11,7 +11,7 @@ use crate::config::get_config;
|
|||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::errors::Error;
|
use crate::errors::Error;
|
||||||
use crate::messages::*;
|
use crate::messages::*;
|
||||||
use crate::pool::{get_pool, ClientServerMap, ConnectionPool};
|
use crate::pool::{get_pool, ClientServerMap};
|
||||||
use crate::query_router::{Command, QueryRouter};
|
use crate::query_router::{Command, QueryRouter};
|
||||||
use crate::server::Server;
|
use crate::server::Server;
|
||||||
use crate::stats::{get_reporter, Reporter};
|
use crate::stats::{get_reporter, Reporter};
|
||||||
@@ -73,8 +73,13 @@ pub struct Client<S, T> {
|
|||||||
/// Last server process id we talked to.
|
/// Last server process id we talked to.
|
||||||
last_server_id: Option<i32>,
|
last_server_id: Option<i32>,
|
||||||
|
|
||||||
target_pool: ConnectionPool,
|
/// Name of the server pool for this client (This comes from the database name in the connection string)
|
||||||
|
target_pool_name: String,
|
||||||
|
|
||||||
|
/// Postgres user for this client (This comes from the user in the connection string)
|
||||||
|
target_user_name: String,
|
||||||
|
|
||||||
|
/// Used to notify clients about an impending shutdown
|
||||||
shutdown_event_receiver: Receiver<()>,
|
shutdown_event_receiver: Receiver<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,19 +310,19 @@ where
|
|||||||
|
|
||||||
trace!("Got StartupMessage");
|
trace!("Got StartupMessage");
|
||||||
let parameters = parse_startup(bytes.clone())?;
|
let parameters = parse_startup(bytes.clone())?;
|
||||||
let database = match parameters.get("database") {
|
let target_pool_name = match parameters.get("database") {
|
||||||
Some(db) => db,
|
Some(db) => db,
|
||||||
None => return Err(Error::ClientError),
|
None => return Err(Error::ClientError),
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = match parameters.get("user") {
|
let target_user_name = match parameters.get("user") {
|
||||||
Some(user) => user,
|
Some(user) => user,
|
||||||
None => return Err(Error::ClientError),
|
None => return Err(Error::ClientError),
|
||||||
};
|
};
|
||||||
|
|
||||||
let admin = ["pgcat", "pgbouncer"]
|
let admin = ["pgcat", "pgbouncer"]
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|db| *db == &database)
|
.filter(|db| *db == &target_pool_name)
|
||||||
.count()
|
.count()
|
||||||
== 1;
|
== 1;
|
||||||
|
|
||||||
@@ -352,7 +357,7 @@ where
|
|||||||
Err(_) => return Err(Error::SocketError),
|
Err(_) => return Err(Error::SocketError),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (target_pool, transaction_mode, server_info) = if admin {
|
let (transaction_mode, server_info) = if admin {
|
||||||
let correct_user = config.general.admin_username.as_str();
|
let correct_user = config.general.admin_username.as_str();
|
||||||
let correct_password = config.general.admin_password.as_str();
|
let correct_password = config.general.admin_password.as_str();
|
||||||
|
|
||||||
@@ -360,23 +365,20 @@ where
|
|||||||
let password_hash = md5_hash_password(correct_user, correct_password, &salt);
|
let password_hash = md5_hash_password(correct_user, correct_password, &salt);
|
||||||
if password_hash != password_response {
|
if password_hash != password_response {
|
||||||
debug!("Password authentication failed");
|
debug!("Password authentication failed");
|
||||||
wrong_password(&mut write, user).await?;
|
wrong_password(&mut write, target_user_name).await?;
|
||||||
return Err(Error::ClientError);
|
return Err(Error::ClientError);
|
||||||
}
|
}
|
||||||
(
|
|
||||||
ConnectionPool::default(),
|
(false, generate_server_info_for_admin())
|
||||||
false,
|
|
||||||
generate_server_info_for_admin(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
let target_pool = match get_pool(database.clone(), user.clone()) {
|
let target_pool = match get_pool(target_pool_name.clone(), target_user_name.clone()) {
|
||||||
Some(pool) => pool,
|
Some(pool) => pool,
|
||||||
None => {
|
None => {
|
||||||
error_response(
|
error_response(
|
||||||
&mut write,
|
&mut write,
|
||||||
&format!(
|
&format!(
|
||||||
"No pool configured for database: {:?}, user: {:?}",
|
"No pool configured for database: {:?}, user: {:?}",
|
||||||
database, user
|
target_pool_name, target_user_name
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -387,14 +389,14 @@ where
|
|||||||
let server_info = target_pool.server_info();
|
let server_info = target_pool.server_info();
|
||||||
// Compare server and client hashes.
|
// Compare server and client hashes.
|
||||||
let correct_password = target_pool.settings.user.password.as_str();
|
let correct_password = target_pool.settings.user.password.as_str();
|
||||||
let password_hash = md5_hash_password(user, correct_password, &salt);
|
let password_hash = md5_hash_password(&target_user_name, correct_password, &salt);
|
||||||
|
|
||||||
if password_hash != password_response {
|
if password_hash != password_response {
|
||||||
debug!("Password authentication failed");
|
debug!("Password authentication failed");
|
||||||
wrong_password(&mut write, user).await?;
|
wrong_password(&mut write, &target_user_name).await?;
|
||||||
return Err(Error::ClientError);
|
return Err(Error::ClientError);
|
||||||
}
|
}
|
||||||
(target_pool, transaction_mode, server_info)
|
(transaction_mode, server_info)
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Password authentication successful");
|
debug!("Password authentication successful");
|
||||||
@@ -424,7 +426,8 @@ where
|
|||||||
admin: admin,
|
admin: admin,
|
||||||
last_address_id: None,
|
last_address_id: None,
|
||||||
last_server_id: None,
|
last_server_id: None,
|
||||||
target_pool: target_pool,
|
target_pool_name: target_pool_name.clone(),
|
||||||
|
target_user_name: target_user_name.clone(),
|
||||||
shutdown_event_receiver: shutdown_event_receiver,
|
shutdown_event_receiver: shutdown_event_receiver,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -455,7 +458,8 @@ where
|
|||||||
admin: false,
|
admin: false,
|
||||||
last_address_id: None,
|
last_address_id: None,
|
||||||
last_server_id: None,
|
last_server_id: None,
|
||||||
target_pool: ConnectionPool::default(),
|
target_pool_name: String::from("undefined"),
|
||||||
|
target_user_name: String::from("undefined"),
|
||||||
shutdown_event_receiver: shutdown_event_receiver,
|
shutdown_event_receiver: shutdown_event_receiver,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -494,7 +498,7 @@ where
|
|||||||
|
|
||||||
// The query router determines where the query is going to go,
|
// The query router determines where the query is going to go,
|
||||||
// e.g. primary, replica, which shard.
|
// e.g. primary, replica, which shard.
|
||||||
let mut query_router = QueryRouter::new(self.target_pool.clone());
|
let mut query_router = QueryRouter::new();
|
||||||
let mut round_robin = 0;
|
let mut round_robin = 0;
|
||||||
|
|
||||||
// Our custom protocol loop.
|
// Our custom protocol loop.
|
||||||
@@ -520,11 +524,6 @@ where
|
|||||||
message_result = read_message(&mut self.read) => message_result?
|
message_result = read_message(&mut self.read) => message_result?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get a pool instance referenced by the most up-to-date
|
|
||||||
// pointer. This ensures we always read the latest config
|
|
||||||
// when starting a query.
|
|
||||||
let mut pool = self.target_pool.clone();
|
|
||||||
|
|
||||||
// Avoid taking a server if the client just wants to disconnect.
|
// Avoid taking a server if the client just wants to disconnect.
|
||||||
if message[0] as char == 'X' {
|
if message[0] as char == 'X' {
|
||||||
debug!("Client disconnecting");
|
debug!("Client disconnecting");
|
||||||
@@ -538,6 +537,25 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a pool instance referenced by the most up-to-date
|
||||||
|
// pointer. This ensures we always read the latest config
|
||||||
|
// when starting a query.
|
||||||
|
let mut pool =
|
||||||
|
match get_pool(self.target_pool_name.clone(), self.target_user_name.clone()) {
|
||||||
|
Some(pool) => pool,
|
||||||
|
None => {
|
||||||
|
error_response(
|
||||||
|
&mut self.write,
|
||||||
|
&format!(
|
||||||
|
"No pool configured for database: {:?}, user: {:?}",
|
||||||
|
self.target_pool_name, self.target_user_name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
return Err(Error::ClientError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
query_router.update_pool_settings(pool.settings.clone());
|
||||||
let current_shard = query_router.shard();
|
let current_shard = query_router.shard();
|
||||||
|
|
||||||
// Handle all custom protocol commands, if any.
|
// Handle all custom protocol commands, if any.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use sqlparser::dialect::PostgreSqlDialect;
|
|||||||
use sqlparser::parser::Parser;
|
use sqlparser::parser::Parser;
|
||||||
|
|
||||||
use crate::config::Role;
|
use crate::config::Role;
|
||||||
use crate::pool::{ConnectionPool, PoolSettings};
|
use crate::pool::PoolSettings;
|
||||||
use crate::sharding::{Sharder, ShardingFunction};
|
use crate::sharding::{Sharder, ShardingFunction};
|
||||||
|
|
||||||
/// Regexes used to parse custom commands.
|
/// Regexes used to parse custom commands.
|
||||||
@@ -91,16 +91,20 @@ impl QueryRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new instance of the query router. Each client gets its own.
|
/// Create a new instance of the query router. Each client gets its own.
|
||||||
pub fn new(target_pool: ConnectionPool) -> QueryRouter {
|
pub fn new() -> QueryRouter {
|
||||||
QueryRouter {
|
QueryRouter {
|
||||||
active_shard: None,
|
active_shard: None,
|
||||||
active_role: None,
|
active_role: None,
|
||||||
query_parser_enabled: target_pool.settings.query_parser_enabled,
|
query_parser_enabled: false,
|
||||||
primary_reads_enabled: target_pool.settings.primary_reads_enabled,
|
primary_reads_enabled: false,
|
||||||
pool_settings: target_pool.settings,
|
pool_settings: PoolSettings::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_pool_settings(&mut self, pool_settings: PoolSettings) {
|
||||||
|
self.pool_settings = pool_settings;
|
||||||
|
}
|
||||||
|
|
||||||
/// Try to parse a command and execute it.
|
/// Try to parse a command and execute it.
|
||||||
pub fn try_execute_command(&mut self, mut buf: BytesMut) -> Option<(Command, String)> {
|
pub fn try_execute_command(&mut self, mut buf: BytesMut) -> Option<(Command, String)> {
|
||||||
let code = buf.get_u8() as char;
|
let code = buf.get_u8() as char;
|
||||||
@@ -363,6 +367,8 @@ impl QueryRouter {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::messages::simple_query;
|
use crate::messages::simple_query;
|
||||||
use bytes::BufMut;
|
use bytes::BufMut;
|
||||||
@@ -370,7 +376,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_defaults() {
|
fn test_defaults() {
|
||||||
QueryRouter::setup();
|
QueryRouter::setup();
|
||||||
let qr = QueryRouter::new(ConnectionPool::default());
|
let qr = QueryRouter::new();
|
||||||
|
|
||||||
assert_eq!(qr.role(), None);
|
assert_eq!(qr.role(), None);
|
||||||
}
|
}
|
||||||
@@ -378,7 +384,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_infer_role_replica() {
|
fn test_infer_role_replica() {
|
||||||
QueryRouter::setup();
|
QueryRouter::setup();
|
||||||
let mut qr = QueryRouter::new(ConnectionPool::default());
|
let mut qr = QueryRouter::new();
|
||||||
assert!(qr.try_execute_command(simple_query("SET SERVER ROLE TO 'auto'")) != None);
|
assert!(qr.try_execute_command(simple_query("SET SERVER ROLE TO 'auto'")) != None);
|
||||||
assert_eq!(qr.query_parser_enabled(), true);
|
assert_eq!(qr.query_parser_enabled(), true);
|
||||||
|
|
||||||
@@ -402,7 +408,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_infer_role_primary() {
|
fn test_infer_role_primary() {
|
||||||
QueryRouter::setup();
|
QueryRouter::setup();
|
||||||
let mut qr = QueryRouter::new(ConnectionPool::default());
|
let mut qr = QueryRouter::new();
|
||||||
|
|
||||||
let queries = vec![
|
let queries = vec![
|
||||||
simple_query("UPDATE items SET name = 'pumpkin' WHERE id = 5"),
|
simple_query("UPDATE items SET name = 'pumpkin' WHERE id = 5"),
|
||||||
@@ -421,7 +427,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_infer_role_primary_reads_enabled() {
|
fn test_infer_role_primary_reads_enabled() {
|
||||||
QueryRouter::setup();
|
QueryRouter::setup();
|
||||||
let mut qr = QueryRouter::new(ConnectionPool::default());
|
let mut qr = QueryRouter::new();
|
||||||
let query = simple_query("SELECT * FROM items WHERE id = 5");
|
let query = simple_query("SELECT * FROM items WHERE id = 5");
|
||||||
assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO on")) != None);
|
assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO on")) != None);
|
||||||
|
|
||||||
@@ -432,7 +438,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_infer_role_parse_prepared() {
|
fn test_infer_role_parse_prepared() {
|
||||||
QueryRouter::setup();
|
QueryRouter::setup();
|
||||||
let mut qr = QueryRouter::new(ConnectionPool::default());
|
let mut qr = QueryRouter::new();
|
||||||
qr.try_execute_command(simple_query("SET SERVER ROLE TO 'auto'"));
|
qr.try_execute_command(simple_query("SET SERVER ROLE TO 'auto'"));
|
||||||
assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO off")) != None);
|
assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO off")) != None);
|
||||||
|
|
||||||
@@ -523,7 +529,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_try_execute_command() {
|
fn test_try_execute_command() {
|
||||||
QueryRouter::setup();
|
QueryRouter::setup();
|
||||||
let mut qr = QueryRouter::new(ConnectionPool::default());
|
let mut qr = QueryRouter::new();
|
||||||
|
|
||||||
// SetShardingKey
|
// SetShardingKey
|
||||||
let query = simple_query("SET SHARDING KEY TO 13");
|
let query = simple_query("SET SHARDING KEY TO 13");
|
||||||
@@ -600,7 +606,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_enable_query_parser() {
|
fn test_enable_query_parser() {
|
||||||
QueryRouter::setup();
|
QueryRouter::setup();
|
||||||
let mut qr = QueryRouter::new(ConnectionPool::default());
|
let mut qr = QueryRouter::new();
|
||||||
let query = simple_query("SET SERVER ROLE TO 'auto'");
|
let query = simple_query("SET SERVER ROLE TO 'auto'");
|
||||||
assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO off")) != None);
|
assert!(qr.try_execute_command(simple_query("SET PRIMARY READS TO off")) != None);
|
||||||
|
|
||||||
@@ -621,4 +627,43 @@ mod test {
|
|||||||
assert!(qr.try_execute_command(query) != None);
|
assert!(qr.try_execute_command(query) != None);
|
||||||
assert!(qr.query_parser_enabled());
|
assert!(qr.query_parser_enabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_from_pool_settings() {
|
||||||
|
QueryRouter::setup();
|
||||||
|
|
||||||
|
let pool_settings = PoolSettings {
|
||||||
|
pool_mode: "transaction".to_string(),
|
||||||
|
shards: HashMap::default(),
|
||||||
|
user: crate::config::User::default(),
|
||||||
|
default_role: Role::Replica.to_string(),
|
||||||
|
query_parser_enabled: true,
|
||||||
|
primary_reads_enabled: false,
|
||||||
|
sharding_function: "pg_bigint_hash".to_string(),
|
||||||
|
};
|
||||||
|
let mut qr = QueryRouter::new();
|
||||||
|
assert_eq!(qr.active_role, None);
|
||||||
|
assert_eq!(qr.active_shard, None);
|
||||||
|
assert_eq!(qr.query_parser_enabled, false);
|
||||||
|
assert_eq!(qr.primary_reads_enabled, false);
|
||||||
|
|
||||||
|
// Internal state must not be changed due to this, only defaults
|
||||||
|
qr.update_pool_settings(pool_settings.clone());
|
||||||
|
|
||||||
|
assert_eq!(qr.active_role, None);
|
||||||
|
assert_eq!(qr.active_shard, None);
|
||||||
|
assert_eq!(qr.query_parser_enabled, false);
|
||||||
|
assert_eq!(qr.primary_reads_enabled, false);
|
||||||
|
|
||||||
|
let q1 = simple_query("SET SERVER ROLE TO 'primary'");
|
||||||
|
assert!(qr.try_execute_command(q1) != None);
|
||||||
|
assert_eq!(qr.active_role.unwrap(), Role::Primary);
|
||||||
|
|
||||||
|
let q2 = simple_query("SET SERVER ROLE TO 'default'");
|
||||||
|
assert!(qr.try_execute_command(q2) != None);
|
||||||
|
assert_eq!(
|
||||||
|
qr.active_role.unwrap().to_string(),
|
||||||
|
pool_settings.clone().default_role
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
2.7.1
|
3.0.0
|
||||||
|
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ source "https://rubygems.org"
|
|||||||
gem "pg"
|
gem "pg"
|
||||||
gem "activerecord"
|
gem "activerecord"
|
||||||
gem "rubocop"
|
gem "rubocop"
|
||||||
|
gem "toml", "~> 0.3.0"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ GEM
|
|||||||
parallel (1.22.1)
|
parallel (1.22.1)
|
||||||
parser (3.1.2.0)
|
parser (3.1.2.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
|
parslet (2.0.0)
|
||||||
pg (1.3.2)
|
pg (1.3.2)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
regexp_parser (2.3.1)
|
regexp_parser (2.3.1)
|
||||||
@@ -35,17 +36,21 @@ GEM
|
|||||||
rubocop-ast (1.17.0)
|
rubocop-ast (1.17.0)
|
||||||
parser (>= 3.1.1.0)
|
parser (>= 3.1.1.0)
|
||||||
ruby-progressbar (1.11.0)
|
ruby-progressbar (1.11.0)
|
||||||
|
toml (0.3.0)
|
||||||
|
parslet (>= 1.8.0, < 3.0.0)
|
||||||
tzinfo (2.0.4)
|
tzinfo (2.0.4)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (2.1.0)
|
unicode-display_width (2.1.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
|
arm64-darwin-21
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
activerecord
|
activerecord
|
||||||
pg
|
pg
|
||||||
rubocop
|
rubocop
|
||||||
|
toml (~> 0.3.0)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.7
|
2.3.7
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
require 'active_record'
|
require 'active_record'
|
||||||
require 'pg'
|
require 'pg'
|
||||||
|
require 'toml'
|
||||||
|
|
||||||
$stdout.sync = true
|
$stdout.sync = true
|
||||||
|
|
||||||
@@ -141,3 +142,62 @@ def test_server_parameters
|
|||||||
|
|
||||||
puts 'Server parameters ok'
|
puts 'Server parameters ok'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigEditor
|
||||||
|
def initialize
|
||||||
|
@original_config_text = File.read('../../.circleci/pgcat.toml')
|
||||||
|
text_to_load = @original_config_text.gsub("5432", "\"5432\"")
|
||||||
|
|
||||||
|
@original_configs = TOML.load(text_to_load)
|
||||||
|
end
|
||||||
|
|
||||||
|
def original_configs
|
||||||
|
TOML.load(TOML::Generator.new(@original_configs).body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_modified_configs(new_configs)
|
||||||
|
text_to_write = TOML::Generator.new(new_configs).body
|
||||||
|
text_to_write = text_to_write.gsub("\"5432\"", "5432")
|
||||||
|
File.write('../../.circleci/pgcat.toml', text_to_write)
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
File.write('../../.circleci/pgcat.toml', @original_config_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_reload_pool_recycling
|
||||||
|
admin_conn = PG::connect("postgres://admin_user:admin_pass@127.0.0.1:6432/pgcat")
|
||||||
|
server_conn = PG::connect("postgres://sharding_user:sharding_user@127.0.0.1:6432/sharded_db?application_name=testing_pgcat")
|
||||||
|
|
||||||
|
server_conn.async_exec("BEGIN")
|
||||||
|
conf_editor = ConfigEditor.new
|
||||||
|
new_configs = conf_editor.original_configs
|
||||||
|
|
||||||
|
# swap shards
|
||||||
|
new_configs["pools"]["sharded_db"]["shards"]["0"]["database"] = "shard1"
|
||||||
|
new_configs["pools"]["sharded_db"]["shards"]["1"]["database"] = "shard0"
|
||||||
|
|
||||||
|
raise StandardError if server_conn.async_exec("SELECT current_database();")[0]["current_database"] != 'shard0'
|
||||||
|
conf_editor.with_modified_configs(new_configs) { admin_conn.async_exec("RELOAD") }
|
||||||
|
raise StandardError if server_conn.async_exec("SELECT current_database();")[0]["current_database"] != 'shard0'
|
||||||
|
server_conn.async_exec("COMMIT;")
|
||||||
|
|
||||||
|
# Transaction finished, client should get new configs
|
||||||
|
raise StandardError if server_conn.async_exec("SELECT current_database();")[0]["current_database"] != 'shard1'
|
||||||
|
server_conn.close()
|
||||||
|
|
||||||
|
# New connection should get new configs
|
||||||
|
server_conn = PG::connect("postgres://sharding_user:sharding_user@127.0.0.1:6432/sharded_db?application_name=testing_pgcat")
|
||||||
|
raise StandardError if server_conn.async_exec("SELECT current_database();")[0]["current_database"] != 'shard1'
|
||||||
|
|
||||||
|
ensure
|
||||||
|
admin_conn.async_exec("RELOAD") # Go back to old state
|
||||||
|
admin_conn.close
|
||||||
|
server_conn.close
|
||||||
|
puts "Pool Recycling okay!"
|
||||||
|
end
|
||||||
|
|
||||||
|
test_reload_pool_recycling
|
||||||
|
|||||||
Reference in New Issue
Block a user