CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-amazonaws--aws-java-sdk-sqs

The AWS Java SDK for Amazon SQS module provides client classes for communicating with Amazon Simple Queue Service

Pending
Overview
Eval results
Files

message-visibility.mddocs/

Message Visibility

Message visibility operations control how long messages remain invisible to other consumers after being received. This mechanism prevents multiple consumers from processing the same message simultaneously and allows for flexible processing time management.

Change Message Visibility

Change Single Message Visibility

Modify the visibility timeout for a specific message using its receipt handle.

ChangeMessageVisibilityResult changeMessageVisibility(ChangeMessageVisibilityRequest request);

// Convenience method
ChangeMessageVisibilityResult changeMessageVisibility(String queueUrl, String receiptHandle, Integer visibilityTimeout);

class ChangeMessageVisibilityRequest extends AmazonWebServiceRequest {
    ChangeMessageVisibilityRequest();
    ChangeMessageVisibilityRequest(String queueUrl, String receiptHandle, Integer visibilityTimeout);
    
    String getQueueUrl();
    ChangeMessageVisibilityRequest withQueueUrl(String queueUrl);
    
    String getReceiptHandle();
    ChangeMessageVisibilityRequest withReceiptHandle(String receiptHandle);
    
    Integer getVisibilityTimeout();
    ChangeMessageVisibilityRequest withVisibilityTimeout(Integer visibilityTimeout);
}

class ChangeMessageVisibilityResult {
    // Empty result class - success indicated by no exception
}

Usage Example:

// Extend processing time for a message
ReceiveMessageResult receiveResult = client.receiveMessage(new ReceiveMessageRequest(queueUrl));

for (Message message : receiveResult.getMessages()) {
    try {
        // Start processing
        System.out.println("Processing message: " + message.getMessageId());
        
        // If processing will take longer, extend visibility timeout
        client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
            .withQueueUrl(queueUrl)
            .withReceiptHandle(message.getReceiptHandle())
            .withVisibilityTimeout(300)); // Extend to 5 minutes
        
        // Continue with long-running processing
        performLongRunningTask(message);
        
        // Delete after successful processing
        client.deleteMessage(queueUrl, message.getReceiptHandle());
        
    } catch (Exception e) {
        // Make message immediately visible for retry
        client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
            .withQueueUrl(queueUrl)
            .withReceiptHandle(message.getReceiptHandle())
            .withVisibilityTimeout(0)); // Make immediately visible
    }
}

Change Batch Message Visibility

Modify visibility timeout for up to 10 messages in a single API call.

ChangeMessageVisibilityBatchResult changeMessageVisibilityBatch(ChangeMessageVisibilityBatchRequest request);

// Convenience method
ChangeMessageVisibilityBatchResult changeMessageVisibilityBatch(String queueUrl, List<ChangeMessageVisibilityBatchRequestEntry> entries);

class ChangeMessageVisibilityBatchRequest extends AmazonWebServiceRequest {
    String getQueueUrl();
    ChangeMessageVisibilityBatchRequest withQueueUrl(String queueUrl);
    
    List<ChangeMessageVisibilityBatchRequestEntry> getEntries();
    ChangeMessageVisibilityBatchRequest withEntries(List<ChangeMessageVisibilityBatchRequestEntry> entries);
    ChangeMessageVisibilityBatchRequest withEntries(ChangeMessageVisibilityBatchRequestEntry... entries);
}

class ChangeMessageVisibilityBatchRequestEntry {
    String getId();
    ChangeMessageVisibilityBatchRequestEntry withId(String id);
    
    String getReceiptHandle();
    ChangeMessageVisibilityBatchRequestEntry withReceiptHandle(String receiptHandle);
    
    Integer getVisibilityTimeout();
    ChangeMessageVisibilityBatchRequestEntry withVisibilityTimeout(Integer visibilityTimeout);
}

class ChangeMessageVisibilityBatchResult {
    List<ChangeMessageVisibilityBatchResultEntry> getSuccessful();
    List<BatchResultErrorEntry> getFailed();
}

class ChangeMessageVisibilityBatchResultEntry {
    String getId();
}

Usage Example:

// Batch visibility timeout changes
ReceiveMessageResult receiveResult = client.receiveMessage(new ReceiveMessageRequest(queueUrl)
    .withMaxNumberOfMessages(10));

List<ChangeMessageVisibilityBatchRequestEntry> visibilityEntries = new ArrayList<>();

for (Message message : receiveResult.getMessages()) {
    // Determine new timeout based on message attributes or content
    int newTimeout = determineProcessingTime(message);
    
    visibilityEntries.add(new ChangeMessageVisibilityBatchRequestEntry()
        .withId(message.getMessageId())
        .withReceiptHandle(message.getReceiptHandle())
        .withVisibilityTimeout(newTimeout));
}

