I'm attempting to test out cargo lambda for use in deploying rust aws lambdas. Initially when it was just the default cargo lambda that is generated, the cold start times were quite fast, but after adding in reqwest the cold starts are over a hundred milliseconds. I found this blog that hinted that the problem might be native-tls, so I've tried to remove all traces of native-tls and instead am using rustls. This is what my Cargo.toml looks like:
[package]
name = "rust-test-lambda"
version = "0.1.0"
edition = "2021"
[dependencies]
lambda_runtime = "0.8.3"
#openapi = "0.1.5"
#opentelemetry = { version = "0.21.0"}
openssl = { version = "0.10.35", features = ["vendored"], default-features = false }
# Due to https://www.cargo-lambda.info/guide/cross-compiling.html#known-cross-compilation-issues
# we must enable `native-tls-vendored`
reqwest = { version = "0.11", features = [ "serde_json", "blocking", "json", "rustls-tls"], default-features = false }
serde = { version = "1.0.136", default-features = false }
serde-this-or-that = { version = "0.4.2",default-features = false}
serde_derive = { version = "1.0.193", default-features = false}
serde_json = { version = "1.0.108", default-features = false}
serde_with = { version = "3.4.0", default-features = false}
#tokio = { version = "1", features = ["macros"], default-features = false }
tracing = { version = "0.1", features = ["log"], default-features = false }
tracing-subscriber = { version = "0.3", features = ["fmt"], default-features = false }
When I run cargo tree, there is no mention of native
rust-test-lambda v0.1.0 (/Users/tylerthrailkill/Documents/dev/work/rust-test-lambda)
āāā lambda_runtime v0.8.3
ā āāā async-stream v0.3.5
ā ā āāā async-stream-impl v0.3.5 (proc-macro)
ā ā ā āāā proc-macro2 v1.0.69
ā ā ā ā āāā unicode-ident v1.0.12
ā ā ā āāā quote v1.0.33
ā ā ā ā āāā proc-macro2 v1.0.69 (*)
ā ā ā āāā syn v2.0.39
ā ā ā āāā proc-macro2 v1.0.69 (*)
ā ā ā āāā quote v1.0.33 (*)
ā ā ā āāā unicode-ident v1.0.12
ā ā āāā futures-core v0.3.29
ā ā āāā pin-project-lite v0.2.13
ā āāā base64 v0.20.0
ā āāā bytes v1.5.0
ā āāā futures v0.3.29
ā ā āāā futures-channel v0.3.29
ā ā ā āāā futures-core v0.3.29
ā ā ā āāā futures-sink v0.3.29
ā ā āāā futures-core v0.3.29
ā ā āāā futures-executor v0.3.29
ā ā ā āāā futures-core v0.3.29
ā ā ā āāā futures-task v0.3.29
ā ā ā āāā futures-util v0.3.29
ā ā ā āāā futures-channel v0.3.29 (*)
ā ā ā āāā futures-core v0.3.29
ā ā ā āāā futures-io v0.3.29
ā ā ā āāā futures-macro v0.3.29 (proc-macro)
ā ā ā ā āāā proc-macro2 v1.0.69 (*)
ā ā ā ā āāā quote v1.0.33 (*)
ā ā ā ā āāā syn v2.0.39 (*)
ā ā ā āāā futures-sink v0.3.29
ā ā ā āāā futures-task v0.3.29
ā ā ā āāā memchr v2.6.4
ā ā ā āāā pin-project-lite v0.2.13
ā ā ā āāā pin-utils v0.1.0
ā ā ā āāā slab v0.4.9
ā ā ā [build-dependencies]
ā ā ā āāā autocfg v1.1.0
ā ā āāā futures-io v0.3.29
ā ā āāā futures-sink v0.3.29
ā ā āāā futures-task v0.3.29
ā ā āāā futures-util v0.3.29 (*)
ā āāā http v0.2.11
ā ā āāā bytes v1.5.0
ā ā āāā fnv v1.0.7
ā ā āāā itoa v1.0.9
ā āāā http-body v0.4.5
ā ā āāā bytes v1.5.0
ā ā āāā http v0.2.11 (*)
ā ā āāā pin-project-lite v0.2.13
ā āāā http-serde v1.1.3
ā ā āāā http v0.2.11 (*)
ā ā āāā serde v1.0.193
ā ā āāā serde_derive v1.0.193 (proc-macro)
ā ā āāā proc-macro2 v1.0.69 (*)
ā ā āāā quote v1.0.33 (*)
ā ā āāā syn v2.0.39 (*)
ā āāā hyper v0.14.27
ā ā āāā bytes v1.5.0
ā ā āāā futures-channel v0.3.29 (*)
ā ā āāā futures-core v0.3.29
ā ā āāā futures-util v0.3.29 (*)
ā ā āāā h2 v0.3.22
ā ā ā āāā bytes v1.5.0
ā ā ā āāā fnv v1.0.7
ā ā ā āāā futures-core v0.3.29
ā ā ā āāā futures-sink v0.3.29
ā ā ā āāā futures-util v0.3.29 (*)
ā ā ā āāā http v0.2.11 (*)
ā ā ā āāā indexmap v2.1.0
ā ā ā ā āāā equivalent v1.0.1
ā ā ā ā āāā hashbrown v0.14.2
ā ā ā āāā slab v0.4.9 (*)
ā ā ā āāā tokio v1.34.0
ā ā ā ā āāā bytes v1.5.0
ā ā ā ā āāā libc v0.2.150
ā ā ā ā āāā mio v0.8.9
ā ā ā ā ā āāā libc v0.2.150
ā ā ā ā āāā num_cpus v1.16.0
ā ā ā ā ā āāā libc v0.2.150
ā ā ā ā āāā pin-project-lite v0.2.13
ā ā ā ā āāā socket2 v0.5.5
ā ā ā ā ā āāā libc v0.2.150
ā ā ā ā āāā tokio-macros v2.2.0 (proc-macro)
ā ā ā ā āāā proc-macro2 v1.0.69 (*)
ā ā ā ā āāā quote v1.0.33 (*)
ā ā ā ā āāā syn v2.0.39 (*)
ā ā ā āāā tokio-util v0.7.10
ā ā ā ā āāā bytes v1.5.0
ā ā ā ā āāā futures-core v0.3.29
ā ā ā ā āāā futures-sink v0.3.29
ā ā ā ā āāā pin-project-lite v0.2.13
ā ā ā ā āāā tokio v1.34.0 (*)
ā ā ā ā āāā tracing v0.1.40
ā ā ā ā āāā log v0.4.20
ā ā ā ā āāā pin-project-lite v0.2.13
ā ā ā ā āāā tracing-attributes v0.1.27 (proc-macro)
ā ā ā ā ā āāā proc-macro2 v1.0.69 (*)
ā ā ā ā ā āāā quote v1.0.33 (*)
ā ā ā ā ā āāā syn v2.0.39 (*)
ā ā ā ā āāā tracing-core v0.1.32
ā ā ā ā āāā once_cell v1.18.0
ā ā ā āāā tracing v0.1.40 (*)
ā ā āāā http v0.2.11 (*)
ā ā āāā http-body v0.4.5 (*)
ā ā āāā httparse v1.8.0
ā ā āāā httpdate v1.0.3
ā ā āāā itoa v1.0.9
ā ā āāā pin-project-lite v0.2.13
ā ā āāā socket2 v0.4.10
ā ā ā āāā libc v0.2.150
ā ā āāā tokio v1.34.0 (*)
ā ā āāā tower-service v0.3.2
ā ā āāā tracing v0.1.40 (*)
ā ā āāā want v0.3.1
ā ā āāā try-lock v0.2.4
ā āāā lambda_runtime_api_client v0.8.0
ā ā āāā http v0.2.11 (*)
ā ā āāā hyper v0.14.27 (*)
ā ā āāā tokio v1.34.0 (*)
ā ā āāā tower-service v0.3.2
ā āāā serde v1.0.193 (*)
ā āāā serde_json v1.0.108
ā ā āāā itoa v1.0.9
ā ā āāā ryu v1.0.15
ā ā āāā serde v1.0.193 (*)
ā āāā serde_path_to_error v0.1.14
ā ā āāā itoa v1.0.9
ā ā āāā serde v1.0.193 (*)
ā āāā tokio v1.34.0 (*)
ā āāā tokio-stream v0.1.14
ā ā āāā futures-core v0.3.29
ā ā āāā pin-project-lite v0.2.13
ā ā āāā tokio v1.34.0 (*)
ā āāā tower v0.4.13
ā ā āāā futures-core v0.3.29
ā ā āāā futures-util v0.3.29 (*)
ā ā āāā pin-project v1.1.3
ā ā ā āāā pin-project-internal v1.1.3 (proc-macro)
ā ā ā āāā proc-macro2 v1.0.69 (*)
ā ā ā āāā quote v1.0.33 (*)
ā ā ā āāā syn v2.0.39 (*)
ā ā āāā pin-project-lite v0.2.13
ā ā āāā tower-layer v0.3.2
ā ā āāā tower-service v0.3.2
ā ā āāā tracing v0.1.40 (*)
ā āāā tracing v0.1.40 (*)
āāā openssl v0.10.60
ā āāā bitflags v2.4.1
ā āāā cfg-if v1.0.0
ā āāā foreign-types v0.3.2
ā ā āāā foreign-types-shared v0.1.1
ā āāā libc v0.2.150
ā āāā once_cell v1.18.0
ā āāā openssl-macros v0.1.1 (proc-macro)
ā ā āāā proc-macro2 v1.0.69 (*)
ā ā āāā quote v1.0.33 (*)
ā ā āāā syn v2.0.39 (*)
ā āāā openssl-sys v0.9.96
ā āāā libc v0.2.150
ā [build-dependencies]
ā āāā cc v1.0.83
ā ā āāā libc v0.2.150
ā āāā openssl-src v300.1.6+3.1.4
ā ā āāā cc v1.0.83 (*)
ā āāā pkg-config v0.3.27
ā āāā vcpkg v0.2.15
āāā reqwest v0.11.22
ā āāā base64 v0.21.5
ā āāā bytes v1.5.0
ā āāā encoding_rs v0.8.33
ā ā āāā cfg-if v1.0.0
ā āāā futures-core v0.3.29
ā āāā futures-util v0.3.29 (*)
ā āāā h2 v0.3.22 (*)
ā āāā http v0.2.11 (*)
ā āāā http-body v0.4.5 (*)
ā āāā hyper v0.14.27 (*)
ā āāā hyper-rustls v0.24.2
ā ā āāā futures-util v0.3.29 (*)
ā ā āāā http v0.2.11 (*)
ā ā āāā hyper v0.14.27 (*)
ā ā āāā rustls v0.21.9
ā ā ā āāā log v0.4.20
ā ā ā āāā ring v0.17.5
ā ā ā ā āāā getrandom v0.2.11
ā ā ā ā ā āāā cfg-if v1.0.0
ā ā ā ā ā āāā libc v0.2.150
ā ā ā ā āāā untrusted v0.9.0
ā ā ā ā [build-dependencies]
ā ā ā ā āāā cc v1.0.83 (*)
ā ā ā āāā rustls-webpki v0.101.7
ā ā ā ā āāā ring v0.17.5 (*)
ā ā ā ā āāā untrusted v0.9.0
ā ā ā āāā sct v0.7.1
ā ā ā āāā ring v0.17.5 (*)
ā ā ā āāā untrusted v0.9.0
ā ā āāā tokio v1.34.0 (*)
ā ā āāā tokio-rustls v0.24.1
ā ā āāā rustls v0.21.9 (*)
ā ā āāā tokio v1.34.0 (*)
ā āāā ipnet v2.9.0
ā āāā log v0.4.20
ā āāā mime v0.3.17
ā āāā once_cell v1.18.0
ā āāā percent-encoding v2.3.0
ā āāā pin-project-lite v0.2.13
ā āāā rustls v0.21.9 (*)
ā āāā rustls-pemfile v1.0.4
ā ā āāā base64 v0.21.5
ā āāā serde v1.0.193 (*)
ā āāā serde_json v1.0.108 (*)
ā āāā serde_urlencoded v0.7.1
ā ā āāā form_urlencoded v1.2.0
ā ā ā āāā percent-encoding v2.3.0
ā ā āāā itoa v1.0.9
ā ā āāā ryu v1.0.15
ā ā āāā serde v1.0.193 (*)
ā āāā system-configuration v0.5.1
ā ā āāā bitflags v1.3.2
ā ā āāā core-foundation v0.9.3
ā ā ā āāā core-foundation-sys v0.8.4
ā ā ā āāā libc v0.2.150
ā ā āāā system-configuration-sys v0.5.0
ā ā āāā core-foundation-sys v0.8.4
ā ā āāā libc v0.2.150
ā āāā tokio v1.34.0 (*)
ā āāā tokio-rustls v0.24.1 (*)
ā āāā tower-service v0.3.2
ā āāā url v2.4.1
ā ā āāā form_urlencoded v1.2.0 (*)
ā ā āāā idna v0.4.0
ā ā ā āāā unicode-bidi v0.3.13
ā ā ā āāā unicode-normalization v0.1.22
ā ā ā āāā tinyvec v1.6.0
ā ā ā āāā tinyvec_macros v0.1.1
ā ā āāā percent-encoding v2.3.0
ā āāā webpki-roots v0.25.3
āāā serde v1.0.193 (*)
āāā serde-this-or-that v0.4.2
ā āāā serde v1.0.193 (*)
āāā serde_derive v1.0.193 (proc-macro) (*)
āāā serde_json v1.0.108 (*)
āāā serde_with v3.4.0
ā āāā serde v1.0.193 (*)
āāā tracing v0.1.40 (*)
āāā tracing-subscriber v0.3.18
āāā sharded-slab v0.1.7
ā āāā lazy_static v1.4.0
āāā thread_local v1.1.7
ā āāā cfg-if v1.0.0
ā āāā once_cell v1.18.0
āāā tracing-core v0.1.32 (*)
I am still seeing at least 100ms cold start times though. My lambda does need to call out to an https service, so I cannot avoid tls.
Here is most of the code, excluding structs. If there's anything else I can provide, please let me know...
mod models;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT, CONTENT_TYPE, AUTHORIZATION};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use crate::models::redacted::api::{LoginForm, LoginResponse, CalculateRequest, CalculateResponse};
use serde::{Deserialize, Serialize};
use crate::models::UnifiedInput;
static EXTERNAL_COMPANY_ID: &str = "redacted";
/// This is a made-up example. Requests come into the runtime as unicode
/// strings in json format, which can map to any structure that implements `serde::Deserialize`
/// The runtime pays no attention to the contents of the request payload.
#[derive(Deserialize)]
struct Request {
command: String,
}
/// This is a made-up example of what a response structure may look like.
/// There is no restriction on what it can be. The runtime requires responses
/// to be serialized into json. The runtime pays no attention
/// to the contents of the response payload.
#[derive(Serialize)]
struct Response {
req_id: String,
msg: String,
}
fn construct_headers(token: String) -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(USER_AGENT, HeaderValue::from_static("reqwest"));
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/vnd.tri.redacted.idt+json"));
let bearer_token = format!("Bearer {}", token);
headers.insert(AUTHORIZATION, HeaderValue::from_str(&bearer_token).unwrap());
headers.insert("Correlation-Id", "redacted".parse().unwrap());
headers
}
/// This is the main body for the function.
/// Write your code inside it.
/// There are some code example in the following URLs:
/// - https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples
/// - https://github.com/aws-samples/serverless-rust-demo/
async fn function_handler(event: LambdaEvent<UnifiedInput>) -> Result<CalculateResponse, Error> {
// Extract some useful info from the request
let command = event.payload.opportunity.prospect_id;
let client = reqwest::Client::new();
let form = LoginForm {
client_id: "redacted".to_string(),
scopes: "redacted".to_string(),
grant_type: "client_credentials".to_string(),
client_secret: "redacted".to_string(),
};
use std::time::Instant;
let now = Instant::now();
let res = client.post("https://redacted.com/oauth2/v1/token")
.form(&form)
.send()
// .await.unwrap().text().await.unwrap();
.await;
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
let response = res.unwrap();
let token = match response.status() {
reqwest::StatusCode::OK => {
// on success, parse our JSON to an APIResponse
match response.json::<LoginResponse>().await {
Ok(parsed) => {
println!("Login Succeeded! {:?}", parsed);
Ok(parsed.token)
},
Err(_) => {
println!("Unable to login");
Err("Unable to login")
},
}
}
other => {
panic!("Uh oh! Something unexpected happened: {:?}", other);
}
};
println!("{:#?}", token);
let access_token = token.unwrap();
let calculate_json = r#"
{
"key": "value"
}
"#;
let calculate_request: CalculateRequest = serde_json::from_str(calculate_json).unwrap();
let now = Instant::now();
let calculate_response = client.post("https://redacted")
.json(&calculate_request)
.headers(construct_headers(access_token))
.send()
// .await.unwrap().text().await.unwrap();
.await
.unwrap();
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
match calculate_response.status() {
reqwest::StatusCode::OK => {
let response_body = calculate_response.text().await.unwrap();
let response_body_clone = response_body.clone();
match serde_json::from_str::<CalculateResponse>(&response_body_clone) {
Ok(parsed) => {
println!("Calculate and deserialize succeeded! {:?}", parsed);
Ok(parsed)
},
Err(err) => {
println!("Unable to deserialize {:?} {:?}", err, response_body);
Err(Error::try_from("Unable to deserialize").unwrap())
},
}
}
other => {
println!("lkdf {:?}", other);
Err(Error::try_from(calculate_response.text().await.unwrap()).unwrap())
}
}
}
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
// disable printing the name of the module in every log line.
.with_target(false)
// disabling time is handy because CloudWatch will add the ingestion time.
.without_time()
.init();
run(service_fn(function_handler)).await
}