Remove `format!` calls from hot path

This commit is contained in:
Daniel Sockwell 2020-04-14 19:53:26 -04:00
parent e100f5760a
commit 8fcd040f21
4 changed files with 70 additions and 32 deletions

View File

@ -19,25 +19,29 @@ impl Timeline {
} }
pub fn to_redis_raw_timeline(&self, hashtag: Option<&String>) -> Result<String> { pub fn to_redis_raw_timeline(&self, hashtag: Option<&String>) -> Result<String> {
use {Content::*, Reach::*, Stream::*}; // TODO -- does this need to account for namespaces?
use {Content::*, Reach::*, Stream::*, TimelineErr::*};
Ok(match self { Ok(match self {
Timeline(Public, Federated, All) => "timeline:public".into(), Timeline(Public, Federated, All) => "timeline:public".to_string(),
Timeline(Public, Local, All) => "timeline:public:local".into(), Timeline(Public, Local, All) => "timeline:public:local".to_string(),
Timeline(Public, Federated, Media) => "timeline:public:media".into(), Timeline(Public, Federated, Media) => "timeline:public:media".to_string(),
Timeline(Public, Local, Media) => "timeline:public:local:media".into(), Timeline(Public, Local, Media) => "timeline:public:local:media".to_string(),
// TODO -- would `.push_str` be faster here? Timeline(Hashtag(_id), Federated, All) => {
Timeline(Hashtag(_id), Federated, All) => format!( ["timeline:hashtag:", hashtag.ok_or(MissingHashtag)?].concat()
"timeline:hashtag:{}", }
hashtag.ok_or(TimelineErr::MissingHashtag)? Timeline(Hashtag(_id), Local, All) => [
), "timeline:hashtag:",
Timeline(Hashtag(_id), Local, All) => format!( hashtag.ok_or(MissingHashtag)?,
"timeline:hashtag:{}:local", ":local",
hashtag.ok_or(TimelineErr::MissingHashtag)? ]
), .concat(),
Timeline(User(id), Federated, All) => format!("timeline:{}", id), Timeline(User(id), Federated, All) => ["timeline:", &id.to_string()].concat(),
Timeline(User(id), Federated, Notification) => format!("timeline:{}:notification", id), Timeline(User(id), Federated, Notification) => {
Timeline(List(id), Federated, All) => format!("timeline:list:{}", id), ["timeline:", &id.to_string(), ":notification"].concat()
Timeline(Direct(id), Federated, All) => format!("timeline:direct:{}", id), }
Timeline(List(id), Federated, All) => ["timeline:list:", &id.to_string()].concat(),
Timeline(Direct(id), Federated, All) => ["timeline:direct:", &id.to_string()].concat(),
Timeline(_one, _two, _three) => Err(TimelineErr::InvalidInput)?, Timeline(_one, _two, _three) => Err(TimelineErr::InvalidInput)?,
}) })
} }
@ -57,8 +61,7 @@ impl Timeline {
[id, "notification"] => Timeline(User(id.parse()?), Federated, Notification), [id, "notification"] => Timeline(User(id.parse()?), Federated, Notification),
["list", id] => Timeline(List(id.parse()?), Federated, All), ["list", id] => Timeline(List(id.parse()?), Federated, All),
["direct", id] => Timeline(Direct(id.parse()?), Federated, All), ["direct", id] => Timeline(Direct(id.parse()?), Federated, All),
// Other endpoints don't exist: [..] => Err(InvalidInput)?, // Other endpoints don't exist
[..] => Err(InvalidInput)?,
}) })
} }

View File