if (!visibilityEntries.isEmpty()) {
    ChangeMessageVisibilityBatchRequest batchRequest = new ChangeMessageVisibilityBatchRequest()
        .withQueueUrl(queueUrl)
        .withEntries(visibilityEntries);
    
    ChangeMessageVisibilityBatchResult batchResult = client.changeMessageVisibilityBatch(batchRequest);
    
    // Check results
    System.out.println("Successfully changed visibility for " + 
        batchResult.getSuccessful().size() + " messages");
    
    for (BatchResultErrorEntry error : batchResult.getFailed()) {
        System.err.println("Failed to change visibility for " + error.getId() + 
            ": " + error.getMessage());
    }
}

Visibility Timeout Patterns

Progressive Timeout Extension

Gradually increase visibility timeout for messages that require more processing time:

public class ProgressiveVisibilityManager {
    private final AmazonSQS sqsClient;
    private final String queueUrl;
    private final Map<String, Integer> messageAttempts = new ConcurrentHashMap<>();
    
    public ProgressiveVisibilityManager(AmazonSQS sqsClient, String queueUrl) {
        this.sqsClient = sqsClient;
        this.queueUrl = queueUrl;
    }
    
    public void extendVisibilityTimeout(Message message) {
        String messageId = message.getMessageId();
        int attempts = messageAttempts.getOrDefault(messageId, 0) + 1;
        messageAttempts.put(messageId, attempts);
        
        // Progressive timeout: 30s, 60s, 120s, 300s
        int timeout = Math.min(30 * (1 << (attempts - 1)), 300);
        
        try {
            sqsClient.changeMessageVisibility(new ChangeMessageVisibilityRequest()
                .withQueueUrl(queueUrl)
                .withReceiptHandle(message.getReceiptHandle())
                .withVisibilityTimeout(timeout));
            
            System.out.println("Extended visibility timeout to " + timeout + 
                " seconds for attempt " + attempts);
        } catch (Exception e) {
            System.err.println("Failed to extend visibility timeout: " + e.getMessage());
        }
    }
    
    public void completeMessage(Message message) {
        messageAttempts.remove(message.getMessageId());
        
        try {
            sqsClient.deleteMessage(queueUrl, message.getReceiptHandle());
        } catch (Exception e) {
            System.err.println("Failed to delete message: " + e.getMessage());
        }
    }
}

Heartbeat Pattern

Periodically refresh visibility timeout for long-running processing:

public class MessageHeartbeat {
    private final AmazonSQS sqsClient;
    private final String queueUrl;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
    public MessageHeartbeat(AmazonSQS sqsClient, String queueUrl) {
        this.sqsClient = sqsClient;
        this.queueUrl = queueUrl;
    }
    
    public CompletableFuture<Void> processWithHeartbeat(Message message, 
            Function<Message, Void> processor) {
        
        // Schedule heartbeat every 30 seconds
        ScheduledFuture<?> heartbeat = scheduler.scheduleAtFixedRate(() -> {
            try {
                sqsClient.changeMessageVisibility(new ChangeMessageVisibilityRequest()
                    .withQueueUrl(queueUrl)
                    .withReceiptHandle(message.getReceiptHandle())
                    .withVisibilityTimeout(60)); // Keep alive for 1 minute
            } catch (Exception e) {
                System.err.println("Heartbeat failed: " + e.getMessage());
            }
        }, 30, 30, TimeUnit.SECONDS);
        
        return CompletableFuture.runAsync(() -> {
            try {
                processor.apply(message);
                
                // Processing completed successfully
                sqsClient.deleteMessage(queueUrl, message.getReceiptHandle());
                
            } catch (Exception e) {
                // Make message immediately visible for retry
                sqsClient.changeMessageVisibility(new ChangeMessageVisibilityRequest()
                    .withQueueUrl(queueUrl)
                    .withReceiptHandle(message.getReceiptHandle())
                    .withVisibilityTimeout(0));
                
                throw new RuntimeException("Processing failed", e);
            } finally {
                heartbeat.cancel(false);
            }
        });
    }
}

Immediate Retry Pattern

Make failed messages immediately available for reprocessing:

