Queue Visibility Timeout
Function Timeout
Batch Window
Cloud Computing Best Practices
Message Queue Management

Why is queue visibility timeout is recommended to be six times function timeout plus batch window?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When using AWS Lambda with SQS as an event source, AWS recommends setting the queue's visibility timeout to at least six times the function timeout plus the MaximumBatchingWindowInSeconds. This formula (6 * functionTimeout + batchWindow) prevents messages from being processed multiple times when Lambda retries failed invocations. Setting it too low causes duplicate processing; setting it too high delays legitimate retries after real failures.

How Visibility Timeout Works

When SQS delivers a message to a consumer, it hides that message from other consumers for the duration of the visibility timeout. If the consumer does not delete the message before the timeout expires, SQS makes the message visible again for redelivery.

 
11. SQS delivers message → message becomes invisible
22. Consumer processes message
33a. Consumer deletes message → done
43b. Consumer fails/crashes → message stays invisible until timeout expires
54. Timeout expires → message becomes visible again → redelivered

Without an adequate visibility timeout, the message reappears while the original consumer is still processing it, causing duplicate execution.

Why Six Times the Function Timeout

Lambda's SQS integration has built-in retry logic. When a Lambda function fails (throws an error or times out), Lambda retries the entire batch of messages. The retry behavior includes:

  1. Initial attempt: Lambda invokes the function with the batch
  2. Up to 5 retries: On failure, Lambda retries with exponential backoff
  3. Total attempts: Up to 6 invocations (1 original + 5 retries)

Each retry can take up to the full function timeout duration. In the worst case:

 
1Total time = 6 attempts * functionTimeout + batchWindow
2
3Example:
4  Function timeout:  30 seconds
5  Batch window:      10 seconds
6  Visibility timeout: 6 * 30 + 10 = 190 seconds (minimum)

If the visibility timeout is shorter than this total, the message becomes visible again while Lambda is still retrying, causing another Lambda invocation to pick up the same message.

The Batch Window Factor

The MaximumBatchingWindowInSeconds adds additional delay before Lambda starts processing. SQS waits up to this duration to collect messages into a batch before invoking the function:

 
1Timeline:
2  [0s]          SQS delivers message, starts visibility timeout
3  [0-10s]       Batch window: SQS collects more messages
4  [10s]         Lambda invoked with batch
5  [10-40s]      First attempt (30s function timeout)
6  [40-70s]      Second attempt (retry 1)
7  ...
8  [160-190s]    Sixth attempt (retry 5)

The batch window consumes part of the visibility timeout before processing even begins, which is why it is added to the formula.

Configuring the Settings

AWS CDK

typescript
1import * as cdk from 'aws-cdk-lib';
2import * as lambda from 'aws-cdk-lib/aws-lambda';
3import * as sqs from 'aws-cdk-lib/aws-sqs';
4import * as eventsources from 'aws-cdk-lib/aws-lambda-event-sources';
5
6const functionTimeout = cdk.Duration.seconds(30);
7const batchWindow = cdk.Duration.seconds(10);
8
9const queue = new sqs.Queue(this, 'MyQueue', {
10  visibilityTimeout: cdk.Duration.seconds(
11    6 * functionTimeout.toSeconds() + batchWindow.toSeconds()
12  ),  // 190 seconds
13});
14
15const fn = new lambda.Function(this, 'MyFunction', {
16  runtime: lambda.Runtime.NODEJS_18_X,
17  handler: 'index.handler',
18  timeout: functionTimeout,
19});
20
21fn.addEventSource(new eventsources.SqsEventSource(queue, {
22  batchSize: 10,
23  maxBatchingWindow: batchWindow,
24}));

AWS SAM Template

yaml
1Resources:
2  MyQueue:
3    Type: AWS::SQS::Queue
4    Properties:
5      VisibilityTimeout: 190
6
7  MyFunction:
8    Type: AWS::Serverless::Function
9    Properties:
10      Handler: index.handler
11      Runtime: nodejs18.x
12      Timeout: 30
13      Events:
14        SQSEvent:
15          Type: SQS
16          Properties:
17            Queue: !GetAtt MyQueue.Arn
18            BatchSize: 10
19            MaximumBatchingWindowInSeconds: 10

Terraform

hcl
1resource "aws_sqs_queue" "my_queue" {
2  name                       = "my-queue"
3  visibility_timeout_seconds = 190
4}
5
6resource "aws_lambda_event_source_mapping" "sqs_trigger" {
7  event_source_arn                   = aws_sqs_queue.my_queue.arn
8  function_name                      = aws_lambda_function.my_function.arn
9  batch_size                         = 10
10  maximum_batching_window_in_seconds = 10
11}

What Happens with Incorrect Settings

Visibility TimeoutBehavior
Too low (less than 6x + batch)Messages reappear during retries, causing duplicate processing
Correct (6x + batch)Messages stay hidden through all retry attempts
Too high (much more than 6x)Failed messages take a long time to become available for reprocessing

Common Pitfalls

  • Setting visibility timeout equal to function timeout: This only covers one attempt. Lambda retries up to 5 additional times, so the message reappears during retry 2 and gets processed by another invocation concurrently.
  • Forgetting the batch window: The batch window delays invocation but the visibility timeout clock is already ticking. Without adding the batch window to the formula, the effective processing time is shorter than expected.
  • Using default SQS visibility timeout (30 seconds): The SQS default of 30 seconds is almost never sufficient for Lambda integrations. Always calculate and set it explicitly based on your function timeout and batch window.
  • Not making the function idempotent: Even with correct visibility timeout settings, network issues or Lambda service errors can cause occasional duplicate deliveries. Design your function to handle the same message more than once safely.
  • Ignoring dead-letter queue configuration: If a message fails all 6 attempts, it needs somewhere to go. Configure a dead-letter queue with maxReceiveCount to prevent poison messages from being retried indefinitely.

Summary

  • The formula is visibilityTimeout = 6 * functionTimeout + batchWindow
  • The factor of 6 accounts for 1 original invocation + 5 automatic retries by Lambda
  • The batch window is added because it consumes visibility timeout before processing starts
  • Setting visibility timeout too low causes duplicate message processing
  • Always configure a dead-letter queue for messages that exhaust all retry attempts
  • Design Lambda functions to be idempotent regardless of timeout settings

Course illustration
Course illustration

All Rights Reserved.