Skip to content

Fire-and-forget non-essential API calls in Lambda without extending the function’s duration?

0

I ran into a tricky Lambda behaviour today that I can’t quite square with my mental model of the Node.js runtime.

I had a user request get delayed because a call to flush telemetry to LangSmith was hanging. That’s fine — third-party calls fail — but it’s not OK for this to impact customers. So I updated the code to add a simple timeout around the flush:

try { await Promise.race([ flushPromise, new Promise((_, reject) => setTimeout(() => reject(new Error('LangSmith forceFlush timed out')), 2000) ), ]); } catch {}

It worked locally, so I deployed it — then immediately started getting P50 alarms. Every Lambda was taking exactly 2 seconds to finish. The user responses were fast (so the handler returned), but Lambda kept the function “open” until the loser of the race finished its internal work. I get why — Promise.race doesn’t cancel anything — so I removed the race and switched to this:

setTimeout(() => { void flushPromise.catch(console.warn); }, 2000);

But I hit the same problem. Even this delayed call — not awaited, not referenced — caused all Lambdas to report a ~2s duration. Removing it fixed the problem immediately.

So here’s what I’m trying to understand:

What is the correct way to fire-and-forget a non-essential API call in Node.js Lambda, without forcing Lambda to wait for the timeout window?

Constraints:

  • I can’t use callbackWaitsForEmptyEventLoop = false because some handlers stream responses.
  • I don’t want telemetry to block customer-visible work.
  • I don’t want to eat a fixed delay on every invocation.
  • I don’t want dangling timers or promises that keep the event loop “dirty.”

I’m fine if the flush doesn’t complete — it’s best-effort telemetry.

I feel like there must be a clean, accepted pattern for this — something that won’t hold the Lambda open and won’t interfere with streaming responses.

What’s the recommended approach for truly non-blocking, best-effort background calls in Lambda?


I should note, for the purposes of this discussion - this is can be considered a basic LLM chat handler. SO really, i'd expect this to be trivial! Hoping i'm missing something, maybe things have evolved and there is a better pattern for this!

asked 10 months ago479 views

1 Answer
0

When working with AWS Lambda in Node.js, handling asynchronous operations that shouldn't block the main function execution can be tricky due to how Lambda and the JavaScript event loop interact.

The behavior you're experiencing is related to how Lambda's execution environment handles the JavaScript event loop. Even when you don't explicitly await a Promise or reference it, Lambda will still wait for the event loop to empty before considering the function complete. This is why your function was consistently taking 2 seconds to finish.

For your use case of non-essential telemetry that shouldn't impact function duration, here are effective approaches:

  1. Use asynchronous invocation of another Lambda function:
  • Have your main function asynchronously invoke a separate Lambda function that handles the telemetry.
  • This completely separates the telemetry processing from your main function's execution.
  1. Use AWS services designed for asynchronous processing:
  • Send your telemetry data to SQS, SNS, or EventBridge.
  • These services accept your data quickly and handle the processing asynchronously.
  1. If you must handle it within the same function, use the context object's callbackWaitsForEmptyEventLoop = false property:
  • While you mentioned this isn't viable for streaming responses, it's worth noting that this is the standard solution for this exact problem.
  • For streaming response handlers, you might need to architect your solution differently.

The key insight is that in Lambda's Node.js runtime, there's no way to truly "fire and forget" within the same function execution context without affecting the function's duration. This is because of how JavaScript's event loop works in conjunction with Lambda's execution model.

Remember that any pending asynchronous operation (including timers and network requests) will keep the event loop active, and Lambda will wait for these operations to complete or time out before finalizing the function execution.
Sources
Troubleshoot configuration issues in Lambda - AWS Lambda
Handle unpredictable processing times with operational consistency when integrating asynchronous AWS services with an AWS Step Functions state machine | AWS Compute Blog
Invoking a Lambda function asynchronously - AWS Lambda

answered 10 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.