mirror of
https://github.com/postgresml/pgcat.git
synced 2026-03-26 18:36:28 +00:00
Merge pull request #8 from levkk/default-role
add default server role; bug fix
This commit is contained in:
12
pgcat.toml
12
pgcat.toml
@@ -69,3 +69,15 @@ servers = [
|
|||||||
# [ "127.0.1.1", 5432, "replica" ],
|
# [ "127.0.1.1", 5432, "replica" ],
|
||||||
]
|
]
|
||||||
database = "shard2"
|
database = "shard2"
|
||||||
|
|
||||||
|
|
||||||
|
# Settings for our query routing layer.
|
||||||
|
[query_router]
|
||||||
|
|
||||||
|
# If the client doesn't specify, route traffic to
|
||||||
|
# this role by default.
|
||||||
|
#
|
||||||
|
# any: round-robin between primary and replicas,
|
||||||
|
# replica: round-robin between replicas only without touching the primary,
|
||||||
|
# primary: all queries go to the primary unless otherwise specified.
|
||||||
|
default_role = "any"
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ pub struct Client {
|
|||||||
// Clients are mapped to servers while they use them. This allows a client
|
// Clients are mapped to servers while they use them. This allows a client
|
||||||
// to connect and cancel a query.
|
// to connect and cancel a query.
|
||||||
client_server_map: ClientServerMap,
|
client_server_map: ClientServerMap,
|
||||||
|
|
||||||
|
// Unless client specifies, route queries to the servers that have this role,
|
||||||
|
// e.g. primary or replicas or any.
|
||||||
|
default_server_role: Option<Role>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
@@ -58,6 +62,7 @@ impl Client {
|
|||||||
mut stream: TcpStream,
|
mut stream: TcpStream,
|
||||||
client_server_map: ClientServerMap,
|
client_server_map: ClientServerMap,
|
||||||
transaction_mode: bool,
|
transaction_mode: bool,
|
||||||
|
default_server_role: Option<Role>,
|
||||||
) -> Result<Client, Error> {
|
) -> Result<Client, Error> {
|
||||||
loop {
|
loop {
|
||||||
// Could be StartupMessage or SSLRequest
|
// Could be StartupMessage or SSLRequest
|
||||||
@@ -114,6 +119,7 @@ impl Client {
|
|||||||
process_id: process_id,
|
process_id: process_id,
|
||||||
secret_key: secret_key,
|
secret_key: secret_key,
|
||||||
client_server_map: client_server_map,
|
client_server_map: client_server_map,
|
||||||
|
default_server_role: default_server_role,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +139,7 @@ impl Client {
|
|||||||
process_id: process_id,
|
process_id: process_id,
|
||||||
secret_key: secret_key,
|
secret_key: secret_key,
|
||||||
client_server_map: client_server_map,
|
client_server_map: client_server_map,
|
||||||
|
default_server_role: default_server_role,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +179,7 @@ impl Client {
|
|||||||
let mut shard: Option<usize> = None;
|
let mut shard: Option<usize> = None;
|
||||||
|
|
||||||
// Active database role we want to talk to, e.g. primary or replica.
|
// Active database role we want to talk to, e.g. primary or replica.
|
||||||
let mut role: Option<Role> = None;
|
let mut role: Option<Role> = self.default_server_role;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Read a complete message from the client, which normally would be
|
// Read a complete message from the client, which normally would be
|
||||||
@@ -275,7 +282,7 @@ impl Client {
|
|||||||
// Release server
|
// Release server
|
||||||
if !server.in_transaction() && self.transaction_mode {
|
if !server.in_transaction() && self.transaction_mode {
|
||||||
shard = None;
|
shard = None;
|
||||||
role = None;
|
role = self.default_server_role;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +345,7 @@ impl Client {
|
|||||||
// Release server
|
// Release server
|
||||||
if !server.in_transaction() && self.transaction_mode {
|
if !server.in_transaction() && self.transaction_mode {
|
||||||
shard = None;
|
shard = None;
|
||||||
role = None;
|
role = self.default_server_role;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -366,7 +373,7 @@ impl Client {
|
|||||||
if !server.in_transaction() && self.transaction_mode {
|
if !server.in_transaction() && self.transaction_mode {
|
||||||
println!("Releasing after copy done");
|
println!("Releasing after copy done");
|
||||||
shard = None;
|
shard = None;
|
||||||
role = None;
|
role = self.default_server_role;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,7 +389,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Release the server from being mine. I can't cancel its queries anymore.
|
/// Release the server from being mine. I can't cancel its queries anymore.
|
||||||
pub fn release(&mut self) {
|
pub fn release(&self) {
|
||||||
let mut guard = self.client_server_map.lock().unwrap();
|
let mut guard = self.client_server_map.lock().unwrap();
|
||||||
guard.remove(&(self.process_id, self.secret_key));
|
guard.remove(&(self.process_id, self.secret_key));
|
||||||
}
|
}
|
||||||
@@ -390,7 +397,7 @@ impl Client {
|
|||||||
/// Determine if the query is part of our special syntax, extract
|
/// Determine if the query is part of our special syntax, extract
|
||||||
/// the shard key, and return the shard to query based on Postgres'
|
/// the shard key, and return the shard to query based on Postgres'
|
||||||
/// PARTITION BY HASH function.
|
/// PARTITION BY HASH function.
|
||||||
fn select_shard(&mut self, mut buf: BytesMut, shards: usize) -> Option<usize> {
|
fn select_shard(&self, mut buf: BytesMut, shards: usize) -> Option<usize> {
|
||||||
let code = buf.get_u8() as char;
|
let code = buf.get_u8() as char;
|
||||||
|
|
||||||
// Only supporting simpe protocol here, so
|
// Only supporting simpe protocol here, so
|
||||||
@@ -425,7 +432,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pick a primary or a replica from the pool.
|
// Pick a primary or a replica from the pool.
|
||||||
fn select_role(&mut self, mut buf: BytesMut) -> Option<Role> {
|
fn select_role(&self, mut buf: BytesMut) -> Option<Role> {
|
||||||
let code = buf.get_u8() as char;
|
let code = buf.get_u8() as char;
|
||||||
|
|
||||||
// Same story as select_shard() above.
|
// Same story as select_shard() above.
|
||||||
|
|||||||
@@ -43,11 +43,17 @@ pub struct Shard {
|
|||||||
pub database: String,
|
pub database: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
pub struct QueryRouter {
|
||||||
|
pub default_role: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub general: General,
|
pub general: General,
|
||||||
pub user: User,
|
pub user: User,
|
||||||
pub shards: HashMap<String, Shard>,
|
pub shards: HashMap<String, Shard>,
|
||||||
|
pub query_router: QueryRouter,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the config.
|
/// Parse the config.
|
||||||
@@ -118,6 +124,19 @@ pub async fn parse(path: &str) -> Result<Config, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match config.query_router.default_role.as_ref() {
|
||||||
|
"any" => (),
|
||||||
|
"primary" => (),
|
||||||
|
"replica" => (),
|
||||||
|
other => {
|
||||||
|
println!(
|
||||||
|
"> Query router default_role must be 'primary', 'replica', or 'any', got: '{}'",
|
||||||
|
other
|
||||||
|
);
|
||||||
|
return Err(Error::BadConfig);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,5 +151,6 @@ mod test {
|
|||||||
assert_eq!(config.shards.len(), 3);
|
assert_eq!(config.shards.len(), 3);
|
||||||
assert_eq!(config.shards["1"].servers[0].0, "127.0.0.1");
|
assert_eq!(config.shards["1"].servers[0].0, "127.0.0.1");
|
||||||
assert_eq!(config.shards["0"].servers[0].2, "primary");
|
assert_eq!(config.shards["0"].servers[0].2, "primary");
|
||||||
|
assert_eq!(config.query_router.default_role, "any");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/main.rs
19
src/main.rs
@@ -40,6 +40,7 @@ mod sharding;
|
|||||||
|
|
||||||
// Support for query cancellation: this maps our process_ids and
|
// Support for query cancellation: this maps our process_ids and
|
||||||
// secret keys to the backend's.
|
// secret keys to the backend's.
|
||||||
|
use config::Role;
|
||||||
use pool::{ClientServerMap, ConnectionPool};
|
use pool::{ClientServerMap, ConnectionPool};
|
||||||
|
|
||||||
/// Main!
|
/// Main!
|
||||||
@@ -87,6 +88,15 @@ async fn main() {
|
|||||||
|
|
||||||
let pool = ConnectionPool::from_config(config.clone(), client_server_map.clone()).await;
|
let pool = ConnectionPool::from_config(config.clone(), client_server_map.clone()).await;
|
||||||
let transaction_mode = config.general.pool_mode == "transaction";
|
let transaction_mode = config.general.pool_mode == "transaction";
|
||||||
|
let default_server_role = match config.query_router.default_role.as_ref() {
|
||||||
|
"any" => None,
|
||||||
|
"primary" => Some(Role::Primary),
|
||||||
|
"replica" => Some(Role::Replica),
|
||||||
|
_ => {
|
||||||
|
println!("> Config error, got unexpected query_router.default_role.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
println!("> Waiting for clients...");
|
println!("> Waiting for clients...");
|
||||||
|
|
||||||
@@ -109,7 +119,14 @@ async fn main() {
|
|||||||
addr, transaction_mode
|
addr, transaction_mode
|
||||||
);
|
);
|
||||||
|
|
||||||
match client::Client::startup(socket, client_server_map, transaction_mode).await {
|
match client::Client::startup(
|
||||||
|
socket,
|
||||||
|
client_server_map,
|
||||||
|
transaction_mode,
|
||||||
|
default_server_role,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(mut client) => {
|
Ok(mut client) => {
|
||||||
println!(">> Client {:?} authenticated successfully!", addr);
|
println!(">> Client {:?} authenticated successfully!", addr);
|
||||||
|
|
||||||
|
|||||||
@@ -124,10 +124,7 @@ impl ConnectionPool {
|
|||||||
// Make sure if a specific role is requested, it's available in the pool.
|
// Make sure if a specific role is requested, it's available in the pool.
|
||||||
match role {
|
match role {
|
||||||
Some(role) => {
|
Some(role) => {
|
||||||
let role_count = addresses
|
let role_count = addresses.iter().filter(|&db| db.role == role).count();
|
||||||
.iter()
|
|
||||||
.filter(|&db| db.role == Role::Primary)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
if role_count == 0 {
|
if role_count == 0 {
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
Reference in New Issue
Block a user