Skip to content

Commit 29a3c84

Browse files
authored
feat: Early load ustr (#792)
## Problem The first time `Ustr::from()` is called, it takes 10+ ms to init. Right now this is on the critical path and adds to cold start time. ## This PR Start it early in a separate thread, so when it is needed later, it will be already initialized. ## Result Using `extensiononly` stack on Node 22: ### Before - `Datadog Next-Gen Extension ready in`: **57.2** ±3.1 ms (95% confidence interval, 17 data points) - `Metrics aggregator created in`: **10+ ms** (10,000+ us) ### After - `Datadog Next-Gen Extension ready in`: **49.7**±1.9 ms (95% confidence level, 10 data points) - `Metrics aggregator created in`: **277±186 us** (95% confidence level, 10 data points) Thanks @duncanista for identifying this performance bottleneck! Jira: https://datadoghq.atlassian.net/browse/SVLS-7456
1 parent 483ef3b commit 29a3c84

3 files changed

Lines changed: 20 additions & 0 deletions

File tree

bottlecap/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bottlecap/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ opentelemetry-proto = { version = "0.29", features = ["trace", "with-serde", "ge
4242
opentelemetry-semantic-conventions = { version = "0.29", features = ["semconv_experimental"] }
4343
rustls-native-certs = { version = "0.8.1", optional = true }
4444
axum = { version = "0.8.4", default-features = false, features = ["default"] }
45+
ustr = { version = "1.0.0", default-features = false }
4546
# If you are adding or updating a datadog-owned code dependency, please ensure
4647
# that it has a clippy.toml rule for disallowing the reqwest::Client::builder
4748
# method in favor of our

bottlecap/src/bin/bottlecap/main.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ use tokio::{sync::Mutex as TokioMutex, sync::RwLock, sync::mpsc::Sender, task::J
9090
use tokio_util::sync::CancellationToken;
9191
use tracing::{debug, error};
9292
use tracing_subscriber::EnvFilter;
93+
use ustr::Ustr;
9394

9495
#[allow(clippy::struct_field_names)]
9596
struct PendingFlushHandles {
@@ -331,6 +332,7 @@ async fn register(client: &Client) -> Result<RegisterResponse> {
331332
#[tokio::main]
332333
async fn main() -> Result<()> {
333334
let start_time = Instant::now();
335+
init_ustr();
334336
let (aws_config, aws_credentials, config) = load_configs(start_time);
335337

336338
enable_logging_subsystem(&config);
@@ -382,6 +384,14 @@ async fn main() -> Result<()> {
382384
}
383385
}
384386

387+
// Ustr initialization can take 10+ ms.
388+
// Start it early in a separate thread so it won't become a bottleneck later when SortedTags::parse() is called.
389+
fn init_ustr() {
390+
tokio::spawn(async {
391+
Ustr::from("");
392+
});
393+
}
394+
385395
fn load_configs(start_time: Instant) -> (AwsConfig, AwsCredentials, Arc<Config>) {
386396
// First load the AWS configuration
387397
let aws_config = AwsConfig::from_env(start_time);
@@ -479,13 +489,21 @@ async fn extension_loop_active(
479489
event_bus.get_sender_copy(),
480490
);
481491

492+
let metrics_aggr_init_start_time = Instant::now();
482493
let metrics_aggr = Arc::new(Mutex::new(
483494
MetricsAggregator::new(
484495
SortedTags::parse(&tags_provider.get_tags_string()).unwrap_or(EMPTY_TAGS),
485496
CONTEXTS,
486497
)
487498
.expect("failed to create aggregator"),
488499
));
500+
debug!(
501+
"Metrics aggregator created in {:} microseconds",
502+
metrics_aggr_init_start_time
503+
.elapsed()
504+
.as_micros()
505+
.to_string()
506+
);
489507

490508
let metrics_flushers = Arc::new(TokioMutex::new(start_metrics_flushers(
491509
Arc::clone(&api_key_factory),

0 commit comments

Comments
 (0)