Skip to content

Commit

Permalink
count request types received in ConfigState
Browse files Browse the repository at this point in the history
documenting comments on ConfigState
  • Loading branch information
Keksoj committed Jun 1, 2023
1 parent 33abd28 commit b34b60f
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 14 deletions.
5 changes: 5 additions & 0 deletions bin/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ pub enum StateCmd {
#[clap(short = 'f', long = "file")]
file: String,
},
#[clap(
name = "stats",
about = "show the counts of requests that were received since startup"
)]
Stats,
}

#[derive(Subcommand, PartialEq, Eq, Clone, Debug)]
Expand Down
6 changes: 4 additions & 2 deletions bin/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ pub enum Success {
PropagatedWorkerEvent,
Query(ResponseContent),
ReloadConfiguration(usize, usize), // ok, errors
SaveState(usize, String), // amount of written commands, path of the saved state
Status(ResponseContent), // Vec<WorkerInfo>
RequestCounts(ResponseContent),
SaveState(usize, String), // amount of written commands, path of the saved state
Status(ResponseContent), // Vec<WorkerInfo>
SubscribeEvent(String),
UpgradeMain(i32), // pid of the new main process
UpgradeWorker(u32), // worker id
Expand Down Expand Up @@ -150,6 +151,7 @@ impl std::fmt::Display for Success {
f,
"Successfully reloaded configuration, ok: {ok}, errors: {error}"
),
Self::RequestCounts(_) => write!(f, "count requests"),
Self::SaveState(counter, path) => {
write!(f, "saved {counter} config messages to {path}")
}
Expand Down
9 changes: 9 additions & 0 deletions bin/src/command/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl CommandServer {
Some(RequestType::QueryCertificatesFromTheState(filters)) => {
self.query_certificates_from_the_state(filters)
}
Some(RequestType::CountRequests(_)) => self.query_request_count(),
Some(RequestType::QueryClusterById(_))
| Some(RequestType::QueryCertificatesFromWorkers(_))
| Some(RequestType::QueryClustersByDomain(_))
Expand Down Expand Up @@ -112,6 +113,13 @@ impl CommandServer {
Ok(Success::HandledClientRequest)
}

pub fn query_request_count(&mut self) -> anyhow::Result<Option<Success>> {
let request_counts = self.state.get_request_counts();
Ok(Some(Success::RequestCounts(
ContentType::RequestCounts(request_counts).into(),
)))
}

pub async fn save_state(&mut self, path: &str) -> anyhow::Result<Option<Success>> {
let mut file = File::create(path)
.with_context(|| format!("could not open file at path: {}", &path))?;
Expand Down Expand Up @@ -1354,6 +1362,7 @@ impl CommandServer {

let command_response_data = match success {
Success::ListFrontends(crd)
| Success::RequestCounts(crd)
| Success::ListWorkers(crd)
| Success::CertificatesFromTheState(crd)
| Success::Query(crd)
Expand Down
5 changes: 4 additions & 1 deletion bin/src/ctl/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::ctl::{
display::{
print_available_metrics, print_certificates_by_worker, print_certificates_with_validity,
print_cluster_responses, print_frontend_list, print_json_response, print_listeners,
print_metrics, print_status,
print_metrics, print_request_counts, print_status,
},
CommandManager,
};
Expand Down Expand Up @@ -68,6 +68,9 @@ impl CommandManager {
}
if let Some(response_content) = response.content {
match response_content.content_type {
Some(ContentType::RequestCounts(request_counts)) => {
print_request_counts(&request_counts)
}
Some(ContentType::FrontendList(frontends)) => {
print_frontend_list(frontends)
}
Expand Down
13 changes: 12 additions & 1 deletion bin/src/ctl/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use sozu_command_lib::proto::{
filtered_metrics, response_content::ContentType, AggregatedMetrics, AvailableMetrics,
CertificateAndKey, CertificatesWithFingerprints, ClusterMetrics, FilteredMetrics,
ListedFrontends, ListenersList, ResponseContent, WorkerInfos, WorkerMetrics,
WorkerResponses,
WorkerResponses, RequestCounts,
},
display::concatenate_vector,
};
Expand Down Expand Up @@ -745,6 +745,17 @@ pub fn print_certificates_with_validity(
Ok(())
}

pub fn print_request_counts(request_counts: &RequestCounts) {
let mut table = Table::new();
table.set_format(*prettytable::format::consts::FORMAT_BOX_CHARS);
table.add_row(row!["request type", "count"]);

for (request_type, count) in &request_counts.map {
table.add_row(row!(request_type, count));
}
table.printstd();
}

// ISO 8601
fn format_datetime(asn1_time: ASN1Time) -> anyhow::Result<String> {
let datetime = asn1_time.to_datetime();
Expand Down
1 change: 1 addition & 0 deletions bin/src/ctl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ impl CommandManager {
SubCmd::State { cmd } => match cmd {
StateCmd::Save { file } => self.save_state(file),
StateCmd::Load { file } => self.load_state(file),
StateCmd::Stats => self.count_requests(),
},
SubCmd::Reload { file, json } => self.reload_configuration(file, json),
SubCmd::Cluster { cmd, json } => self.cluster_command(cmd, json),
Expand Down
10 changes: 7 additions & 3 deletions bin/src/ctl/request_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use sozu_command_lib::{
config::{Config, ListenerBuilder},
proto::command::{
request::RequestType, ActivateListener, AddBackend, AddCertificate, CertificateAndKey,
Cluster, DeactivateListener, FrontendFilters, HardStop, ListListeners, ListenerType,
LoadBalancingParams, MetricsConfiguration, PathRule, ProxyProtocolConfig, RemoveBackend,
RemoveCertificate, RemoveListener, ReplaceCertificate, RequestHttpFrontend,
Cluster, CountRequests, DeactivateListener, FrontendFilters, HardStop, ListListeners,
ListenerType, LoadBalancingParams, MetricsConfiguration, PathRule, ProxyProtocolConfig,
RemoveBackend, RemoveCertificate, RemoveListener, ReplaceCertificate, RequestHttpFrontend,
RequestTcpFrontend, RulePosition, SoftStop, Status, SubscribeEvents, TlsVersion,
},
};
Expand All @@ -35,6 +35,10 @@ impl CommandManager {
self.send_request(RequestType::LoadState(path).into())
}

pub fn count_requests(&mut self) -> anyhow::Result<()> {
self.send_request(RequestType::CountRequests(CountRequests {}).into())
}

pub fn soft_stop(&mut self) -> anyhow::Result<()> {
println!("shutting down proxy softly");

Expand Down
10 changes: 10 additions & 0 deletions command/src/command.proto
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ message Request {
QueryCertificatesFilters query_certificates_from_the_state = 44;
// Get certificates from the workers (rather than from the state)
QueryCertificatesFilters query_certificates_from_workers = 45;
// query the state about how many requests of each type has been received
// since startup
CountRequests count_requests = 46;
}
}

Expand All @@ -105,6 +108,7 @@ message QueryClustersHashes {}
message SoftStop {}
message HardStop {}
message ReturnListenSockets {}
message CountRequests {}

// details of an HTTP listener
message HttpListenerConfig {
Expand Down Expand Up @@ -454,6 +458,8 @@ message ResponseContent {
ListOfCertificatesByAddress certificates_by_address = 11;
// a map of complete certificates using fingerprints as key
CertificatesWithFingerprints certificates_with_fingerprints = 12;
// a census of the types of requests received since startup,
RequestCounts request_counts = 13;
}
}

Expand Down Expand Up @@ -587,4 +593,8 @@ message Percentiles {
required uint64 p_99_99 = 6;
required uint64 p_99_999 = 7;
required uint64 p_100 = 8;
}

message RequestCounts {
map<string, int32> map = 1;
}
51 changes: 50 additions & 1 deletion command/src/proto/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::fmt::{Display, Formatter};

use crate::proto::command::TlsVersion;

use super::command::{CertificateAndKey, CertificateSummary, QueryCertificatesFilters};
use super::command::{
request::RequestType, CertificateAndKey, CertificateSummary, QueryCertificatesFilters,
};

impl Display for CertificateAndKey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Expand Down Expand Up @@ -52,3 +54,50 @@ pub fn concatenate_vector(vec: &Vec<String>) -> String {
}
concatenated
}

pub fn format_request_type(request_type: &RequestType) -> String {
match request_type {
RequestType::SaveState(_) => "SaveState".to_owned(),
RequestType::LoadState(_) => "LoadState".to_owned(),
RequestType::CountRequests(_) => "CountRequests".to_owned(),
RequestType::ListWorkers(_) => "ListWorkers".to_owned(),
RequestType::ListFrontends(_) => "ListFrontends".to_owned(),
RequestType::ListListeners(_) => "ListListeners".to_owned(),
RequestType::LaunchWorker(_) => "LaunchWorker".to_owned(),
RequestType::UpgradeMain(_) => "UpgradeMain".to_owned(),
RequestType::UpgradeWorker(_) => "UpgradeWorker".to_owned(),
RequestType::SubscribeEvents(_) => "SubscribeEvents".to_owned(),
RequestType::ReloadConfiguration(_) => "ReloadConfiguration".to_owned(),
RequestType::Status(_) => "Status".to_owned(),
RequestType::AddCluster(_) => "AddCluster".to_owned(),
RequestType::RemoveCluster(_) => "RemoveCluster".to_owned(),
RequestType::AddHttpFrontend(_) => "AddHttpFrontend".to_owned(),
RequestType::RemoveHttpFrontend(_) => "RemoveHttpFrontend".to_owned(),
RequestType::AddHttpsFrontend(_) => "AddHttpsFrontend".to_owned(),
RequestType::RemoveHttpsFrontend(_) => "RemoveHttpsFrontend".to_owned(),
RequestType::AddCertificate(_) => "AddCertificate".to_owned(),
RequestType::ReplaceCertificate(_) => "ReplaceCertificate".to_owned(),
RequestType::RemoveCertificate(_) => "RemoveCertificate".to_owned(),
RequestType::AddTcpFrontend(_) => "AddTcpFrontend".to_owned(),
RequestType::RemoveTcpFrontend(_) => "RemoveTcpFrontend".to_owned(),
RequestType::AddBackend(_) => "AddBackend".to_owned(),
RequestType::RemoveBackend(_) => "RemoveBackend".to_owned(),
RequestType::AddHttpListener(_) => "AddHttpListener".to_owned(),
RequestType::AddHttpsListener(_) => "AddHttpsListener".to_owned(),
RequestType::AddTcpListener(_) => "AddTcpListener".to_owned(),
RequestType::RemoveListener(_) => "RemoveListener".to_owned(),
RequestType::ActivateListener(_) => "ActivateListener".to_owned(),
RequestType::DeactivateListener(_) => "DeactivateListener".to_owned(),
RequestType::QueryClusterById(_) => "QueryClusterById".to_owned(),
RequestType::QueryClustersByDomain(_) => "QueryClustersByDomain".to_owned(),
RequestType::QueryClustersHashes(_) => "QueryClustersHashes".to_owned(),
RequestType::QueryMetrics(_) => "QueryMetrics".to_owned(),
RequestType::SoftStop(_) => "SoftStop".to_owned(),
RequestType::HardStop(_) => "HardStop".to_owned(),
RequestType::ConfigureMetrics(_) => "ConfigureMetrics".to_owned(),
RequestType::Logging(_) => "Logging".to_owned(),
RequestType::ReturnListenSockets(_) => "ReturnListenSockets".to_owned(),
RequestType::QueryCertificatesFromTheState(_) => "QueryCertificatesFromTheState".to_owned(),
RequestType::QueryCertificatesFromWorkers(_) => "QueryCertificatesFromWorkers".to_owned(),
}
}
1 change: 1 addition & 0 deletions command/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl Request {

// These won't ever reach a worker anyway
RequestType::SaveState(_)
| RequestType::CountRequests(_)
| RequestType::QueryCertificatesFromTheState(_)
| RequestType::LoadState(_)
| RequestType::ListWorkers(_)
Expand Down
50 changes: 44 additions & 6 deletions command/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,40 @@ use anyhow::{bail, Context};

use crate::{
certificate::{calculate_fingerprint, Fingerprint},
proto::command::{
request::RequestType, ActivateListener, AddBackend, AddCertificate, CertificateAndKey,
Cluster, ClusterInformation, DeactivateListener, FrontendFilters, HttpListenerConfig,
HttpsListenerConfig, ListedFrontends, ListenerType, ListenersList, PathRule,
QueryCertificatesFilters, RemoveBackend, RemoveCertificate, RemoveListener,
ReplaceCertificate, Request, RequestHttpFrontend, RequestTcpFrontend, TcpListenerConfig,
proto::{
command::{
request::RequestType, ActivateListener, AddBackend, AddCertificate, CertificateAndKey,
Cluster, ClusterInformation, DeactivateListener, FrontendFilters, HttpListenerConfig,
HttpsListenerConfig, ListedFrontends, ListenerType, ListenersList, PathRule,
QueryCertificatesFilters, RemoveBackend, RemoveCertificate, RemoveListener,
ReplaceCertificate, Request, RequestCounts, RequestHttpFrontend, RequestTcpFrontend,
TcpListenerConfig,
},
display::format_request_type,
},
response::{Backend, HttpFrontend, TcpFrontend},
};

/// To use throughout Sōzu
pub type ClusterId = String;

/// The `ConfigState` represents the state of Sōzu's business, which is to forward traffic
/// from frontends to backends. Hence, it contains all details about:
///
/// - listeners (socket addresses, for TCP and HTTP connections)
/// - frontends (bind to a listener)
/// - backends (to forward connections to)
/// - clusters (routing rules from frontends to backends)
/// - TLS certificates
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ConfigState {
pub clusters: BTreeMap<ClusterId, Cluster>,
pub backends: BTreeMap<ClusterId, Vec<Backend>>,
/// socket address -> HTTP listener
pub http_listeners: BTreeMap<String, HttpListenerConfig>,
/// socket address -> HTTPS listener
pub https_listeners: BTreeMap<String, HttpsListenerConfig>,
/// socket address -> TCP listener
pub tcp_listeners: BTreeMap<String, TcpListenerConfig>,
/// HTTP frontends, indexed by a summary of each front's address;hostname;path, for uniqueness.
/// For example: `"0.0.0.0:8080;lolcatho.st;P/api"`
Expand All @@ -39,6 +54,8 @@ pub struct ConfigState {
pub https_fronts: BTreeMap<String, HttpFrontend>,
pub tcp_fronts: HashMap<ClusterId, Vec<TcpFrontend>>,
pub certificates: HashMap<SocketAddr, HashMap<Fingerprint, CertificateAndKey>>,
/// A census of requests that were received. Name of the request -> number of occurences
pub request_counts: BTreeMap<String, i32>,
}

impl ConfigState {
Expand All @@ -51,6 +68,9 @@ impl ConfigState {
Some(t) => t,
None => bail!("Empty request!"),
};

self.increment_request_count(request);

match request_type {
RequestType::AddCluster(cluster) => self
.add_cluster(cluster)
Expand Down Expand Up @@ -111,6 +131,7 @@ impl ConfigState {
.with_context(|| "Could not remove backend"),
// This is to avoid the error message
&RequestType::Logging(_)
| &RequestType::CountRequests(_)
| &RequestType::Status(_)
| &RequestType::SoftStop(_)
| &RequestType::QueryCertificatesFromWorkers(_)
Expand All @@ -128,6 +149,23 @@ impl ConfigState {
}
}

/// Increments the count for this request type
fn increment_request_count(&mut self, request: &Request) {
if let Some(request_type) = &request.request_type {
let count = self
.request_counts
.entry(format_request_type(&request_type))
.or_insert(1);
*count += 1;
}
}

pub fn get_request_counts(&self) -> RequestCounts {
RequestCounts {
map: self.request_counts.clone(),
}
}

fn add_cluster(&mut self, cluster: &Cluster) -> anyhow::Result<()> {
let cluster = cluster.clone();
self.clusters.insert(cluster.cluster_id.clone(), cluster);
Expand Down

0 comments on commit b34b60f

Please sign in to comment.