once_cell is way faster

This commit is contained in:
Lev Kokotov
2022-02-10 17:05:20 -08:00
parent 0bec14ba1c
commit 06575eae7b
4 changed files with 31 additions and 18 deletions

2
Cargo.lock generated
View File

@@ -323,6 +323,8 @@ dependencies = [
"bytes", "bytes",
"chrono", "chrono",
"md-5", "md-5",
"num_cpus",
"once_cell",
"rand", "rand",
"regex", "regex",
"serde", "serde",

View File

@@ -18,3 +18,5 @@ toml = "0.5"
serde = "1" serde = "1"
serde_derive = "1" serde_derive = "1"
regex = "1" regex = "1"
num_cpus = "1"
once_cell = "1"

View File

@@ -2,6 +2,7 @@
/// We are pretending to the server in this scenario, /// We are pretending to the server in this scenario,
/// and this module implements that. /// and this module implements that.
use bytes::{Buf, BufMut, BytesMut}; use bytes::{Buf, BufMut, BytesMut};
use once_cell::sync::OnceCell;
use regex::Regex; use regex::Regex;
use tokio::io::{AsyncReadExt, BufReader}; use tokio::io::{AsyncReadExt, BufReader};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
@@ -14,8 +15,11 @@ use crate::pool::{ClientServerMap, ConnectionPool};
use crate::server::Server; use crate::server::Server;
use crate::sharding::Sharder; use crate::sharding::Sharder;
const SHARDING_REGEX: &str = r"SET SHARDING KEY TO '[0-9]+';"; pub const SHARDING_REGEX: &str = r"SET SHARDING KEY TO '[0-9]+';";
const ROLE_REGEX: &str = r"SET SERVER ROLE TO '(PRIMARY|REPLICA)';"; pub const ROLE_REGEX: &str = r"SET SERVER ROLE TO '(PRIMARY|REPLICA)';";
pub static SHARDING_REGEX_RE: OnceCell<Regex> = OnceCell::new();
pub static ROLE_REGEX_RE: OnceCell<Regex> = OnceCell::new();
/// The client state. One of these is created per client. /// The client state. One of these is created per client.
pub struct Client { pub struct Client {
@@ -44,12 +48,6 @@ 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,
// sharding regex
sharding_regex: Regex,
// role detection regex
role_regex: Regex,
} }
impl Client { impl Client {
@@ -61,9 +59,6 @@ impl Client {
client_server_map: ClientServerMap, client_server_map: ClientServerMap,
transaction_mode: bool, transaction_mode: bool,
) -> Result<Client, Error> { ) -> Result<Client, Error> {
let sharding_regex = Regex::new(SHARDING_REGEX).unwrap();
let role_regex = Regex::new(ROLE_REGEX).unwrap();
loop { loop {
// Could be StartupMessage or SSLRequest // Could be StartupMessage or SSLRequest
// which makes this variable length. // which makes this variable length.
@@ -119,8 +114,6 @@ 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,
sharding_regex: sharding_regex,
role_regex: role_regex,
}); });
} }
@@ -140,8 +133,6 @@ 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,
sharding_regex: sharding_regex,
role_regex: role_regex,
}); });
} }
@@ -414,8 +405,12 @@ impl Client {
let len = buf.get_i32(); let len = buf.get_i32();
let query = String::from_utf8_lossy(&buf[..len as usize - 4 - 1]).to_ascii_uppercase(); // Don't read the ternminating null let query = String::from_utf8_lossy(&buf[..len as usize - 4 - 1]).to_ascii_uppercase(); // Don't read the ternminating null
let rgx = match SHARDING_REGEX_RE.get() {
Some(r) => r,
None => return None,
};
if self.sharding_regex.is_match(&query) { if rgx.is_match(&query) {
let shard = query.split("'").collect::<Vec<&str>>()[1]; let shard = query.split("'").collect::<Vec<&str>>()[1];
match shard.parse::<i64>() { match shard.parse::<i64>() {
Ok(shard) => { Ok(shard) => {
@@ -441,10 +436,14 @@ impl Client {
let len = buf.get_i32(); let len = buf.get_i32();
let query = String::from_utf8_lossy(&buf[..len as usize - 4 - 1]).to_ascii_uppercase(); let query = String::from_utf8_lossy(&buf[..len as usize - 4 - 1]).to_ascii_uppercase();
let rgx = match ROLE_REGEX_RE.get() {
Some(r) => r,
None => return None,
};
// Copy / paste from above. If we get one more of these use cases, // Copy / paste from above. If we get one more of these use cases,
// it'll be time to abstract :). // it'll be time to abstract :).
if self.role_regex.is_match(&query) { if rgx.is_match(&query) {
let role = query.split("'").collect::<Vec<&str>>()[1]; let role = query.split("'").collect::<Vec<&str>>()[1];
match role { match role {
"PRIMARY" => Some(Role::Primary), "PRIMARY" => Some(Role::Primary),

View File

@@ -17,11 +17,14 @@ extern crate async_trait;
extern crate bb8; extern crate bb8;
extern crate bytes; extern crate bytes;
extern crate md5; extern crate md5;
extern crate num_cpus;
extern crate once_cell;
extern crate serde; extern crate serde;
extern crate serde_derive; extern crate serde_derive;
extern crate tokio; extern crate tokio;
extern crate toml; extern crate toml;
use regex::Regex;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use std::collections::HashMap; use std::collections::HashMap;
@@ -44,6 +47,13 @@ use pool::{ClientServerMap, ConnectionPool};
async fn main() { async fn main() {
println!("> Welcome to PgCat! Meow."); println!("> Welcome to PgCat! Meow.");
client::SHARDING_REGEX_RE
.set(Regex::new(client::SHARDING_REGEX).unwrap())
.unwrap();
client::ROLE_REGEX_RE
.set(Regex::new(client::ROLE_REGEX).unwrap())
.unwrap();
let config = match config::parse("pgcat.toml").await { let config = match config::parse("pgcat.toml").await {
Ok(config) => config, Ok(config) => config,
Err(err) => { Err(err) => {