commit
08eb4ad775
|
@ -31,6 +31,17 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "796540673305a66d127804eef19ad696f1f204b8c1025aaca4958c17eab32877"
|
||||
dependencies = [
|
||||
"getrandom 0.2.2",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.2"
|
||||
|
@ -96,6 +107,15 @@ version = "0.3.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
|
@ -159,7 +179,7 @@ dependencies = [
|
|||
name = "arrow_util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.2",
|
||||
"arrow 0.1.0",
|
||||
"futures",
|
||||
"hashbrown 0.11.2",
|
||||
|
@ -372,7 +392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"arrayvec 0.5.2",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
|
@ -424,6 +444,12 @@ version = "3.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -804,7 +830,7 @@ name = "datafusion"
|
|||
version = "4.0.0-SNAPSHOT"
|
||||
source = "git+https://github.com/apache/arrow-datafusion.git?rev=9cf32cf2cda8472b87130142c4eee1126d4d9cbe#9cf32cf2cda8472b87130142c4eee1126d4d9cbe"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.2",
|
||||
"arrow 4.0.0-SNAPSHOT",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
|
@ -830,6 +856,15 @@ dependencies = [
|
|||
"futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debugid"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91cf5a8c2f2097e2a32627123508635d47ce10563d999ec1a95addf08b502ba"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difference"
|
||||
version = "2.0.0"
|
||||
|
@ -1319,7 +1354,7 @@ version = "0.11.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1477,6 +1512,24 @@ dependencies = [
|
|||
"hashbrown 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inferno"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37fb405dbcc505ed20838c4f8dad7b901094de90add237755df54bd5dcda2fdd"
|
||||
dependencies = [
|
||||
"ahash 0.6.3",
|
||||
"atty",
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"num-format",
|
||||
"quick-xml",
|
||||
"rgb",
|
||||
"str_stack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "influxdb2_client"
|
||||
version = "0.1.0"
|
||||
|
@ -1541,6 +1594,7 @@ dependencies = [
|
|||
"panic_logging",
|
||||
"parking_lot",
|
||||
"parquet 0.1.0",
|
||||
"pprof",
|
||||
"predicates",
|
||||
"prettytable-rs",
|
||||
"prost",
|
||||
|
@ -1738,7 +1792,7 @@ version = "0.7.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"arrayvec 0.5.2",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"ryu",
|
||||
|
@ -1859,6 +1913,16 @@ version = "2.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "memmap"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.3"
|
||||
|
@ -2024,6 +2088,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
|
@ -2095,6 +2165,16 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-format"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.12",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
|
@ -2625,6 +2705,27 @@ dependencies = [
|
|||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pprof"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234cb1ca0d59aa771d9bc7e268739d7aef6ca7e9e8d3b78d92f266cd663fd0c1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"inferno",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"parking_lot",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"prost-derive",
|
||||
"symbolic-demangle",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
|
@ -2824,6 +2925,15 @@ version = "1.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26aab6b48e2590e4a64d1ed808749ba06257882b461d01ca71baeb747074a6dd"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
|
@ -3095,6 +3205,15 @@ dependencies = [
|
|||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fddb3b23626145d1776addfc307e1a1851f60ef6ca64f376bcb889697144cf0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
|
@ -3702,6 +3821,12 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "str_stack"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
@ -3738,6 +3863,28 @@ version = "2.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
||||
|
||||
[[package]]
|
||||
name = "symbolic-common"
|
||||
version = "8.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be7dfa630954f18297ceae1ff2890cb7f5008a0b2d2106b0468dafc45b0b6b12"
|
||||
dependencies = [
|
||||
"debugid",
|
||||
"memmap",
|
||||
"stable_deref_trait",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symbolic-demangle"
|
||||
version = "8.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b4ba42bd1221803e965054767b1899f2db9a12c89969965c6cb3a02af7014eb"
|
||||
dependencies = [
|
||||
"rustc-demangle",
|
||||
"symbolic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.67"
|
||||
|
|
|
@ -92,6 +92,7 @@ parking_lot = "0.11.1"
|
|||
itertools = "0.9.0"
|
||||
# used by arrow/datafusion anyway
|
||||
prettytable-rs = "0.8"
|
||||
pprof = { version = "^0.4", default-features = false, features = ["flamegraph", "protobuf"] }
|
||||
prost = "0.7"
|
||||
# Forked to upgrade hyper and tokio
|
||||
routerify = { git = "https://github.com/influxdata/routerify", rev = "274e250" }
|
||||
|
|
|
@ -26,21 +26,24 @@ use server::{ConnectionManager, Server as AppServer};
|
|||
use bytes::{Bytes, BytesMut};
|
||||
use futures::{self, StreamExt};
|
||||
use http::header::{CONTENT_ENCODING, CONTENT_TYPE};
|
||||
use hyper::{Body, Method, Request, Response, StatusCode};
|
||||
use hyper::{http::HeaderValue, Body, Method, Request, Response, StatusCode};
|
||||
use observability_deps::{
|
||||
opentelemetry::KeyValue,
|
||||
tracing::{self, debug, error},
|
||||
tracing::{self, debug, error, info},
|
||||
};
|
||||
use routerify::{prelude::*, Middleware, RequestInfo, Router, RouterError, RouterService};
|
||||
use serde::Deserialize;
|
||||
use snafu::{OptionExt, ResultExt, Snafu};
|
||||
|
||||
use hyper::server::conn::AddrIncoming;
|
||||
use pprof::protos::Message;
|
||||
use std::num::NonZeroI32;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
str::{self, FromStr},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::time::Duration;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
/// Constants used in API error codes.
|
||||
|
@ -217,6 +220,15 @@ pub enum ApplicationError {
|
|||
partition: String,
|
||||
table_name: String,
|
||||
},
|
||||
|
||||
#[snafu(display("PProf error: {}", source))]
|
||||
PProf { source: pprof::Error },
|
||||
|
||||
#[snafu(display("Protobuf error: {}", source))]
|
||||
Prost { source: prost::EncodeError },
|
||||
|
||||
#[snafu(display("Empty flamegraph"))]
|
||||
EmptyFlamegraph,
|
||||
}
|
||||
|
||||
impl ApplicationError {
|
||||
|
@ -251,6 +263,9 @@ impl ApplicationError {
|
|||
Self::ParsingFormat { .. } => self.bad_request(),
|
||||
Self::Planning { .. } => self.bad_request(),
|
||||
Self::NoSnapshot { .. } => self.not_modified(),
|
||||
Self::PProf { .. } => self.internal_error(),
|
||||
Self::Prost { .. } => self.internal_error(),
|
||||
Self::EmptyFlamegraph => self.no_content(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,6 +297,13 @@ impl ApplicationError {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn no_content(&self) -> Response<Body> {
|
||||
Response::builder()
|
||||
.status(StatusCode::NO_CONTENT)
|
||||
.body(self.body())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn body(&self) -> Body {
|
||||
let json =
|
||||
serde_json::json!({"error": self.to_string(), "error_code": self.api_error_code()})
|
||||
|
@ -340,6 +362,8 @@ where
|
|||
.get("/iox/api/v1/databases/:name/query", query::<M>)
|
||||
.get("/api/v1/partitions", list_partitions::<M>)
|
||||
.post("/api/v1/snapshot", snapshot_partition::<M>)
|
||||
.get("/debug/pprof", pprof_home::<M>)
|
||||
.get("/debug/pprof/profile", pprof_profile::<M>)
|
||||
// Specify the error handler to handle any errors caused by
|
||||
// a route or any middleware.
|
||||
.err_handler_with_info(error_handler)
|
||||
|
@ -785,6 +809,97 @@ async fn snapshot_partition<M: ConnectionManager + Send + Sync + Debug + 'static
|
|||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
async fn pprof_home<M: ConnectionManager + Send + Sync + Debug + 'static>(
|
||||
req: Request<Body>,
|
||||
) -> Result<Response<Body>, ApplicationError> {
|
||||
let default_host = HeaderValue::from_static("localhost");
|
||||
let host = req
|
||||
.headers()
|
||||
.get("host")
|
||||
.unwrap_or(&default_host)
|
||||
.to_str()
|
||||
.unwrap_or_default();
|
||||
let cmd = format!(
|
||||
"/debug/pprof/profile?seconds={}",
|
||||
PProfArgs::default_seconds()
|
||||
);
|
||||
Ok(Response::new(Body::from(format!(
|
||||
r#"<a href="{}">http://{}{}</a>"#,
|
||||
cmd, host, cmd
|
||||
))))
|
||||
}
|
||||
|
||||
async fn dump_rsprof(seconds: u64, frequency: i32) -> pprof::Result<pprof::Report> {
|
||||
let guard = pprof::ProfilerGuard::new(frequency)?;
|
||||
info!(
|
||||
"start profiling {} seconds with frequency {} /s",
|
||||
seconds, frequency
|
||||
);
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(seconds)).await;
|
||||
|
||||
info!(
|
||||
"done profiling {} seconds with frequency {} /s",
|
||||
seconds, frequency
|
||||
);
|
||||
guard.report().build()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PProfArgs {
|
||||
#[serde(default = "PProfArgs::default_seconds")]
|
||||
seconds: u64,
|
||||
#[serde(default = "PProfArgs::default_frequency")]
|
||||
frequency: NonZeroI32,
|
||||
}
|
||||
|
||||
impl PProfArgs {
|
||||
fn default_seconds() -> u64 {
|
||||
30
|
||||
}
|
||||
|
||||
// 99Hz to avoid coinciding with special periods
|
||||
fn default_frequency() -> NonZeroI32 {
|
||||
NonZeroI32::new(99).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
async fn pprof_profile<M: ConnectionManager + Send + Sync + Debug + 'static>(
|
||||
req: Request<Body>,
|
||||
) -> Result<Response<Body>, ApplicationError> {
|
||||
let query_string = req.uri().query().unwrap_or_default();
|
||||
let query: PProfArgs =
|
||||
serde_urlencoded::from_str(query_string).context(InvalidQueryString { query_string })?;
|
||||
|
||||
let report = dump_rsprof(query.seconds, query.frequency.get())
|
||||
.await
|
||||
.context(PProf)?;
|
||||
|
||||
let mut body: Vec<u8> = Vec::new();
|
||||
|
||||
// render flamegraph when opening in the browser
|
||||
// otherwise render as protobuf; works great with: go tool pprof http://..../debug/pprof/profile
|
||||
if req
|
||||
.headers()
|
||||
.get_all("Accept")
|
||||
.iter()
|
||||
.flat_map(|i| i.to_str().unwrap_or_default().split(','))
|
||||
.any(|i| i == "text/html" || i == "image/svg+xml")
|
||||
{
|
||||
report.flamegraph(&mut body).context(PProf)?;
|
||||
if body.is_empty() {
|
||||
return EmptyFlamegraph.fail();
|
||||
}
|
||||
} else {
|
||||
let profile = report.pprof().context(PProf)?;
|
||||
profile.encode(&mut body).context(Prost)?;
|
||||
}
|
||||
|
||||
Ok(Response::new(Body::from(body)))
|
||||
}
|
||||
|
||||
pub async fn serve<M>(
|
||||
addr: AddrIncoming,
|
||||
server: Arc<AppServer<M>>,
|
||||
|
|
Loading…
Reference in New Issue