mirror of
https://github.com/mastodon/flodgatt
synced 2025-04-11 22:58:25 +02:00
Functional config (#59)
This commit is contained in:
parent
7c40f7173f
commit
9d96907406
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -386,17 +386,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flodgatt"
|
name = "flodgatt"
|
||||||
version = "0.3.5"
|
version = "0.3.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dotenv 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dotenv 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"postgres 0.16.0-rc.2 (git+https://github.com/sfackler/rust-postgres.git)",
|
"postgres 0.16.0-rc.2 (git+https://github.com/sfackler/rust-postgres.git)",
|
||||||
"postgres-openssl 0.2.0-rc.1 (git+https://github.com/sfackler/rust-postgres.git)",
|
"postgres-openssl 0.2.0-rc.1 (git+https://github.com/sfackler/rust-postgres.git)",
|
||||||
"pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "flodgatt"
|
name = "flodgatt"
|
||||||
description = "A blazingly fast drop-in replacement for the Mastodon streaming api server"
|
description = "A blazingly fast drop-in replacement for the Mastodon streaming api server"
|
||||||
version = "0.3.5"
|
version = "0.3.6"
|
||||||
authors = ["Daniel Long Sockwell <daniel@codesections.com", "Julian Laubstein <contact@julianlaubstein.de>"]
|
authors = ["Daniel Long Sockwell <daniel@codesections.com", "Julian Laubstein <contact@julianlaubstein.de>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -10,7 +10,6 @@ log = "0.4.6"
|
|||||||
futures = "0.1.26"
|
futures = "0.1.26"
|
||||||
tokio = "0.1.19"
|
tokio = "0.1.19"
|
||||||
warp = "0.1.15"
|
warp = "0.1.15"
|
||||||
regex = "1.1.5"
|
|
||||||
serde_json = "1.0.39"
|
serde_json = "1.0.39"
|
||||||
serde_derive = "1.0.90"
|
serde_derive = "1.0.90"
|
||||||
serde = "1.0.90"
|
serde = "1.0.90"
|
||||||
@ -18,7 +17,6 @@ pretty_env_logger = "0.3.0"
|
|||||||
postgres = { git = "https://github.com/sfackler/rust-postgres.git" }
|
postgres = { git = "https://github.com/sfackler/rust-postgres.git" }
|
||||||
uuid = { version = "0.7", features = ["v4"] }
|
uuid = { version = "0.7", features = ["v4"] }
|
||||||
dotenv = "0.14.0"
|
dotenv = "0.14.0"
|
||||||
lazy_static = "1.3.0"
|
|
||||||
postgres-openssl = { git = "https://github.com/sfackler/rust-postgres.git"}
|
postgres-openssl = { git = "https://github.com/sfackler/rust-postgres.git"}
|
||||||
url = "2.1.0"
|
url = "2.1.0"
|
||||||
|
|
||||||
|
102
src/config/deployment_cfg.rs
Normal file
102
src/config/deployment_cfg.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use crate::{err, maybe_update};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt,
|
||||||
|
net::{IpAddr, Ipv4Addr},
|
||||||
|
os::unix::net::UnixListener,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DeploymentConfig<'a> {
|
||||||
|
pub env: String,
|
||||||
|
pub log_level: String,
|
||||||
|
pub address: IpAddr,
|
||||||
|
pub port: u16,
|
||||||
|
pub unix_socket: Option<UnixListener>,
|
||||||
|
pub cors: Cors<'a>,
|
||||||
|
pub sse_interval: Duration,
|
||||||
|
pub ws_interval: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cors<'a> {
|
||||||
|
pub allowed_headers: Vec<&'a str>,
|
||||||
|
pub allowed_methods: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
impl fmt::Debug for Cors<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"allowed headers: {:?}\n allowed methods: {:?}",
|
||||||
|
self.allowed_headers, self.allowed_methods
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DeploymentConfig<'_> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
env: "development".to_string(),
|
||||||
|
log_level: "error".to_string(),
|
||||||
|
address: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
|
||||||
|
port: 4000,
|
||||||
|
unix_socket: None,
|
||||||
|
cors: Cors {
|
||||||
|
allowed_methods: vec!["GET", "OPTIONS"],
|
||||||
|
allowed_headers: vec!["Authorization", "Accept", "Cache-Control"],
|
||||||
|
},
|
||||||
|
sse_interval: Duration::from_millis(100),
|
||||||
|
ws_interval: Duration::from_millis(100),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DeploymentConfig<'_> {
|
||||||
|
pub fn from_env(env_vars: HashMap<String, String>) -> Self {
|
||||||
|
Self::default()
|
||||||
|
.maybe_update_env(env_vars.get("NODE_ENV").map(String::from))
|
||||||
|
.maybe_update_env(env_vars.get("RUST_ENV").map(String::from))
|
||||||
|
.maybe_update_address(
|
||||||
|
env_vars
|
||||||
|
.get("BIND")
|
||||||
|
.map(|a| err::unwrap_or_die(a.parse().ok(), "BIND must be a valid address")),
|
||||||
|
)
|
||||||
|
.maybe_update_port(
|
||||||
|
env_vars
|
||||||
|
.get("PORT")
|
||||||
|
.map(|port| err::unwrap_or_die(port.parse().ok(), "PORT must be a number")),
|
||||||
|
)
|
||||||
|
.maybe_update_unix_socket(
|
||||||
|
env_vars
|
||||||
|
.get("SOCKET")
|
||||||
|
.map(|s| UnixListener::bind(s).unwrap()),
|
||||||
|
)
|
||||||
|
.maybe_update_log_level(env_vars.get("RUST_LOG").map(|level| match level.as_ref() {
|
||||||
|
l @ "trace" | l @ "debug" | l @ "info" | l @ "warn" | l @ "error" => l.to_string(),
|
||||||
|
_ => err::die_with_msg("Invalid log level specified"),
|
||||||
|
}))
|
||||||
|
.maybe_update_sse_interval(
|
||||||
|
env_vars
|
||||||
|
.get("SSE_UPDATE_INTERVAL")
|
||||||
|
.map(|str| Duration::from_millis(str.parse().unwrap())),
|
||||||
|
)
|
||||||
|
.maybe_update_ws_interval(
|
||||||
|
env_vars
|
||||||
|
.get("WS_UPDATE_INTERVAL")
|
||||||
|
.map(|str| Duration::from_millis(str.parse().unwrap())),
|
||||||
|
)
|
||||||
|
.log()
|
||||||
|
}
|
||||||
|
|
||||||
|
maybe_update!(maybe_update_env; env: String);
|
||||||
|
maybe_update!(maybe_update_port; port: u16);
|
||||||
|
maybe_update!(maybe_update_address; address: IpAddr);
|
||||||
|
maybe_update!(maybe_update_unix_socket; Some(unix_socket: UnixListener));
|
||||||
|
maybe_update!(maybe_update_log_level; log_level: String);
|
||||||
|
maybe_update!(maybe_update_sse_interval; sse_interval: Duration);
|
||||||
|
maybe_update!(maybe_update_ws_interval; ws_interval: Duration);
|
||||||
|
|
||||||
|
fn log(self) -> Self {
|
||||||
|
log::warn!("Using deployment configuration:\n {:#?}", &self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
@ -1,126 +1,29 @@
|
|||||||
//! Configuration defaults. All settings with the prefix of `DEFAULT_` can be overridden
|
//! Configuration defaults. All settings with the prefix of `DEFAULT_` can be overridden
|
||||||
//! by an environmental variable of the same name without that prefix (either by setting
|
//! by an environmental variable of the same name without that prefix (either by setting
|
||||||
//! the variable at runtime or in the `.env` file)
|
//! the variable at runtime or in the `.env` file)
|
||||||
|
mod deployment_cfg;
|
||||||
mod postgres_cfg;
|
mod postgres_cfg;
|
||||||
mod redis_cfg;
|
mod redis_cfg;
|
||||||
pub use self::{postgres_cfg::PostgresConfig, redis_cfg::RedisConfig};
|
pub use self::{
|
||||||
use dotenv::dotenv;
|
deployment_cfg::DeploymentConfig, postgres_cfg::PostgresConfig, redis_cfg::RedisConfig,
|
||||||
use lazy_static::lazy_static;
|
};
|
||||||
use log::warn;
|
|
||||||
use std::{env, net};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
const CORS_ALLOWED_METHODS: [&str; 2] = ["GET", "OPTIONS"];
|
// **NOTE**: Polling Redis is much more time consuming than polling the `Receiver`
|
||||||
const CORS_ALLOWED_HEADERS: [&str; 3] = ["Authorization", "Accept", "Cache-Control"];
|
// (on the order of 10ms rather than 50μs). Thus, changing this setting
|
||||||
// Postgres
|
// would be a good place to start for performance improvements at the cost
|
||||||
// Deployment
|
// of delaying all updates.
|
||||||
const DEFAULT_SERVER_ADDR: &str = "127.0.0.1:4000";
|
|
||||||
|
|
||||||
const DEFAULT_SSE_UPDATE_INTERVAL: u64 = 100;
|
|
||||||
const DEFAULT_WS_UPDATE_INTERVAL: u64 = 100;
|
|
||||||
/// **NOTE**: Polling Redis is much more time consuming than polling the `Receiver`
|
|
||||||
/// (on the order of 10ms rather than 50μs). Thus, changing this setting
|
|
||||||
/// would be a good place to start for performance improvements at the cost
|
|
||||||
/// of delaying all updates.
|
|
||||||
const DEFAULT_REDIS_POLL_INTERVAL: u64 = 100;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref SERVER_ADDR: net::SocketAddr = env::var("SERVER_ADDR")
|
|
||||||
.unwrap_or_else(|_| DEFAULT_SERVER_ADDR.to_owned())
|
|
||||||
.parse()
|
|
||||||
.expect("static string");
|
|
||||||
|
|
||||||
/// Interval, in ms, at which `ClientAgent` polls `Receiver` for updates to send via SSE.
|
|
||||||
pub static ref SSE_UPDATE_INTERVAL: u64 = env::var("SSE_UPDATE_INTERVAL")
|
|
||||||
.map(|s| s.parse().expect("Valid config"))
|
|
||||||
.unwrap_or(DEFAULT_SSE_UPDATE_INTERVAL);
|
|
||||||
/// Interval, in ms, at which `ClientAgent` polls `Receiver` for updates to send via WS.
|
|
||||||
pub static ref WS_UPDATE_INTERVAL: u64 = env::var("WS_UPDATE_INTERVAL")
|
|
||||||
.map(|s| s.parse().expect("Valid config"))
|
|
||||||
.unwrap_or(DEFAULT_WS_UPDATE_INTERVAL);
|
|
||||||
/// Interval, in ms, at which the `Receiver` polls Redis.
|
|
||||||
pub static ref REDIS_POLL_INTERVAL: u64 = env::var("REDIS_POLL_INTERVAL")
|
|
||||||
.map(|s| s.parse().expect("Valid config"))
|
|
||||||
.unwrap_or(DEFAULT_REDIS_POLL_INTERVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure CORS for the API server
|
|
||||||
pub fn cross_origin_resource_sharing() -> warp::filters::cors::Cors {
|
|
||||||
warp::cors()
|
|
||||||
.allow_any_origin()
|
|
||||||
.allow_methods(CORS_ALLOWED_METHODS.to_vec())
|
|
||||||
.allow_headers(CORS_ALLOWED_HEADERS.to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize logging and read values from `src/.env`
|
|
||||||
pub fn logging_and_env() {
|
|
||||||
dotenv().ok();
|
|
||||||
pretty_env_logger::init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure Postgres and return a connection
|
|
||||||
pub fn postgres() -> PostgresConfig {
|
|
||||||
// use openssl::ssl::{SslConnector, SslMethod};
|
|
||||||
// use postgres_openssl::MakeTlsConnector;
|
|
||||||
// let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
|
||||||
// builder.set_ca_file("/etc/ssl/cert.pem").unwrap();
|
|
||||||
// let connector = MakeTlsConnector::new(builder.build());
|
|
||||||
// TODO: add TLS support, remove `NoTls`
|
|
||||||
let pg_cfg = match &env::var("DATABASE_URL").ok() {
|
|
||||||
Some(url) => {
|
|
||||||
warn!("DATABASE_URL env variable set. Connecting to Postgres with that URL and ignoring any values set in DB_HOST, DB_USER, DB_NAME, DB_PASS, or DB_PORT.");
|
|
||||||
PostgresConfig::from_url(Url::parse(url).unwrap())
|
|
||||||
}
|
|
||||||
None => PostgresConfig::default()
|
|
||||||
.maybe_update_user(env::var("USER").ok())
|
|
||||||
.maybe_update_user(env::var("DB_USER").ok())
|
|
||||||
.maybe_update_host(env::var("DB_HOST").ok())
|
|
||||||
.maybe_update_password(env::var("DB_PASS").ok())
|
|
||||||
.maybe_update_db(env::var("DB_NAME").ok())
|
|
||||||
.maybe_update_sslmode(env::var("DB_SSLMODE").ok()),
|
|
||||||
};
|
|
||||||
log::warn!(
|
|
||||||
"Connecting to Postgres with the following configuration:\n{:#?}",
|
|
||||||
&pg_cfg
|
|
||||||
);
|
|
||||||
pg_cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure Redis and return a pair of connections
|
|
||||||
pub fn redis() -> RedisConfig {
|
|
||||||
let redis_cfg = match &env::var("REDIS_URL") {
|
|
||||||
Ok(url) => {
|
|
||||||
warn!("REDIS_URL env variable set. Connecting to Redis with that URL and ignoring any values set in REDIS_HOST or DB_PORT.");
|
|
||||||
RedisConfig::from_url(Url::parse(url).unwrap())
|
|
||||||
}
|
|
||||||
Err(_) => RedisConfig::default()
|
|
||||||
.maybe_update_host(env::var("REDIS_HOST").ok())
|
|
||||||
.maybe_update_port(env::var("REDIS_PORT").ok()),
|
|
||||||
}.maybe_update_namespace(env::var("REDIS_NAMESPACE").ok());
|
|
||||||
if let Some(user) = &redis_cfg.user {
|
|
||||||
log::error!(
|
|
||||||
"Username {} provided, but Redis does not need a username. Ignoring it",
|
|
||||||
user
|
|
||||||
);
|
|
||||||
};
|
|
||||||
log::warn!(
|
|
||||||
"Connecting to Redis with the following configuration:\n{:#?}",
|
|
||||||
&redis_cfg
|
|
||||||
);
|
|
||||||
redis_cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! maybe_update {
|
macro_rules! maybe_update {
|
||||||
($name:ident; $item: tt) => (
|
($name:ident; $item: tt:$type:ty) => (
|
||||||
pub fn $name(self, item: Option<String>) -> Self{
|
pub fn $name(self, item: Option<$type>) -> Self {
|
||||||
match item {
|
match item {
|
||||||
Some($item) => Self{ $item, ..self },
|
Some($item) => Self{ $item, ..self },
|
||||||
None => Self { ..self }
|
None => Self { ..self }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
($name:ident; Some($item: tt)) => (
|
($name:ident; Some($item: tt: $type:ty)) => (
|
||||||
pub fn $name(self, item: Option<String>) -> Self{
|
fn $name(self, item: Option<$type>) -> Self{
|
||||||
match item {
|
match item {
|
||||||
Some($item) => Self{ $item: Some($item), ..self },
|
Some($item) => Self{ $item: Some($item), ..self },
|
||||||
None => Self { ..self }
|
None => Self { ..self }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{err, maybe_update};
|
use crate::{err, maybe_update};
|
||||||
|
use std::collections::HashMap;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -7,7 +8,7 @@ pub struct PostgresConfig {
|
|||||||
pub host: String,
|
pub host: String,
|
||||||
pub password: Option<String>,
|
pub password: Option<String>,
|
||||||
pub database: String,
|
pub database: String,
|
||||||
pub port: String,
|
pub port: u16,
|
||||||
pub ssl_mode: String,
|
pub ssl_mode: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ impl Default for PostgresConfig {
|
|||||||
host: "localhost".to_string(),
|
host: "localhost".to_string(),
|
||||||
password: None,
|
password: None,
|
||||||
database: "mastodon_development".to_string(),
|
database: "mastodon_development".to_string(),
|
||||||
port: "5432".to_string(),
|
port: 5432,
|
||||||
ssl_mode: "prefer".to_string(),
|
ssl_mode: "prefer".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,14 +29,36 @@ fn none_if_empty(item: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PostgresConfig {
|
impl PostgresConfig {
|
||||||
maybe_update!(maybe_update_user; user);
|
/// Configure Postgres and return a connection
|
||||||
maybe_update!(maybe_update_host; host);
|
pub fn from_env(env_vars: HashMap<String, String>) -> Self {
|
||||||
maybe_update!(maybe_update_db; database);
|
// use openssl::ssl::{SslConnector, SslMethod};
|
||||||
maybe_update!(maybe_update_port; port);
|
// use postgres_openssl::MakeTlsConnector;
|
||||||
maybe_update!(maybe_update_sslmode; ssl_mode);
|
// let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
maybe_update!(maybe_update_password; Some(password));
|
// builder.set_ca_file("/etc/ssl/cert.pem").unwrap();
|
||||||
|
// let connector = MakeTlsConnector::new(builder.build());
|
||||||
|
// TODO: add TLS support, remove `NoTls`
|
||||||
|
match env_vars.get("DATABASE_URL") {
|
||||||
|
Some(url) => {
|
||||||
|
log::warn!("DATABASE_URL env variable set. Connecting to Postgres with that URL and ignoring any values set in DB_HOST, DB_USER, DB_NAME, DB_PASS, or DB_PORT.");
|
||||||
|
PostgresConfig::from_url(Url::parse(url).unwrap())
|
||||||
|
}
|
||||||
|
None => Self::default()
|
||||||
|
.maybe_update_user(env_vars.get("USER").map(String::from))
|
||||||
|
.maybe_update_user(env_vars.get("DB_USER").map(String::from))
|
||||||
|
.maybe_update_host(env_vars.get("DB_HOST").map(String::from))
|
||||||
|
.maybe_update_password(env_vars.get("DB_PASS").map(String::from))
|
||||||
|
.maybe_update_db(env_vars.get("DB_NAME").map(String::from))
|
||||||
|
.maybe_update_sslmode(env_vars.get("DB_SSLMODE").map(String::from))}
|
||||||
|
.log()
|
||||||
|
}
|
||||||
|
maybe_update!(maybe_update_user; user: String);
|
||||||
|
maybe_update!(maybe_update_host; host: String);
|
||||||
|
maybe_update!(maybe_update_db; database: String);
|
||||||
|
maybe_update!(maybe_update_port; port: u16);
|
||||||
|
maybe_update!(maybe_update_sslmode; ssl_mode: String);
|
||||||
|
maybe_update!(maybe_update_password; Some(password: String));
|
||||||
|
|
||||||
pub fn from_url(url: Url) -> Self {
|
fn from_url(url: Url) -> Self {
|
||||||
let (mut user, mut host, mut sslmode, mut password) = (None, None, None, None);
|
let (mut user, mut host, mut sslmode, mut password) = (None, None, None, None);
|
||||||
for (k, v) in url.query_pairs() {
|
for (k, v) in url.query_pairs() {
|
||||||
match k.to_string().as_str() {
|
match k.to_string().as_str() {
|
||||||
@ -57,7 +80,11 @@ impl PostgresConfig {
|
|||||||
.maybe_update_user(none_if_empty(url.username()))
|
.maybe_update_user(none_if_empty(url.username()))
|
||||||
.maybe_update_host(url.host_str().filter(|h| !h.is_empty()).map(String::from))
|
.maybe_update_host(url.host_str().filter(|h| !h.is_empty()).map(String::from))
|
||||||
.maybe_update_password(url.password().map(String::from))
|
.maybe_update_password(url.password().map(String::from))
|
||||||
.maybe_update_port(url.port().map(|port_num| port_num.to_string()))
|
.maybe_update_port(url.port())
|
||||||
.maybe_update_db(none_if_empty(&url.path()[1..]))
|
.maybe_update_db(none_if_empty(&url.path()[1..]))
|
||||||
}
|
}
|
||||||
|
fn log(self) -> Self {
|
||||||
|
log::warn!("Postgres configuration:\n{:#?}", &self);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
use crate::{err, maybe_update};
|
use crate::{err, maybe_update};
|
||||||
|
use std::{collections::HashMap, time::Duration};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
fn none_if_empty(item: &str) -> Option<String> {
|
fn none_if_empty(item: &str) -> Option<String> {
|
||||||
if item.is_empty() {
|
Some(item).filter(|i| !i.is_empty()).map(String::from)
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(item.to_string())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RedisConfig {
|
pub struct RedisConfig {
|
||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub password: Option<String>,
|
pub password: Option<String>,
|
||||||
pub port: String,
|
pub port: u16,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub db: Option<String>,
|
pub db: Option<String>,
|
||||||
pub namespace: Option<String>,
|
pub namespace: Option<String>,
|
||||||
|
pub polling_interval: Duration,
|
||||||
}
|
}
|
||||||
impl Default for RedisConfig {
|
impl Default for RedisConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -24,14 +22,31 @@ impl Default for RedisConfig {
|
|||||||
user: None,
|
user: None,
|
||||||
password: None,
|
password: None,
|
||||||
db: None,
|
db: None,
|
||||||
port: "6379".to_string(),
|
port: 6379,
|
||||||
host: "127.0.0.1".to_string(),
|
host: "127.0.0.1".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
|
polling_interval: Duration::from_millis(100),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl RedisConfig {
|
impl RedisConfig {
|
||||||
pub fn from_url(url: Url) -> Self {
|
pub fn from_env(env_vars: HashMap<String, String>) -> Self {
|
||||||
|
match env_vars.get("REDIS_URL") {
|
||||||
|
Some(url) => {
|
||||||
|
log::warn!("REDIS_URL env variable set. Connecting to Redis with that URL and ignoring any values set in REDIS_HOST or DB_PORT.");
|
||||||
|
Self::from_url(Url::parse(url).unwrap())
|
||||||
|
}
|
||||||
|
None => RedisConfig::default()
|
||||||
|
.maybe_update_host(env_vars.get("REDIS_HOST").map(String::from))
|
||||||
|
.maybe_update_port(env_vars.get("REDIS_PORT").map(|p| err::unwrap_or_die(
|
||||||
|
p.parse().ok(),"REDIS_PORT must be a number."))),
|
||||||
|
}
|
||||||
|
.maybe_update_namespace(env_vars.get("REDIS_NAMESPACE").map(String::from))
|
||||||
|
.maybe_update_polling_interval(env_vars.get("REDIS_POLL_INTERVAL")
|
||||||
|
.map(|str| Duration::from_millis(str.parse().unwrap()))).log()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_url(url: Url) -> Self {
|
||||||
let mut password = url.password().as_ref().map(|str| str.to_string());
|
let mut password = url.password().as_ref().map(|str| str.to_string());
|
||||||
let mut db = none_if_empty(&url.path()[1..]);
|
let mut db = none_if_empty(&url.path()[1..]);
|
||||||
for (k, v) in url.query_pairs() {
|
for (k, v) in url.query_pairs() {
|
||||||
@ -41,16 +56,32 @@ impl RedisConfig {
|
|||||||
_ => { err::die_with_msg(format!("Unsupported parameter {} in REDIS_URL.\n Flodgatt supports only `password` and `db` parameters.", k))}
|
_ => { err::die_with_msg(format!("Unsupported parameter {} in REDIS_URL.\n Flodgatt supports only `password` and `db` parameters.", k))}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let user = none_if_empty(url.username());
|
||||||
|
if let Some(user) = &user {
|
||||||
|
log::error!(
|
||||||
|
"Username {} provided, but Redis does not need a username. Ignoring it",
|
||||||
|
user
|
||||||
|
);
|
||||||
|
}
|
||||||
RedisConfig {
|
RedisConfig {
|
||||||
user: none_if_empty(url.username()),
|
user,
|
||||||
host: err::unwrap_or_die(url.host_str(), "Missing or invalid host in REDIS_URL"),
|
host: err::unwrap_or_die(url.host_str(), "Missing or invalid host in REDIS_URL")
|
||||||
|
.to_string(),
|
||||||
port: err::unwrap_or_die(url.port(), "Missing or invalid port in REDIS_URL"),
|
port: err::unwrap_or_die(url.port(), "Missing or invalid port in REDIS_URL"),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
password,
|
password,
|
||||||
db,
|
db,
|
||||||
|
polling_interval: Duration::from_millis(100),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maybe_update!(maybe_update_host; host);
|
|
||||||
maybe_update!(maybe_update_port; port);
|
maybe_update!(maybe_update_host; host: String);
|
||||||
maybe_update!(maybe_update_namespace; Some(namespace));
|
maybe_update!(maybe_update_port; port: u16);
|
||||||
|
maybe_update!(maybe_update_namespace; Some(namespace: String));
|
||||||
|
maybe_update!(maybe_update_polling_interval; polling_interval: Duration);
|
||||||
|
|
||||||
|
fn log(self) -> Self {
|
||||||
|
log::warn!("Redis configuration:\n{:#?},", &self);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
12
src/err.rs
12
src/err.rs
@ -5,12 +5,20 @@ pub fn die_with_msg(msg: impl Display) -> ! {
|
|||||||
eprintln!("FATAL ERROR: {}", msg);
|
eprintln!("FATAL ERROR: {}", msg);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
pub fn unwrap_or_die(s: Option<impl Display>, msg: &str) -> String {
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! dbg_and_die {
|
||||||
|
($msg:expr) => {
|
||||||
|
let message = format!("FATAL ERROR: {}", $msg);
|
||||||
|
dbg!(message);
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn unwrap_or_die<T: Display>(s: Option<T>, msg: &str) -> T {
|
||||||
s.unwrap_or_else(|| {
|
s.unwrap_or_else(|| {
|
||||||
eprintln!("FATAL ERROR: {}", msg);
|
eprintln!("FATAL ERROR: {}", msg);
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
})
|
})
|
||||||
.to_string()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
48
src/main.rs
48
src/main.rs
@ -1,20 +1,34 @@
|
|||||||
use flodgatt::{
|
use flodgatt::{
|
||||||
config, err,
|
config, dbg_and_die, err,
|
||||||
parse_client_request::{sse, user, ws},
|
parse_client_request::{sse, user, ws},
|
||||||
redis_to_client_stream::{self, ClientAgent},
|
redis_to_client_stream::{self, ClientAgent},
|
||||||
};
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
use std::{collections::HashMap, env, net};
|
||||||
use warp::{ws::Ws2, Filter as WarpFilter};
|
use warp::{ws::Ws2, Filter as WarpFilter};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
config::logging_and_env();
|
dotenv::from_filename(
|
||||||
let client_agent_sse = ClientAgent::blank();
|
match env::var("ENV").ok().as_ref().map(String::as_str) {
|
||||||
|
Some("production") => ".env.production",
|
||||||
|
Some("development") | None => ".env",
|
||||||
|
Some(_) => err::die_with_msg("Unknown ENV variable specified.\n Valid options are: `production` or `development`."),
|
||||||
|
}).ok();
|
||||||
|
let env_vars: HashMap<_, _> = dotenv::vars().collect();
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
|
let cfg = config::DeploymentConfig::from_env(env_vars.clone());
|
||||||
|
let redis_cfg = config::RedisConfig::from_env(env_vars.clone());
|
||||||
|
let postgres_cfg = config::PostgresConfig::from_env(env_vars.clone());
|
||||||
|
|
||||||
|
let client_agent_sse = ClientAgent::blank(redis_cfg);
|
||||||
let client_agent_ws = client_agent_sse.clone_with_shared_receiver();
|
let client_agent_ws = client_agent_sse.clone_with_shared_receiver();
|
||||||
let pg_conn = user::PostgresConn::new();
|
let pg_conn = user::PostgresConn::new(postgres_cfg);
|
||||||
|
|
||||||
warn!("Streaming server initialized and ready to accept connections");
|
warn!("Streaming server initialized and ready to accept connections");
|
||||||
|
|
||||||
// Server Sent Events
|
// Server Sent Events
|
||||||
|
let sse_update_interval = cfg.ws_interval;
|
||||||
let sse_routes = sse::extract_user_or_reject(pg_conn.clone())
|
let sse_routes = sse::extract_user_or_reject(pg_conn.clone())
|
||||||
.and(warp::sse())
|
.and(warp::sse())
|
||||||
.map(
|
.map(
|
||||||
@ -24,13 +38,18 @@ fn main() {
|
|||||||
// Assign ClientAgent to generate stream of updates for the user/timeline pair
|
// Assign ClientAgent to generate stream of updates for the user/timeline pair
|
||||||
client_agent.init_for_user(user);
|
client_agent.init_for_user(user);
|
||||||
// send the updates through the SSE connection
|
// send the updates through the SSE connection
|
||||||
redis_to_client_stream::send_updates_to_sse(client_agent, sse_connection_to_client)
|
redis_to_client_stream::send_updates_to_sse(
|
||||||
|
client_agent,
|
||||||
|
sse_connection_to_client,
|
||||||
|
sse_update_interval,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with(warp::reply::with::header("Connection", "keep-alive"))
|
.with(warp::reply::with::header("Connection", "keep-alive"))
|
||||||
.recover(err::handle_errors);
|
.recover(err::handle_errors);
|
||||||
|
|
||||||
// WebSocket
|
// WebSocket
|
||||||
|
let ws_update_interval = cfg.ws_interval;
|
||||||
let websocket_routes = ws::extract_user_or_reject(pg_conn.clone())
|
let websocket_routes = ws::extract_user_or_reject(pg_conn.clone())
|
||||||
.and(warp::ws::ws2())
|
.and(warp::ws::ws2())
|
||||||
.map(move |user: user::User, ws: Ws2| {
|
.map(move |user: user::User, ws: Ws2| {
|
||||||
@ -44,14 +63,27 @@ fn main() {
|
|||||||
|
|
||||||
(
|
(
|
||||||
ws.on_upgrade(move |socket| {
|
ws.on_upgrade(move |socket| {
|
||||||
redis_to_client_stream::send_updates_to_ws(socket, client_agent)
|
redis_to_client_stream::send_updates_to_ws(
|
||||||
|
socket,
|
||||||
|
client_agent,
|
||||||
|
ws_update_interval,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(reply, token)| warp::reply::with_header(reply, "sec-websocket-protocol", token));
|
.map(|(reply, token)| warp::reply::with_header(reply, "sec-websocket-protocol", token));
|
||||||
|
|
||||||
let cors = config::cross_origin_resource_sharing();
|
let cors = warp::cors()
|
||||||
|
.allow_any_origin()
|
||||||
|
.allow_methods(cfg.cors.allowed_methods)
|
||||||
|
.allow_headers(cfg.cors.allowed_headers);
|
||||||
|
|
||||||
warp::serve(websocket_routes.or(sse_routes).with(cors)).run(*config::SERVER_ADDR);
|
let server_addr = net::SocketAddr::new(cfg.address, cfg.port);
|
||||||
|
|
||||||
|
if let Some(_socket) = cfg.unix_socket {
|
||||||
|
dbg_and_die!("Unix socket support not yet implemented");
|
||||||
|
} else {
|
||||||
|
warp::serve(websocket_routes.or(sse_routes).with(cors)).run(server_addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,11 @@ use std::sync::{Arc, Mutex};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PostgresConn(pub Arc<Mutex<postgres::Client>>);
|
pub struct PostgresConn(pub Arc<Mutex<postgres::Client>>);
|
||||||
impl PostgresConn {
|
impl PostgresConn {
|
||||||
pub fn new() -> Self {
|
pub fn new(pg_cfg: config::PostgresConfig) -> Self {
|
||||||
let pg_cfg = config::postgres();
|
|
||||||
let mut con = postgres::Client::configure();
|
let mut con = postgres::Client::configure();
|
||||||
con.user(&pg_cfg.user)
|
con.user(&pg_cfg.user)
|
||||||
.host(&pg_cfg.host)
|
.host(&pg_cfg.host)
|
||||||
.port(pg_cfg.port.parse::<u16>().unwrap())
|
.port(pg_cfg.port)
|
||||||
.dbname(&pg_cfg.database);
|
.dbname(&pg_cfg.database);
|
||||||
if let Some(password) = &pg_cfg.password {
|
if let Some(password) = &pg_cfg.password {
|
||||||
con.password(password);
|
con.password(password);
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
//! communicate with Redis, it we create a new `ClientAgent` for
|
//! communicate with Redis, it we create a new `ClientAgent` for
|
||||||
//! each new client connection (each in its own thread).
|
//! each new client connection (each in its own thread).
|
||||||
use super::receiver::Receiver;
|
use super::receiver::Receiver;
|
||||||
use crate::parse_client_request::user::User;
|
use crate::{config, parse_client_request::user::User};
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::sync;
|
use std::sync;
|
||||||
@ -24,7 +24,7 @@ use tokio::io::Error;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// Struct for managing all Redis streams.
|
/// Struct for managing all Redis streams.
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ClientAgent {
|
pub struct ClientAgent {
|
||||||
receiver: sync::Arc<sync::Mutex<Receiver>>,
|
receiver: sync::Arc<sync::Mutex<Receiver>>,
|
||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
@ -34,9 +34,9 @@ pub struct ClientAgent {
|
|||||||
|
|
||||||
impl ClientAgent {
|
impl ClientAgent {
|
||||||
/// Create a new `ClientAgent` with no shared data.
|
/// Create a new `ClientAgent` with no shared data.
|
||||||
pub fn blank() -> Self {
|
pub fn blank(redis_cfg: config::RedisConfig) -> Self {
|
||||||
ClientAgent {
|
ClientAgent {
|
||||||
receiver: sync::Arc::new(sync::Mutex::new(Receiver::new())),
|
receiver: sync::Arc::new(sync::Mutex::new(Receiver::new(redis_cfg))),
|
||||||
id: Uuid::default(),
|
id: Uuid::default(),
|
||||||
target_timeline: String::new(),
|
target_timeline: String::new(),
|
||||||
current_user: User::default(),
|
current_user: User::default(),
|
||||||
|
@ -14,18 +14,16 @@ use std::time;
|
|||||||
pub fn send_updates_to_sse(
|
pub fn send_updates_to_sse(
|
||||||
mut client_agent: ClientAgent,
|
mut client_agent: ClientAgent,
|
||||||
connection: warp::sse::Sse,
|
connection: warp::sse::Sse,
|
||||||
|
update_interval: time::Duration,
|
||||||
) -> impl warp::reply::Reply {
|
) -> impl warp::reply::Reply {
|
||||||
let event_stream = tokio::timer::Interval::new(
|
let event_stream = tokio::timer::Interval::new(time::Instant::now(), update_interval)
|
||||||
time::Instant::now(),
|
.filter_map(move |_| match client_agent.poll() {
|
||||||
time::Duration::from_millis(*config::SSE_UPDATE_INTERVAL),
|
Ok(Async::Ready(Some(json_value))) => Some((
|
||||||
)
|
warp::sse::event(json_value["event"].clone().to_string()),
|
||||||
.filter_map(move |_| match client_agent.poll() {
|
warp::sse::data(json_value["payload"].clone()),
|
||||||
Ok(Async::Ready(Some(json_value))) => Some((
|
)),
|
||||||
warp::sse::event(json_value["event"].clone().to_string()),
|
_ => None,
|
||||||
warp::sse::data(json_value["payload"].clone()),
|
});
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.reply(warp::sse::keep(event_stream, None))
|
connection.reply(warp::sse::keep(event_stream, None))
|
||||||
}
|
}
|
||||||
@ -34,6 +32,7 @@ pub fn send_updates_to_sse(
|
|||||||
pub fn send_updates_to_ws(
|
pub fn send_updates_to_ws(
|
||||||
socket: warp::ws::WebSocket,
|
socket: warp::ws::WebSocket,
|
||||||
mut stream: ClientAgent,
|
mut stream: ClientAgent,
|
||||||
|
update_interval: time::Duration,
|
||||||
) -> impl futures::future::Future<Item = (), Error = ()> {
|
) -> impl futures::future::Future<Item = (), Error = ()> {
|
||||||
let (ws_tx, mut ws_rx) = socket.split();
|
let (ws_tx, mut ws_rx) = socket.split();
|
||||||
|
|
||||||
@ -50,22 +49,19 @@ pub fn send_updates_to_ws(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Yield new events for as long as the client is still connected
|
// Yield new events for as long as the client is still connected
|
||||||
let event_stream = tokio::timer::Interval::new(
|
let event_stream = tokio::timer::Interval::new(time::Instant::now(), update_interval)
|
||||||
time::Instant::now(),
|
.take_while(move |_| match ws_rx.poll() {
|
||||||
time::Duration::from_millis(*config::WS_UPDATE_INTERVAL),
|
Ok(Async::NotReady) | Ok(Async::Ready(Some(_))) => futures::future::ok(true),
|
||||||
)
|
Ok(Async::Ready(None)) => {
|
||||||
.take_while(move |_| match ws_rx.poll() {
|
// TODO: consider whether we should manually drop closed connections here
|
||||||
Ok(Async::NotReady) | Ok(Async::Ready(Some(_))) => futures::future::ok(true),
|
log::info!("Client closed WebSocket connection");
|
||||||
Ok(Async::Ready(None)) => {
|
futures::future::ok(false)
|
||||||
// TODO: consider whether we should manually drop closed connections here
|
}
|
||||||
log::info!("Client closed WebSocket connection");
|
Err(e) => {
|
||||||
futures::future::ok(false)
|
log::warn!("{}", e);
|
||||||
}
|
futures::future::ok(false)
|
||||||
Err(e) => {
|
}
|
||||||
log::warn!("{}", e);
|
});
|
||||||
futures::future::ok(false)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Every time you get an event from that stream, send it through the pipe
|
// Every time you get an event from that stream, send it through the pipe
|
||||||
event_stream
|
event_stream
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! Receives data from Redis, sorts it by `ClientAgent`, and stores it until
|
//! Receives data from Redis, sorts it by `ClientAgent`, and stores it until
|
||||||
//! polled by the correct `ClientAgent`. Also manages sububscriptions and
|
//! polled by the correct `ClientAgent`. Also manages sububscriptions and
|
||||||
//! unsubscriptions to/from Redis.
|
//! unsubscriptions to/from Redis.
|
||||||
use super::{redis_cmd, redis_stream, redis_stream::RedisConn};
|
use super::{config, redis_cmd, redis_stream, redis_stream::RedisConn};
|
||||||
use crate::{config, pubsub_cmd};
|
use crate::pubsub_cmd;
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{collections, net, time};
|
use std::{collections, net, time};
|
||||||
@ -15,6 +15,7 @@ pub struct Receiver {
|
|||||||
pub pubsub_connection: net::TcpStream,
|
pub pubsub_connection: net::TcpStream,
|
||||||
secondary_redis_connection: net::TcpStream,
|
secondary_redis_connection: net::TcpStream,
|
||||||
pub redis_namespace: Option<String>,
|
pub redis_namespace: Option<String>,
|
||||||
|
redis_poll_interval: time::Duration,
|
||||||
redis_polled_at: time::Instant,
|
redis_polled_at: time::Instant,
|
||||||
timeline: String,
|
timeline: String,
|
||||||
manager_id: Uuid,
|
manager_id: Uuid,
|
||||||
@ -26,17 +27,19 @@ pub struct Receiver {
|
|||||||
impl Receiver {
|
impl Receiver {
|
||||||
/// Create a new `Receiver`, with its own Redis connections (but, as yet, no
|
/// Create a new `Receiver`, with its own Redis connections (but, as yet, no
|
||||||
/// active subscriptions).
|
/// active subscriptions).
|
||||||
pub fn new() -> Self {
|
pub fn new(redis_cfg: config::RedisConfig) -> Self {
|
||||||
let RedisConn {
|
let RedisConn {
|
||||||
primary: pubsub_connection,
|
primary: pubsub_connection,
|
||||||
secondary: secondary_redis_connection,
|
secondary: secondary_redis_connection,
|
||||||
namespace: redis_namespace,
|
namespace: redis_namespace,
|
||||||
} = RedisConn::new();
|
polling_interval: redis_poll_interval,
|
||||||
|
} = RedisConn::new(redis_cfg);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pubsub_connection,
|
pubsub_connection,
|
||||||
secondary_redis_connection,
|
secondary_redis_connection,
|
||||||
redis_namespace,
|
redis_namespace,
|
||||||
|
redis_poll_interval,
|
||||||
redis_polled_at: time::Instant::now(),
|
redis_polled_at: time::Instant::now(),
|
||||||
timeline: String::new(),
|
timeline: String::new(),
|
||||||
manager_id: Uuid::default(),
|
manager_id: Uuid::default(),
|
||||||
@ -123,12 +126,6 @@ impl Receiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Receiver {
|
|
||||||
fn default() -> Self {
|
|
||||||
Receiver::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The stream that the ClientAgent polls to learn about new messages.
|
/// The stream that the ClientAgent polls to learn about new messages.
|
||||||
impl futures::stream::Stream for Receiver {
|
impl futures::stream::Stream for Receiver {
|
||||||
type Item = Value;
|
type Item = Value;
|
||||||
@ -142,9 +139,7 @@ impl futures::stream::Stream for Receiver {
|
|||||||
/// been polled lately.
|
/// been polled lately.
|
||||||
fn poll(&mut self) -> Poll<Option<Value>, Self::Error> {
|
fn poll(&mut self) -> Poll<Option<Value>, Self::Error> {
|
||||||
let timeline = self.timeline.clone();
|
let timeline = self.timeline.clone();
|
||||||
if self.redis_polled_at.elapsed()
|
if self.redis_polled_at.elapsed() > self.redis_poll_interval {
|
||||||
> time::Duration::from_millis(*config::REDIS_POLL_INTERVAL)
|
|
||||||
{
|
|
||||||
redis_stream::AsyncReadableStream::poll_redis(self);
|
redis_stream::AsyncReadableStream::poll_redis(self);
|
||||||
self.redis_polled_at = time::Instant::now();
|
self.redis_polled_at = time::Instant::now();
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,10 @@ pub struct RedisConn {
|
|||||||
pub primary: net::TcpStream,
|
pub primary: net::TcpStream,
|
||||||
pub secondary: net::TcpStream,
|
pub secondary: net::TcpStream,
|
||||||
pub namespace: Option<String>,
|
pub namespace: Option<String>,
|
||||||
|
pub polling_interval: time::Duration,
|
||||||
}
|
}
|
||||||
impl RedisConn {
|
impl RedisConn {
|
||||||
pub fn new() -> Self {
|
pub fn new(redis_cfg: config::RedisConfig) -> Self {
|
||||||
let redis_cfg = config::redis();
|
|
||||||
let addr = format!("{}:{}", redis_cfg.host, redis_cfg.port);
|
let addr = format!("{}:{}", redis_cfg.host, redis_cfg.port);
|
||||||
let mut pubsub_connection =
|
let mut pubsub_connection =
|
||||||
net::TcpStream::connect(addr.clone()).expect("Can connect to Redis");
|
net::TcpStream::connect(addr.clone()).expect("Can connect to Redis");
|
||||||
@ -49,6 +49,7 @@ impl RedisConn {
|
|||||||
primary: pubsub_connection,
|
primary: pubsub_connection,
|
||||||
secondary: secondary_redis_connection,
|
secondary: secondary_redis_connection,
|
||||||
namespace: redis_cfg.namespace,
|
namespace: redis_cfg.namespace,
|
||||||
|
polling_interval: redis_cfg.polling_interval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user