MCPcopy Index your code
hub / github.com/aws/aws-lambda-rust-runtime / run_concurrent_inner

Method run_concurrent_inner

lambda-runtime/src/runtime.rs:201–277  ·  view source on GitHub ↗

Concurrent processing using N independent long-poll loops (for Lambda managed-concurrency).

(
        service: S,
        config: Arc<Config>,
        client: Arc<ApiClient>,
        concurrency_limit: u32,
    )

Source from the content-addressed store, hash-verified

199
200 /// Concurrent processing using N independent long-poll loops (for Lambda managed-concurrency).
201 async fn run_concurrent_inner(
202 service: S,
203 config: Arc<Config>,
204 client: Arc<ApiClient>,
205 concurrency_limit: u32,
206 ) -> Result<(), BoxError> {
207 let limit = concurrency_limit as usize;
208
209 // Use FuturesUnordered so we can observe worker exits as they happen,
210 // rather than waiting for all workers to finish (join_all).
211 let mut workers: FuturesUnordered<tokio::task::JoinHandle<(tokio::task::Id, Result<(), BoxError>)>> =
212 FuturesUnordered::new();
213 let spawn_worker = |service: S, config: Arc<Config>, client: Arc<ApiClient>| {
214 tokio::spawn(async move {
215 let task_id = tokio::task::id();
216 let result = concurrent_worker_loop(service, config, client).await;
217 (task_id, result)
218 })
219 };
220 // Spawn one worker per concurrency slot; the last uses the owned service to avoid an extra clone.
221 for _ in 1..limit {
222 workers.push(spawn_worker(service.clone(), config.clone(), client.clone()));
223 }
224 workers.push(spawn_worker(service, config, client));
225
226 // Track worker exits across tasks. A single worker failing should not
227 // terminate the whole runtime (LMI keeps running with the remaining
228 // healthy workers). We only return an error once there are no workers
229 // left (i.e., we cannot keep at least 1 worker alive).
230 //
231 // Note: Handler errors (Err returned from user code) do NOT trigger this.
232 // They are reported to Lambda via /invocation/{id}/error and the worker
233 // continues. This only captures unrecoverable runtime failures like
234 // API client failures, runtime panics, etc.
235 let mut errors: Vec<WorkerError> = Vec::new();
236 let mut remaining_workers = limit;
237 while let Some(result) = futures::StreamExt::next(&mut workers).await {
238 remaining_workers = remaining_workers.saturating_sub(1);
239 match result {
240 Ok((task_id, Ok(()))) => {
241 // `concurrent_worker_loop` runs indefinitely, so an Ok return indicates
242 // an unexpected worker exit; we still decrement because the task is gone.
243 error!(
244 task_id = %task_id,
245 remaining_workers,
246 "Concurrent worker exited cleanly (unexpected - loop should run forever)"
247 );
248 errors.push(WorkerError::CleanExit(task_id));
249 }
250 Ok((task_id, Err(err))) => {
251 error!(
252 task_id = %task_id,
253 error = %err,
254 remaining_workers,
255 "Concurrent worker exited with error"
256 );
257 errors.push(WorkerError::Failure(task_id, err));
258 }

Callers

nothing calls this directly

Calls 2

concurrent_worker_loopFunction · 0.85
cloneMethod · 0.45

Tested by

no test coverage detected