opentelemetry_lambda_tower/
cold_start.rs

1//! Cold start detection for Lambda functions.
2//!
3//! Tracks whether the current invocation is a cold start (first invocation
4//! in this container) following the pattern from the OpenTelemetry Node.js
5//! Lambda instrumentation.
6
7use std::sync::atomic::{AtomicBool, Ordering};
8
9/// Global flag tracking whether we've seen an invocation yet.
10/// Starts as `true` and is set to `false` after the first invocation.
11static IS_COLD_START: AtomicBool = AtomicBool::new(true);
12
13/// Checks if this is a cold start and clears the flag.
14///
15/// Returns `true` on the first call (cold start), and `false` on all
16/// subsequent calls (warm starts).
17///
18/// This function also checks the `AWS_LAMBDA_INITIALIZATION_TYPE` environment
19/// variable. If set to `"provisioned-concurrency"`, the function returns
20/// `false` since provisioned concurrency pre-warms the Lambda container.
21///
22/// # Thread Safety
23///
24/// This function is safe to call from multiple threads. The atomic swap
25/// ensures that exactly one invocation will see `true`.
26///
27/// # Example
28///
29/// ```
30/// use opentelemetry_lambda_tower::check_cold_start;
31///
32/// // First call returns true (cold start)
33/// // Note: In tests, this may return false if other tests ran first
34/// let _ = check_cold_start();
35///
36/// // Subsequent calls return false (warm start)
37/// assert!(!check_cold_start());
38/// ```
39pub fn check_cold_start() -> bool {
40    // Check for provisioned concurrency - never a cold start
41    if std::env::var("AWS_LAMBDA_INITIALIZATION_TYPE")
42        .map(|v| v == "provisioned-concurrency")
43        .unwrap_or(false)
44    {
45        // Still clear the flag but return false
46        IS_COLD_START.store(false, Ordering::SeqCst);
47        return false;
48    }
49
50    // Atomically swap to false and return the previous value
51    // First call: swaps true -> false, returns true
52    // Subsequent calls: swaps false -> false, returns false
53    IS_COLD_START.swap(false, Ordering::SeqCst)
54}
55
56/// Resets the cold start flag for testing purposes.
57///
58/// # Safety
59///
60/// This function should only be used in tests. Using it in production
61/// code will cause incorrect cold start reporting.
62#[cfg(test)]
63pub(crate) fn reset_cold_start_for_testing() {
64    IS_COLD_START.store(true, Ordering::SeqCst);
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use serial_test::serial;
71
72    #[test]
73    #[serial]
74    fn test_cold_start_first_invocation() {
75        reset_cold_start_for_testing();
76
77        // First call should return true
78        assert!(check_cold_start());
79
80        // Second call should return false
81        assert!(!check_cold_start());
82
83        // Third call should also return false
84        assert!(!check_cold_start());
85    }
86
87    #[test]
88    #[serial]
89    fn test_provisioned_concurrency_not_cold_start() {
90        reset_cold_start_for_testing();
91
92        // Set provisioned concurrency environment variable
93        temp_env::with_var(
94            "AWS_LAMBDA_INITIALIZATION_TYPE",
95            Some("provisioned-concurrency"),
96            || {
97                // Should return false even on first call
98                assert!(!check_cold_start());
99            },
100        );
101    }
102
103    #[test]
104    #[serial]
105    fn test_other_initialization_type_is_cold_start() {
106        reset_cold_start_for_testing();
107
108        // Set a different initialization type
109        temp_env::with_var("AWS_LAMBDA_INITIALIZATION_TYPE", Some("on-demand"), || {
110            // Should still be a cold start
111            assert!(check_cold_start());
112        });
113    }
114}