@ -15,12 +15,40 @@ impl RedisCmd {
pub fn into_sendable(self, tl: &str) -> (Vec<u8>, Vec<u8>) { pub fn into_sendable(self, tl: &str) -> (Vec<u8>, Vec<u8>) {
match self { match self {
RedisCmd::Subscribe => ( RedisCmd::Subscribe => (
format!("*2\r\n$9\r\nsubscribe\r\n${}\r\n{}\r\n", tl.len(), tl).into_bytes(), [
format!("*3\r\n$3\r\nSET\r\n${}\r\n{}\r\n$1\r\n1\r\n", tl.len(), tl).into_bytes(), b"*2\r\n$9\r\nsubscribe\r\n$",
tl.len().to_string().as_bytes(),
b"\r\n",
tl.as_bytes(),
b"\r\n",
]
.concat(),
[
b"*3\r\n$3\r\nSET\r\n$",
tl.len().to_string().as_bytes(),
b"\r\n",
tl.as_bytes(),
b"\r\n$1\r\n1\r\n",
]
.concat(),
), ),
RedisCmd::Unsubscribe => ( RedisCmd::Unsubscribe => (
format!("*2\r\n$11\r\nunsubscribe\r\n${}\r\n{}\r\n", tl.len(), tl).into_bytes(), [
format!("*3\r\n$3\r\nSET\r\n${}\r\n{}\r\n$1\r\n0\r\n", tl.len(), tl).into_bytes(), b"*2\r\n$11\r\nunsubscribe\r\n$",
tl.len().to_string().as_bytes(),
b"\r\n",
tl.as_bytes(),
b"\r\n",
]
.concat(),
[
b"*3\r\n$3\r\nSET\r\n$",
tl.len().to_string().as_bytes(),
b"\r\n",
tl.as_bytes(),
b"\r\n$1\r\n0\r\n",
]
.concat(),
), ),
} }
} }

View File

@ -29,7 +29,8 @@ pub struct RedisConn {
impl RedisConn { impl RedisConn {
pub fn new(redis_cfg: &Redis) -> Result<Self> { pub fn new(redis_cfg: &Redis) -> Result<Self> {
let addr = format!("{}:{}", *redis_cfg.host, *redis_cfg.port); let addr = [&*redis_cfg.host, ":", &*redis_cfg.port.to_string()].concat();
let conn = Self::new_connection(&addr, redis_cfg.password.as_ref())?; let conn = Self::new_connection(&addr, redis_cfg.password.as_ref())?;
conn.set_nonblocking(true) conn.set_nonblocking(true)
.map_err(|e| RedisConnErr::with_addr(&addr, e))?; .map_err(|e| RedisConnErr::with_addr(&addr, e))?;
@ -81,7 +82,7 @@ impl RedisConn {
use {Async::*, RedisParseOutput::*}; use {Async::*, RedisParseOutput::*};
let (res, leftover) = match RedisParseOutput::try_from(input) { let (res, leftover) = match RedisParseOutput::try_from(input) {
Ok(Msg(msg)) => match &self.redis_namespace { Ok(Msg(msg)) => match &self.redis_namespace {
Some(ns) if msg.timeline_txt.starts_with(&format!("{}:timeline:", ns)) => { Some(ns) if msg.timeline_txt.starts_with(&[ns, ":timeline:"].concat()) => {
let trimmed_tl = &msg.timeline_txt[ns.len() + ":timeline:".len()..]; let trimmed_tl = &msg.timeline_txt[ns.len() + ":timeline:".len()..];
let tl = Timeline::from_redis_text(trimmed_tl, &mut self.tag_id_cache)?; let tl = Timeline::from_redis_text(trimmed_tl, &mut self.tag_id_cache)?;
let event = msg.event_txt.try_into()?; let event = msg.event_txt.try_into()?;
@ -135,8 +136,17 @@ impl RedisConn {
} }
fn auth_connection(conn: &mut TcpStream, addr: &str, pass: &str) -> Result<()> { fn auth_connection(conn: &mut TcpStream, addr: &str, pass: &str) -> Result<()> {
conn.write_all(&format!("*2\r\n$4\r\nauth\r\n${}\r\n{}\r\n", pass.len(), pass).as_bytes()) conn.write_all(
.map_err(|e| RedisConnErr::with_addr(&addr, e))?; &[
b"*2\r\n$4\r\nauth\r\n$",
pass.len().to_string().as_bytes(),
b"\r\n",
pass.as_bytes(),
b"\r\n",
]
.concat(),
)
.map_err(|e| RedisConnErr::with_addr(&addr, e))?;
let mut buffer = vec![0_u8; 5]; let mut buffer = vec![0_u8; 5];
conn.read_exact(&mut buffer) conn.read_exact(&mut buffer)
.map_err(|e| RedisConnErr::with_addr(&addr, e))?; .map_err(|e| RedisConnErr::with_addr(&addr, e))?;

View File

@ -82,10 +82,7 @@ fn utf8_to_redis_data<'a>(s: &'a str) -> Result<(RedisData, &'a str), RedisParse
":" => parse_redis_int(s), ":" => parse_redis_int(s),
"$" => parse_redis_bulk_string(s), "$" => parse_redis_bulk_string(s),
"*" => parse_redis_array(s), "*" => parse_redis_array(s),
e => Err(InvalidLineStart(format!( e => Err(InvalidLineStart(e.to_string())),
"Encountered invalid initial character `{}` in line `{}`",
e, s
))),
} }
} }