public void processMessageWithImmediateRetry(Message message) {
    try {
        // Attempt processing
        processMessage(message);
        
        // Success - delete message
        client.deleteMessage(queueUrl, message.getReceiptHandle());
        
    } catch (RetryableException e) {
        // Transient error - make immediately visible for retry
        System.out.println("Transient error, making message immediately visible for retry");
        
        client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
            .withQueueUrl(queueUrl)
            .withReceiptHandle(message.getReceiptHandle())
            .withVisibilityTimeout(0));
            
    } catch (NonRetryableException e) {
        // Permanent error - delete message or send to DLQ
        System.err.println("Permanent error, removing message from queue");
        
        client.deleteMessage(queueUrl, message.getReceiptHandle());
    }
}

Visibility Timeout Best Practices

Choosing Appropriate Timeouts

Select visibility timeouts based on processing requirements:

public class VisibilityTimeoutCalculator {
    
    public static int calculateTimeout(Message message) {
        // Base timeout
        int baseTimeout = 30; // 30 seconds
        
        // Adjust based on message size
        int messageSize = message.getBody().length();
        if (messageSize > 100000) { // 100KB
            baseTimeout += 60; // Add 1 minute for large messages
        }
        
        // Adjust based on message type
        String messageType = getMessageAttribute(message, "MessageType");
        switch (messageType) {
            case "ImageProcessing":
                return 300; // 5 minutes
            case "DataExport":
                return 900; // 15 minutes
            case "EmailSend":
                return 60;  // 1 minute
            default:
                return baseTimeout;
        }
    }
    
    private static String getMessageAttribute(Message message, String attributeName) {
        MessageAttributeValue attribute = message.getMessageAttributes().get(attributeName);
        return attribute != null ? attribute.getStringValue() : "";
    }
}

Error Handling

Handle visibility timeout errors appropriately:

try {
    client.changeMessageVisibility(request);
} catch (MessageNotInflightException e) {
    // Message is no longer in-flight (may have been processed by another consumer)
    System.err.println("Message not in-flight: " + e.getMessage());
} catch (ReceiptHandleIsInvalidException e) {
    // Receipt handle is invalid or expired
    System.err.println("Invalid receipt handle: " + e.getMessage());
} catch (InvalidAttributeValueException e) {
    // Invalid visibility timeout value (must be 0-43200 seconds)
    System.err.println("Invalid timeout value: " + e.getMessage());
}

// Batch operation error handling
ChangeMessageVisibilityBatchResult result = client.changeMessageVisibilityBatch(batchRequest);

for (BatchResultErrorEntry error : result.getFailed()) {
    switch (error.getCode()) {
        case "MessageNotInflight":
            System.err.println("Message " + error.getId() + " not in-flight");
            break;
        case "ReceiptHandleIsInvalid":
            System.err.println("Invalid receipt handle for " + error.getId());
            break;
        default:
            System.err.println("Error for " + error.getId() + ": " + error.getMessage());
    }
}

Visibility Timeout Limits

Timeout Constraints

Understanding visibility timeout limits and constraints:

public class VisibilityTimeoutLimits {
    // Minimum visibility timeout (0 = immediately visible)
    public static final int MIN_VISIBILITY_TIMEOUT = 0;
    
    // Maximum visibility timeout (12 hours)
    public static final int MAX_VISIBILITY_TIMEOUT = 43200;
    
    // Default queue visibility timeout range
    public static final int DEFAULT_MIN_QUEUE_TIMEOUT = 0;
    public static final int DEFAULT_MAX_QUEUE_TIMEOUT = 43200;
    
    public static boolean isValidTimeout(int timeout) {
        return timeout >= MIN_VISIBILITY_TIMEOUT && timeout <= MAX_VISIBILITY_TIMEOUT;
    }
    
    public static int clampTimeout(int timeout) {
        return Math.max(MIN_VISIBILITY_TIMEOUT, 
               Math.min(MAX_VISIBILITY_TIMEOUT, timeout));
    }
}

Receipt Handle Validity

Understanding receipt handle lifecycle:

public class ReceiptHandleManager {
    // Receipt handles are valid only during message visibility period
    // They become invalid when:
    // 1. Message visibility timeout expires
    // 2. Message is deleted
    // 3. Message is received again by another consumer
    
    public boolean isReceiptHandleValid(String receiptHandle) {
        try {
            // Attempt to change visibility with 0 timeout (no-op if valid)
            client.changeMessageVisibility(new ChangeMessageVisibilityRequest()
                .withQueueUrl(queueUrl)
                .withReceiptHandle(receiptHandle)
                .withVisibilityTimeout(0));
            return true;
        } catch (ReceiptHandleIsInvalidException e) {
            return false;
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-amazonaws--aws-java-sdk-sqs

docs

async-operations.md

buffered-client.md

client-management.md

dead-letter-queues.md

index.md

message-operations.md

message-visibility.md

queue-operations.md

queue-permissions.md

queue-tagging.md

tile.json