@@ -4,18 +4,18 @@ use crate::{
44 types:: { invoke_request_id, IntoFunctionResponse , LambdaEvent } ,
55 Config , Context , Diagnostic ,
66} ;
7- #[ cfg( feature = "experimental- concurrency" ) ]
7+ #[ cfg( feature = "concurrency-tokio " ) ]
88use futures:: stream:: FuturesUnordered ;
99use http_body_util:: BodyExt ;
1010use lambda_runtime_api_client:: { BoxError , Client as ApiClient } ;
1111use serde:: { Deserialize , Serialize } ;
12- #[ cfg( feature = "experimental- concurrency" ) ]
12+ #[ cfg( feature = "concurrency-tokio " ) ]
1313use std:: fmt;
1414use std:: { env, fmt:: Debug , future:: Future , io, sync:: Arc } ;
1515use tokio_stream:: { Stream , StreamExt } ;
1616use tower:: { Layer , Service , ServiceExt } ;
1717use tracing:: trace;
18- #[ cfg( feature = "experimental- concurrency" ) ]
18+ #[ cfg( feature = "concurrency-tokio " ) ]
1919use tracing:: { debug, error, info_span, warn, Instrument } ;
2020
2121/* ----------------------------------------- INVOCATION ---------------------------------------- */
9696 /// Note that manually creating a [Runtime] does not add tracing to the executed handler
9797 /// as is done by [super::run]. If you want to add the default tracing functionality, call
9898 /// [Runtime::layer] with a [super::layers::TracingLayer].
99+ ///
100+ ///
101+ /// # Panics
102+ ///
103+ /// This function panics if required Lambda environment variables are missing
104+ /// (`AWS_LAMBDA_FUNCTION_NAME`, `AWS_LAMBDA_FUNCTION_MEMORY_SIZE`,
105+ /// `AWS_LAMBDA_FUNCTION_VERSION`, `AWS_LAMBDA_RUNTIME_API`).
99106 pub fn new ( handler : F ) -> Self {
100107 trace ! ( "Loading config from env" ) ;
101108 let config = Arc :: new ( Config :: from_env ( ) ) ;
@@ -154,18 +161,28 @@ impl<S> Runtime<S> {
154161 }
155162}
156163
157- #[ cfg( feature = "experimental- concurrency" ) ]
164+ #[ cfg( feature = "concurrency-tokio " ) ]
158165impl < S > Runtime < S >
159166where
160167 S : Service < LambdaInvocation , Response = ( ) , Error = BoxError > + Clone + Send + ' static ,
161168 S :: Future : Send ,
162169{
163- /// Start the runtime in concurrent mode when configured for Lambda managed-concurrency.
170+ /// Start the runtime and begin polling for events on the Lambda Runtime API, in a mode
171+ /// that supports Lambda-managed concurrency.
172+ ///
173+ /// When `AWS_LAMBDA_MAX_CONCURRENCY` is set to a value greater than 1, this
174+ /// spawns multiple worker tasks to handle concurrent invocations. When the
175+ /// environment variable is unset or `<= 1`, it falls back to sequential
176+ /// behavior, so the same handler can run on both classic Lambda and Lambda
177+ /// Managed Instances.
164178 ///
165- /// If `AWS_LAMBDA_MAX_CONCURRENCY` is not set or is `<= 1`, this falls back to the
166- /// sequential `run_with_incoming` loop so that the same handler can run on both
167- /// classic Lambda and Lambda Managed Instances.
168- #[ cfg_attr( docsrs, doc( cfg( feature = "experimental-concurrency" ) ) ) ]
179+ /// This feature requires `Clone + Send + 'static` bounds on the service. If your handler
180+ /// cannot satisfy these bounds, use [Runtime::run] instead.
181+ ///
182+ /// # Panics
183+ ///
184+ /// This function panics if called outside of a Tokio runtime with `AWS_LAMBDA_MAX_CONCURRENCY > 1`
185+ #[ cfg_attr( docsrs, doc( cfg( feature = "concurrency-tokio" ) ) ) ]
169186 pub async fn run_concurrent ( self ) -> Result < ( ) , BoxError > {
170187 if self . concurrency_limit > 1 {
171188 trace ! ( "Concurrent mode: _X_AMZN_TRACE_ID is not set; use context.xray_trace_id" ) ;
@@ -259,20 +276,20 @@ where
259276 }
260277}
261278
262- #[ cfg( feature = "experimental- concurrency" ) ]
279+ #[ cfg( feature = "concurrency-tokio " ) ]
263280#[ derive( Debug ) ]
264281enum WorkerError {
265282 CleanExit ( tokio:: task:: Id ) ,
266283 Failure ( tokio:: task:: Id , BoxError ) ,
267284}
268285
269- #[ cfg( feature = "experimental- concurrency" ) ]
286+ #[ cfg( feature = "concurrency-tokio " ) ]
270287#[ derive( Debug ) ]
271288struct ConcurrentWorkerErrors {
272289 errors : Vec < WorkerError > ,
273290}
274291
275- #[ cfg( feature = "experimental- concurrency" ) ]
292+ #[ cfg( feature = "concurrency-tokio " ) ]
276293#[ derive( Serialize ) ]
277294struct ConcurrentWorkerErrorsPayload < ' a > {
278295 message : & ' a str ,
@@ -282,14 +299,14 @@ struct ConcurrentWorkerErrorsPayload<'a> {
282299 failures : Vec < WorkerFailurePayload > ,
283300}
284301
285- #[ cfg( feature = "experimental- concurrency" ) ]
302+ #[ cfg( feature = "concurrency-tokio " ) ]
286303#[ derive( Serialize ) ]
287304struct WorkerFailurePayload {
288305 id : String ,
289306 err : String ,
290307}
291308
292- #[ cfg( feature = "experimental- concurrency" ) ]
309+ #[ cfg( feature = "concurrency-tokio " ) ]
293310impl fmt:: Display for ConcurrentWorkerErrors {
294311 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
295312 let mut clean = Vec :: new ( ) ;
@@ -326,7 +343,7 @@ impl fmt::Display for ConcurrentWorkerErrors {
326343 }
327344}
328345
329- #[ cfg( feature = "experimental- concurrency" ) ]
346+ #[ cfg( feature = "concurrency-tokio " ) ]
330347impl std:: error:: Error for ConcurrentWorkerErrors { }
331348
332349impl < S > Runtime < S >
@@ -335,14 +352,17 @@ where
335352{
336353 /// Start the runtime and begin polling for events on the Lambda Runtime API.
337354 ///
338- /// If `AWS_LAMBDA_MAX_CONCURRENCY` is set, this returns an error because it does not enable
339- /// concurrent polling. Enable the `experimental-concurrency` feature and use
340- /// [`Runtime::run_concurrent`] instead.
355+ /// The runtime will process requests sequentially.
356+ ///
357+ /// If `AWS_LAMBDA_MAX_CONCURRENCY` is set, a warning is logged.
358+ /// Enable the `concurrency-tokio` feature and use [`Runtime::run_concurrent`]
359+ /// to enable concurrent request processing with Lambda Managed Instances.
341360 pub async fn run ( self ) -> Result < ( ) , BoxError > {
342361 if let Some ( raw) = concurrency_env_value ( ) {
343- return Err ( Box :: new ( io:: Error :: other ( format ! (
344- "AWS_LAMBDA_MAX_CONCURRENCY is set to '{raw}', but Runtime::run does not support concurrent polling; enable the experimental-concurrency feature and use Runtime::run_concurrent instead"
345- ) ) ) ) ;
362+ tracing:: warn!(
363+ "AWS_LAMBDA_MAX_CONCURRENCY is set to '{}', but the concurrency-tokio feature is not enabled; running sequentially" ,
364+ raw
365+ ) ;
346366 }
347367 let incoming = incoming ( & self . client ) ;
348368 Self :: run_with_incoming ( self . service , self . config , incoming) . await
@@ -412,7 +432,7 @@ fn incoming(
412432}
413433
414434/// Creates a future that polls the `/next` endpoint.
415- #[ cfg( feature = "experimental- concurrency" ) ]
435+ #[ cfg( feature = "concurrency-tokio " ) ]
416436async fn next_event_future ( client : & ApiClient ) -> Result < http:: Response < hyper:: body:: Incoming > , BoxError > {
417437 let req = NextEventRequest . into_req ( ) ?;
418438 client. call ( req) . await
@@ -429,7 +449,7 @@ fn concurrency_env_value() -> Option<String> {
429449 env:: var ( "AWS_LAMBDA_MAX_CONCURRENCY" ) . ok ( )
430450}
431451
432- #[ cfg( feature = "experimental- concurrency" ) ]
452+ #[ cfg( feature = "concurrency-tokio " ) ]
433453async fn concurrent_worker_loop < S > ( mut service : S , config : Arc < Config > , client : Arc < ApiClient > ) -> Result < ( ) , BoxError >
434454where
435455 S : Service < LambdaInvocation , Response = ( ) , Error = BoxError > ,
@@ -760,7 +780,7 @@ mod endpoint_tests {
760780 . await
761781 }
762782
763- #[ cfg( feature = "experimental- concurrency" ) ]
783+ #[ cfg( feature = "concurrency-tokio " ) ]
764784 #[ tokio:: test]
765785 async fn concurrent_worker_crash_does_not_stop_other_workers ( ) -> Result < ( ) , Error > {
766786 let next_calls = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
@@ -910,7 +930,7 @@ mod endpoint_tests {
910930 }
911931
912932 #[ tokio:: test]
913- #[ cfg( feature = "experimental- concurrency" ) ]
933+ #[ cfg( feature = "concurrency-tokio " ) ]
914934 async fn test_concurrent_structured_logging_isolation ( ) -> Result < ( ) , Error > {
915935 use std:: collections:: HashSet ;
916936 use tracing:: info;
0 commit comments